Fibers for Ruby 1.8 in 42 lines feat. call/cc
Actually, stripped of comments and stuff, it's even less than 42 lines.Fibersare a big new thing with Ruby 1.9. The name is supposed to suggest that they're the thinner things inside a thread. You create them with a block to execute but they won't run in parallel. Instead they are explicitly entered and left via resume and yield. The first call to resume enters at the top of the block. Calling yield from inside the block jumps back out and the next resume jumps back inside. Repeat as often as you like.
f = Fiber.new {
...
Fiber.yield 23 # returns 5
...
}
f.resume # start it up; returns 23
... # control transfers back here after "yield"
f.resume 5 # run the rest
The two can pass arguments to each other like regular procedure calls.
So it's indeed like threads as there are independent control flows.
But execution switches only explicitly and intercommunication is much more
direct.
Today I found out what they are really useful for:
To separate some very sequential business logic (do A, do B, do C, finish)
from the event-centric tangle of a GUI toolkit.
A fiber does A, B, and C in sequence.
Where doesmeans it starts the job in the background, registers an event handler, and immediately yields back to the GUI event loop. When the job finishes, the handler resumes the fiber and it goes on to the next step. Ruby 1.9 adds fibers as a primitive, but they are also easily implemented in terms of call/cc -
call with current continuation, which Ruby has had for no idea how long. So here goes, a drop-in substitute for 1.9's Fiber class:
class Fibr
@@fs = [] # a stack of fibers corresponding to calls of 'resume'
def initialize(&block)
@k = lambda(&block) # lambda makes 'return' work as expected
end
def resume(*xs)
@@fs.push(self)
jump(xs) # jumping into fiber
end
def self.current
@@fs.last
end
def self.yield(*xs)
f = @@fs.pop
f && f.send(:jump, xs) # jumping out of fiber
end
private
def jump(xs)
callcc { |k|
destination = @k
@k = k
destination.call(*xs)
@@fs.pop
@k.call # return from the last 'resume'
}
end
end
Fiber = Fibr if RUBY_VERSION<"1.9"
Download:
fibr.rb
(1 comment…)