rubyfibers

Demystifying Ruby "Fiber" internals


I am learning about the Ruby Fiber which gives lot of flexibility but I came up with some doubt's in Fiber as well as in Ruby.

require 'fiber'

class MyObj
    def call_yield
      print "Prepare to Yield"
      print "foo bar"
      Fiber.yield
      print "Resumed"
      @fiber = nil
    end 

    def create_fiber
      #@fiber = Fiber.new{call_yield}
      @fiber = Fiber.new {}
      p @fiber
    end

    def update
      p @fiber.resume
    end
end
  
obj = MyObj.new
# obj.call_yield #Fiber error 
obj.create_fiber
obj.update
obj.update
  1. When I uncomment the line with obj.call_yield the Fiber error occurs as expected.Even when I type Fiber.yield in irb session the same error occurs.

    • How ruby recognizes that current fiber is valid or not ?
    • And what is the scope of the block which containing Fiber.Yield given to Fiber.new.
  2. Notice the line #@fiber = Fiber.new{call_yield} inside create fiber method is still a valid code.

    • I didn't have idea what happens there as function is called directly but not as symbol.
    • Even the function call occurs directly I cannot see anything on screen.This shows the code inside function still not started to run,but why this behaviour occurs?
  3. As fiber's are written in C,I have no other sources to clear my doubt's so please provide minimalistic examples for each questions as possible.


Solution

  • I want to know how Fiber.yield behaves when inside block that is provided to Fiber.new vs any other place in program ie)How it produces "root fiber error" elsewhere in program?

    Ah, that is actually quite simple. All you need to do is modify a piece of global state when executing resume. For example, push the current fiber onto a global stack or something like that. Here's a fake Fiber class that exhibits the same behaviour.

    class MyFiber
      class << self
        def stack
          @stack ||= []
        end
        
        def yield
          top = stack.pop
          if top.nil?
            raise 'root fiber error'
          end
          puts "running fiber #{top}"
        end
      end
      
      def initialize(&block)
        @block = block
      end
      
      def resume
        MyFiber.stack.push(@block)
        @block.call
      end
    end