Project

General

Profile

Actions

Bug #17650

closed

TracePoint doesn't receive :fiber_switch events when raising exceptions

Added by nevans (Nicholas Evans) about 3 years ago. Updated about 3 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:<unknown>]

Description

It seems to me the :fiber_switch event should be raised for every fiber switch, unless there's some very important reason not to, e.g. maybe it makes sense to skip the event when a fiber terminates with a fatal error. In addition to sending the event, there's some other fiber cleanup code in fiber_switch that is skipped after raising an exception: checking thread interrupts and incrementing th->blocking. Maybe this is intended behavior? But it all seems like a bug to me.

I've captured the expected behavior in this test (which fails on master):

    evs = []
    f = nil
    TracePoint.new(:raise, :fiber_switch){|tp|
      next unless target_thread?
      evs << [tp.event, Fiber.current]
    }.enable{
      f = Fiber.new{
        Fiber.yield # will raise
        Fiber.yield # unreachable
      }
      begin
        f.resume
        f.raise StopIteration
      rescue StopIteration
        evs << :rescued
      end
    }
    assert_equal [:fiber_switch, f],             evs[0], "initial resume"
    assert_equal [:fiber_switch, Fiber.current], evs[1], "Fiber.yield"
    # fiber switch event comes before the exception is raised:
    assert_equal [:fiber_switch, f],             evs[2], "fiber.raise"
    assert_equal [:raise, f],                    evs[3], "fiber.raise"
    # fiber switch event comes before the exception is raised:
    assert_equal [:fiber_switch, Fiber.current], evs[4], "terminated with raise"
    assert_equal [:raise, Fiber.current],        evs[5], "terminated with raise"
    assert_equal :rescued,                       evs[6]
    assert_equal 7, evs.size

I've attempted to fix this in a PR here: https://github.com/ruby/ruby/pull/4207

That PR moves the rb_exc_raise after all of the other fiber_switchcleanup code. It also changes fiber termination with an unhandled exception to pass the exception to its return_fiber in the same way as Fiber#raise (via fiber->cont.value and fiber->cont.argc == -1) instead of via a thread interrupt, which can lead to unexpected and difficult to debug behavior. Fatal errors are still passed via rb_threadptr_pending_interrupt_enque.

Updated by jeremyevans0 (Jeremy Evans) about 3 years ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0