Actions
Bug #20414
closed`Fiber#raise` should recurse to `resumed_fiber` rather than failing.
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.
Actions
Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0