igrigorik / em-synchrony

Fiber aware EventMachine clients and convenience classes
http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers
MIT License
1.04k stars 151 forks source link

FiberIterator breaks normal exception handling #206

Open dgutov opened 8 years ago

dgutov commented 8 years ago

Example (failing spec):

  it "permits normal handling of errors" do
    caught = nil

    EM.synchrony do
      begin
        EM::Synchrony::FiberIterator.new(0..1, 2).each do |num|
          raise "#{num} here"
        end
      rescue => e
        caught = e.message
      end

      EM.stop
    end

    expect(caught).to eq("0 here")
  end

The rescue block is never entered, and the only way to catch the exception is with EM.error_handler or catching it outside of the EM loop. Neither approach is composable.

@igrigorik Any ideas for an easy fix?

igrigorik commented 8 years ago

Related:

dgutov commented 8 years ago

Right. But still, this kinda works (it outputs 3 at the end, at least):

    counter = 0

    EM.synchrony do
      f = Fiber.current

      maybe_stop = proc { f.resume if counter == 2 }

      f1 = Fiber.new do
        EM::Synchrony.sleep(1)
        counter += 1
        puts counter
        maybe_stop[]
      end

      f2 = Fiber.new do
        begin
          EM::Synchrony.sleep(1)
          counter += 1
          puts counter
          raise "aaa"
        ensure
          maybe_stop[]
        end
      end

      EM.next_tick { f1.resume }
      EM.next_tick { f2.resume }

      begin
        Fiber.yield
      ensure
        puts 3
      end
      EM.stop
    end

And this catches the error, even though it was raised in a different fiber:

    f = Fiber.new do
      raise "bar"
    end

    begin
      f.resume
    rescue
      puts "safety caught"
    end

Maybe all worker fibers should be created by the caller fiber, on the main thread.

dgutov commented 8 years ago

Alas, no. The second option doesn't seem like it'll work either when one of the worker fibers is suspended and then woken by the reactor thread (which is how we handle asynchrony normally).