Add Ractor#alive?

Matches Thread#alive? and Fiber#alive?. Returns false once the ractor's
result has been recorded. Reads sync.legacy under the ractor lock, as
Ractor#monitor does, so it cannot race with termination.

[Feature #22441]
This commit is contained in:
John Hawthorn committed 2026-10-21 16:48:38 -07:00
1 parent 2500ab0668
commit f609897fa4
4 files changed
+66
Notes: git 2026-10-21 23:49:07 +00:00

No files matched your search

+4
View File
@@ -157,6 +157,9 @@ Note: We're only listing outstanding class updates.
`timeout: 0` never blocks: it takes a message if one is already queued
and returns `nil` otherwise. [[Feature #22255]]
* `Ractor#alive?` is added. It returns `false` once the ractor has
terminated. [[Feature #22441]]
* Range
* `Range#clamp` is added. It returns a new `Range` instance whose
@@ -738,6 +741,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #22297]: https://bugs.ruby-lang.org/issues/22297
[Feature #22309]: https://bugs.ruby-lang.org/issues/22309
[Feature #22400]: https://bugs.ruby-lang.org/issues/22400
[Feature #22441]: https://bugs.ruby-lang.org/issues/22441
[PR #17201]: https://github.com/ruby/ruby/pull/17201
[GH-psych #805]: https://github.com/ruby/psych/pull/805
[prism-v1.8.0]: https://github.com/ruby/prism/releases/tag/v1.8.0
+17
View File
@@ -400,6 +400,23 @@ class Ractor
"#<Ractor:##{id}#{name ? ' '+name : ''}#{loc ? " " + loc : ''} #{status}>"
end
#
# call-seq:
# ractor.alive? -> true or false
#
# Returns +true+ if the ractor has not yet terminated, +false+ once it has
# finished running (whether normally or by an unhandled exception).
#
# r = Ractor.new { Ractor.receive }
# r.alive? #=> true
# r << :done
# r.join
# r.alive? #=> false
#
def alive?
__builtin_ractor_alive_p
end
alias to_s inspect
# Returns the name set in Ractor.new, or +nil+.
+16
View File
@@ -819,6 +819,22 @@ ractor_monitor(rb_execution_context_t *ec, VALUE self, VALUE port)
return terminated ? Qfalse : Qtrue;
}
static VALUE
ractor_alive_p(rb_execution_context_t *ec, VALUE self)
{
rb_ractor_t *r = RACTOR_PTR(self);
bool alive;
RACTOR_LOCK(r);
{
alive = UNDEF_P(r->sync.legacy);
}
RACTOR_UNLOCK(r);
// rb_ractor_terminate_atfork terminates ractors without setting legacy
return RBOOL(alive && !rb_ractor_status_p(r, ractor_terminated));
}
static VALUE
ractor_unmonitor(rb_execution_context_t *ec, VALUE self, VALUE port)
{
+29
View File
@@ -13,6 +13,35 @@ class TestRactor < Test::Unit::TestCase
RUBY
end
def test_alive_p
assert_ractor(<<~'RUBY')
r = Ractor.new { Ractor.receive }
assert_predicate r, :alive?
r << :done
r.join
assert_not_predicate r, :alive?
r = Ractor.new do
Thread.current.report_on_exception = false
raise "boom"
end
assert_raise(Ractor::RemoteError) { r.join }
assert_not_predicate r, :alive?
RUBY
end
def test_alive_p_after_fork
omit "fork not supported" unless Process.respond_to?(:fork)
assert_ractor(<<~'RUBY')
r = Ractor.new { Ractor.receive }
assert_predicate r, :alive?
pid = fork { exit!(r.alive? ? 1 : 0) }
_, st = Process.wait2(pid)
assert_predicate st, :success?
assert_predicate r, :alive?
RUBY
end
def test_shareability_of_iseq_proc
assert_raise Ractor::IsolationError do
foo = []