# ruby fibers implemented by callcc
# pesco 2010, isc license

class Fibr
    class Error < RuntimeError; end

    @@fs = []   # a stack of fibers corresponding to calls of 'resume'

    def initialize(&block)
        @k = lambda(&block)         # lambda makes 'return' work as expected
    end

    def alive?
        !@k.nil?
    end

    def resume(*xs)
        @k || raise Error, "dead fiber"
        @@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)                    # spot the BSG reference
        callcc { |k|
            destination = @k
            @k = k
            destination.call(*xs)
            # if the call above falls through (the fiber block exited), we are
            # left in the original execution context of 'destination' where 'k'
            # had a different value! Therefore we must use the saved '@k' to
            # jump back to the last 'resume'.
            @@fs.pop
            k = @k
            @k = nil               # mark this fiber dead
            k.call                 # return from the last 'resume'
        }
    end
end

Fiber = Fibr if RUBY_VERSION<"1.9"

