Project

General

Profile

Actions

Bug #20414

closed

`Fiber#raise` should recurse to `resumed_fiber` rather than failing.

Added by ioquatix (Samuel Williams) 9 months ago. Updated 5 months ago.

Status:
Closed
Target version:
-
[ruby-core:117458]

Description

The following program will fail with FiberError, and is difficult to properly clean up:

root_fiber = Fiber.current

f1 = Fiber.new do
  root_fiber.transfer
end

f2 = Fiber.new do
  f1.resume
end

f2.transfer

f2.raise("error") # => `raise': attempt to transfer to a resuming fiber (FiberError)

This program deliberately set's up a scenario where f2 is resuming f1. Trying to raise an exception on f2 is impossible, because the only way control flow can return to it, is when f1 yields or exits.

We can avoid this problem, by raising the exception on f1, and we can do this automatically using the following logic:

static VALUE
fiber_raise(rb_fiber_t *fiber, VALUE exception)
{
    // Add this recursive step:
    if (fiber->resuming_fiber) {
        return fiber_raise(fiber->resuming_fiber, exception);
    }

    // Existing code ...
    else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
        return fiber_transfer_kw(fiber, -1, &exception, RB_NO_KEYWORDS);
    }
    else {
        return fiber_resume_kw(fiber, -1, &exception, RB_NO_KEYWORDS);
    }
}

This makes Fiber#raise much more robust and useful for the purpose of stopping fibers, without knowing exactly what they are doing.


Related issues 2 (1 open1 closed)

Related to Ruby master - Feature #20102: Introduce `Fiber#resuming?`Closedioquatix (Samuel Williams)Actions
Related to Ruby master - Bug #20089: Fiber#kill transfers to root fiberAssignedioquatix (Samuel Williams)Actions
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0