Method: Fiber#transfer

Defined in:
lib/cfiber.rb

#transfer(*args) ⇒ Object

Useful to implement coroutines.

FIXME: the tricky part is init. In the example above, f.transfer(100) triggers f.resume(100), which in turns reach g.transfer(101). This triggers g.yield(f.resume(101)) but this is wrong. One must atomically pause (yield) g and resume f.

Examples:

f = g = nil

f = Fiber.new do |x|
  puts "F1: #{x}"
  x = g.transfer(x+1)
  puts "F2: #{x}"
  x = g.transfer(x+1)
  puts "F3: #{x}"
end

g = Fiber.new do |x|
  puts "G1: #{x}"
  x = f.transfer(x+1)
  puts "G2: #{x}"
  x = f.transfer(x+1)
end

f.transfer(100)

-----------------

g = Fiber.new { |x|
  puts "G1: #{x}"
  x = Fiber.yield(x+1) # f.transfer => Thread.current[:__cfiber_transfer] != nil => Thread.current[:__cfiber_transfer] = nil && Fiber.yield
  puts "G2: #{x}"
  x = Fiber.yield(x+1)
}

f = Fiber.new { |x|
  puts "F1: #{x}"
  x = g.resume(x+1) # g.transfer => Thread.current[:__cfiber_transfer] = Fiber.current && g.resume
  puts "F2: #{x}"
  x = g.resume(x+1) # pareil
  puts "F3: #{x}"
}

f.resume(100) # f.transfer => Fiber.current == nil => f.resume


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/cfiber.rb', line 135

def transfer(*args)
  if Fiber.current.nil?
    log.debug ""
    log.debug "cas 1"
    Thread.current[:__cfiber_transfer] = nil
    self.resume(*args)

  elsif Thread.current[:__cfiber_transfer].nil?
    log.debug ""
    log.debug "cas 2"
    log.debug "resuming #{self}"
    Thread.current[:__cfiber_transfer] = Fiber.current
    self.resume(*args)

  elsif Thread.current[:__cfiber_transfer].is_a?(Fiber)
    log.debug ""
    log.debug "cas 3"
    log.debug "pausing #{Fiber.current}"
    Thread.current[:__cfiber_transfer] = nil
    Fiber.yield(*args)
    #Fiber.yield(self.resume(*args)) # TODO: handle multiple coroutines
  else
    log.debug ""
    log.debug "cas 4"
    raise FiberError, "transfer mismatch"
  end
end