[ruby-core:115952] [Ruby master Bug#20102] Introduce `Fiber#resuming?`

Issue #20102 has been reported by ioquatix (Samuel Williams). ---------------------------------------- Bug #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by Eregon (Benoit Daloze). Could you make it `raise FiberError` if it's called on a Fiber of a different thread? Because that's always racy (i.e. it might have changed by the time `resuming?` returns) so it seems wrong to query that for a Fiber belonging to another thread. ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-105955 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by ioquatix (Samuel Williams). @Eregon I appreciate your input. I don't mind doing that, but isn't that the same for all methods on Fiber? In other words, the GVL provides a bit of a safety net. Should we apply this model to other methods? Maybe we need to define what methods can be called safely from one thread or another. What about if a user has a mutex to control access, or the fiber/thread is not actually running at the time of the call? I assume there is a lot of historical context/implementation where such behaviour is just racy but nothing is done to protect it. In other words, I feel like the problem you are trying to address spans almost all of Ruby's mutable state... tricky. ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-105996 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by Eregon (Benoit Daloze). ioquatix (Samuel Williams) wrote in #note-4:
@Eregon I appreciate your input. I don't mind doing that, but isn't that the same for all methods on Fiber?
It is, and many methods on Fiber are already not allowed to be called for Fiber belonging to other threads (e.g. resume/transfer/storage/...). Since we are adding a new method, we might as well encourage correct usage and discourage incorrect usage (+ it means the state for resuming does not need to become `volatile` which would be bad for performance).
In other words, the GVL provides a bit of a safety net.
It does not, the GVL could be released e.g. just after `resuming?` is done but before the caller gets the value and handle it. ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-106001 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by matz (Yukihiro Matsumoto). It appears to me that the method you want is to determine if an exception can legitimately be sent to the fiber. I am not rejecting the addition of such a method, but in that case, I think the name `resuming?` would be inappropriate. Any other name candidate? Matz. ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-106297 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by ioquatix (Samuel Williams). Status changed from Assigned to Closed I found a different way to solve this problem, which I think is better, so I'll open a new issue. ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-107842 * Author: ioquatix (Samuel Williams) * Status: Closed * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/

Issue #20102 has been updated by ioquatix (Samuel Williams). Here is the updated proposal: https://bugs.ruby-lang.org/issues/20414 ---------------------------------------- Feature #20102: Introduce `Fiber#resuming?` https://bugs.ruby-lang.org/issues/20102#change-107846 * Author: ioquatix (Samuel Williams) * Status: Closed * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some tricky edge cases when using `Fibre#raise` and `Fiber#kill`, e.g. ```ruby fiber = nil killer = Fiber.new do fiber.raise("Stop") end fiber = Fiber.new do killer.resume end fiber.resume # 4:in `raise': attempt to raise a resuming fiber (FiberError) # 4:in `block in <main>' ``` Async has to deal with this edge case explicitly by rescuing the exception: https://github.com/socketry/async/blob/ffd019d9c1d547926a28fe8f36bf7bfe91d8a... I'd like to avoid doing that and instead just ask "Can I kill/raise on this fiber right now?" which is determined by whether the fiber itself can be resumed or transferred to. To address this, I'd like to introduce `Fiber#resuming?`: ```c /* * call-seq: fiber.resumed? -> true or false * * Whether the fiber is currently resumed. */ VALUE rb_fiber_resuming_p(VALUE fiber_value) { struct rb_fiber_struct *fiber = fiber_ptr(fiber_value); if (FIBER_TERMINATED_P(fiber)) return RUBY_Qfalse; return RBOOL(fiber->resuming_fiber); } ``` See the PR: https://github.com/ruby/ruby/pull/9382 -- https://bugs.ruby-lang.org/
participants (3)
-
Eregon (Benoit Daloze)
-
ioquatix (Samuel Williams)
-
matz (Yukihiro Matsumoto)