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.
Updated by ioquatix (Samuel Williams) 7 months ago
Proposed change: https://github.com/ruby/ruby/pull/10482
Updated by ioquatix (Samuel Williams) 7 months ago
- Related to Feature #20102: Introduce `Fiber#resuming?` added
Updated by ioquatix (Samuel Williams) 7 months ago
With the proposed change, the following program:
root_fiber = Fiber.current
f1 = Fiber.new do
puts "f1 enter"
root_fiber.transfer
puts "f1 exit"
ensure
puts "f1 ensure"
end
f2 = Fiber.new do
puts "f2 enter"
f1.resume
puts "f2 exit"
ensure
puts "f2 ensure"
end
f2.transfer
begin
puts "raise"
f2.raise(Interrupt)
rescue Interrupt => e
puts "rescue Interrupt"
end
... generates the following output:
f2 enter
f1 enter
raise
f1 ensure
f2 ensure
rescue Interrupt
Updated by ioquatix (Samuel Williams) 7 months ago
- Related to Bug #20089: Fiber#kill transfers to root fiber added
Updated by matz (Yukihiro Matsumoto) 7 months ago
I see no problem in the proposal. I agree.
Matz.
Updated by ioquatix (Samuel Williams) 7 months ago
Thanks @matz (Yukihiro Matsumoto).
To clarify, there are two issues are we addressing.
(1) Improve consistency of Thread.current.raise
and Fiber.current.raise
.
(2) Follow resuming_fiber
when raising exceptions.
They overlap, so are addressed by the same PR.
Updated by ioquatix (Samuel Williams) 7 months ago
- Status changed from Open to Closed
- Backport changed from 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN to 3.1: REQUIRED, 3.2: REQUIRED, 3.3: REQUIRED
Updated by k0kubun (Takashi Kokubun) 5 months ago
- Backport changed from 3.1: REQUIRED, 3.2: REQUIRED, 3.3: REQUIRED to 3.1: REQUIRED, 3.2: REQUIRED, 3.3: DONE
ruby_3_3 5c06e930748ef6bdb4ac4751ba16b7b604da3db0 merged revision(s) 6ade36c06b7cef948099b8f5f483763498705d12.
Updated by nagachika (Tomoyuki Chikanaga) 4 months ago
I think this change seems somewhat like a spec change. Could this change potentially reveal latent errors in the application and affect its operation?
Additionally, regardless of the case, I believe the ruby_version_is guard in rubyspec will need to be updated if we backport it to stable branches.
Updated by nagachika (Tomoyuki Chikanaga) 4 months ago
- Backport changed from 3.1: REQUIRED, 3.2: REQUIRED, 3.3: DONE to 3.1: REQUIRED, 3.2: DONE, 3.3: DONE
ruby_3_2 2f8f17e842666abb05ca522d6072c957fab0e12e merged revision(s) 5d1702e01a36e11b183fe29ce10780a9b1a41cf0.
Updated by nagachika (Tomoyuki Chikanaga) 4 months ago
- Backport changed from 3.1: REQUIRED, 3.2: DONE, 3.3: DONE to 3.1: REQUIRED, 3.2: REQUIRED, 3.3: DONE
Sorry, I accidentally changed a different ticket.
Updated by nagachika (Tomoyuki Chikanaga) 4 months ago
- Backport changed from 3.1: REQUIRED, 3.2: REQUIRED, 3.3: DONE to 3.1: REQUIRED, 3.2: WONTFIX, 3.3: DONE