Updated RubySpec submodule to 9f66d0b1.
[rbx.git] / kernel / core / continuation.rb
blob4a34d290d84c6cf520f554b78d290ed7f3655ee9
1 # depends on: class.rb module.rb
3 ##
4 # Continuation objects are generated by Kernel#callcc. They hold a return
5 # address and execution context, allowing a nonlocal return to the end of the
6 # callcc block from anywhere within a program.  Continuations are somewhat
7 # analogous to a structured version of C's setjmp/longjmp (although they
8 # contain more state, so you might consider them closer to threads).
10 # For instance:
12 #   arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
13 #   callcc{|$cc|}
14 #   puts(message = arr.shift)
15 #   $cc.call unless message =~ /Max/
17 # produces:
19 #   Freddie
20 #   Herbie
21 #   Ron
22 #   Max
24 # This (somewhat contrived) example allows the inner loop to abandon
25 # processing early:
27 #   callcc {|cont|
28 #     for i in 0..4
29 #       print "\n#{i}: "
30 #       for j in i*5...(i+1)*5
31 #         cont.call() if j == 17
32 #         printf "%3d", j
33 #       end
34 #     end
35 #   }
36 #   print "\n"
38 # produces:
40 #   0:   0  1  2  3  4
41 #   1:   5  6  7  8  9
42 #   2:  10 11 12 13 14
43 #   3:  15 16
45 class Continuation
46   def self.new(*args)
47     raise NoMethodError.new
48   end
50   def self.create(task=nil)
51     cont = self.allocate
52     cont.setup(task)
53     cont
54   end
55   
56   def setup(task=nil)
57     @task = task
58     @value = nil
59   end
60   
61   def task=(task)
62     @task = task
63   end
64   
65   def value
66     @value
67   end
68   
69   def call(*value)
70     task = @task.dup
71     if value.empty?
72       @value = nil
73     elsif value.size == 1
74       @value = value.pop
75     else
76       @value = value
77     end
78     Task.current = task
79   end
81   alias_method :[], :call
82 end
84 module Kernel
85   def callcc
86     cont = Continuation.create
87     # Task#dup appears as though it returns nil in the dup'd
88     # task, kinda like fork().
89     task = Task.current.dup
90     if task
91       cont.task = task
92       yield cont
93     else
94       return cont.value
95     end
96   end
97   module_function :callcc
98 end