I'm trying to create processes that can wait for certain events to occur
before continuing on. I'm using continuations (callcc) to do this, but it
seems like the continuation is carrying a lot more context around than I
would like it to.
Here's the code (and there's a bit of it):
class WaitException < Exception
end
class T_Process
def initialize(callable= nil,sensitivityList=nil)
@sensitivity_list= sensitivityList
@callableObj = callable
@savedCallableObj = @callableObj
@contin = nil
@wait_cond = proc{true} #nop proc
@wait_pending = false
end
def call
if @wait_pending
if @wait_cond.call
@contin.call
@callableObj = @savedCallableObj if @wait_cond.call
@wait_pending = false
else
call_with_rescue(@callableObj)
end
else
call_with_rescue(@callableObj)
end
end
def wait(cond)
@wait_cond = cond
@wait_pending = true
@callableObj = proc{} #insert a NOP
callcc{|@contin|
puts "Start Wait..."
raise WaitException
}
end
private
def call_with_rescue(p)
begin
p.call
rescue WaitException
puts "Got a WaitException"
end
end
end
$time = 0
class Design
def initialize
@process = nil
end
def define_behavior(&b)
puts "define the behavior"
@behavior = b
end
def process(&p)
unless @process
puts "define the process"
@process = T_Process.new(p)
else
@process.call
end
end
def step
@behavior.call
sleep .2
$time+=1
end
def wait(&cond)
@process.wait(cond)
end
end
#now try it out:
class MyDesign < Design
def initialize
define_behavior {
process {
puts "first"
puts "second"
wait { $time == 8 }
puts "back in define_behavior..."
puts "time is: #{$time}"
}
}
end
end
i=0
md = MyDesign.new
12.times do |i|
puts ">>>>>iteration: #{i}"
md.step
end
#########################end of code######################
Here's the output:
ruby Process.rb
define the behavior
>>>>>iteration: 0
define the process
>>>>>iteration: 1
first
second
Start Wait...
Got a WaitException
>>>>>iteration: 2
>>>>>iteration: 3
>>>>>iteration: 4
>>>>>iteration: 5
>>>>>iteration: 6
>>>>>iteration: 7
>>>>>iteration: 8
back in define_behavior...
time is: 8
>>>>>iteration: 2
>>>>>iteration: 3
>>>>>iteration: 4
>>>>>iteration: 5
>>>>>iteration: 6
>>>>>iteration: 7
>>>>>iteration: 8
>>>>>iteration: 9
>>>>>iteration: 10
>>>>>iteration: 11
The main idea here is that in the constructor for MyDesign I define a
behavior (basically a proc) that has a process in it (again basically a
proc) which contains a wait (yet again, basically a proc). The idea is
that the process defined in define_behavior will get called each time the
step method is called. What I want to happen is that the process executes
until the wait statement which then says in effect 'wait until the global
time is 8'. With each call to md.step the global time variable is
incremented. After $time == 8 the rest of the process should be executed
(in this case it'll do puts "back in define_behavior" and then print the
current value of $time).
It's working pretty close to how I intend it to work, except that after
the wait condition has been satisfied and the continuation is called (to
run the rest of the process) the iteration variable (i) that exists at the
toplevel scope also is returned to the value it had just as the
continuation was created (in the wait method of T_Process class) which
means that although it was 8 before the continuation was called, it's
returned to 2 and it starts all over from that point until it has run 12
times (for a total of something like 20 iterations instead of 12.times as
indicated). So it seems that the call to the continuation not only
continued execution at the statement after the wait, but it also restored
variables at the toplevel scope to their values at that time.
How can I avoid this so I get an output that looks like:
define the behavior
>>>>>iteration: 0
define the process
>>>>>iteration: 1
first
second
Start Wait...
Got a WaitException
>>>>>iteration: 2
>>>>>iteration: 3
>>>>>iteration: 4
>>>>>iteration: 5
>>>>>iteration: 6
>>>>>iteration: 7
>>>>>iteration: 8
back in define_behavior...
time is: 8
>>>>>iteration: 9 #<=continue from 8 instead of reseting to 2
>>>>>iteration: 10
>>>>>iteration: 11
Phil