Search code examples
rubyfiber

Ruby fiber "dead fiber called"


Playing round with fibers and I know I'm doing something stupid here. But trying to create a simple enumerable type thing based on fibers (it's obviously trivial using Enumerable). When I run the following I get a dead fiber error after it prints out the first couple of iterations. Does anyone know why?

class SumPow
  def initialize(a)
    sum = a
    pow = a
    @fiber = Fiber.new do
      Fiber.yield sum
      pow = pow * a
      sum = sum + pow
    end
  end

  def next
    @fiber.resume
  end

  def each
    loop do
      yield self.next
    end
  end

end

sp = SumPow.new(3)
sp.each do |sum|
  puts sum
end

output:

3
12
tmp/fiber_enum.rb:96:in `resume': dead fiber called (FiberError)
    from tmp/fiber_enum.rb:96:in `next'
    from tmp/fiber_enum.rb:101:in `block in each'
    from tmp/fiber_enum.rb:100:in `loop'
    from tmp/fiber_enum.rb:100:in `each'
    from tmp/fiber_enum.rb:108:in `<main>'

This code on the other hand works completely fine:

fib = Fiber.new do
  x, y = 0, 1
  loop do
    Fiber.yield y
    x, y = y, x + y
  end
end
7.times { puts fib.resume }

output:

1
1
2
3
5
8
13

Solution

  • The code is calling resume regardless of state of the fiber.

    Check whether fiber can resume by calling Fiber#alive?

    def each
      while @fiber.alive? do
        yield self.next
      end
    end
    

    NOTE You need to do require 'fiber' to use alive? method.


    UPDATE accoridng to the question edit.

    The original code yields only once. To iterate indefinitely, you need loop.

    def initialize(a)
      sum = a
      pow = a
      @fiber = Fiber.new do
        loop do # <-------------
          Fiber.yield sum
          pow = pow * a
          sum = sum + pow
        end # <-----------------
      end
    end