mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Make Class#attached_object respect Ractor isolation
A singleton class is shareable whatever it is attached to, so it can be sent
to another Ractor, and #attached_object then handed over the object itself:
$ ruby -W0 -e 'Ractor.new(p(Object.new).singleton_class) { |o| p o.attached_object }.join'
#<Object:0x0000000120768c40>
#<Object:0x0000000120768c40> # the same object in both Ractors
Raise Ractor::IsolationError when the attached object is unshareable and
lives in another Ractor's objspace. Guarding the reader covers every way a
singleton class is made: Object#clone (rb_singleton_class_clone_and_attach)
and the metaclass of a singleton class (make_metaclass) allocate one without
going through make_singleton_class, so making singleton classes unshareable
instead would have to catch all of them.
An object's owner is known from the objspace of its page, so a single-objspace
GC (mmtk) cannot make this check; the test is skipped there.
[Bug #21649]
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fccc3b9e81
commit
17baadc072
2 files changed
+42
-1
Notes:
git
2026-09-09 09:38:51 +00:00
No files matched your search
@@ -24,6 +24,7 @@
|
||||
#include "internal/box.h"
|
||||
#include "internal/class.h"
|
||||
#include "internal/eval.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/string.h"
|
||||
@@ -2261,6 +2262,9 @@ rb_mod_descendants(VALUE mod)
|
||||
*
|
||||
* Raises an TypeError if the class is not a singleton class.
|
||||
*
|
||||
* Raises a Ractor::IsolationError if the attached object is not shareable and
|
||||
* belongs to another Ractor.
|
||||
*
|
||||
* class Foo; end
|
||||
*
|
||||
* Foo.singleton_class.attached_object #=> Foo
|
||||
@@ -2277,7 +2281,18 @@ rb_class_attached_object(VALUE klass)
|
||||
rb_raise(rb_eTypeError, "'%"PRIsVALUE"' is not a singleton class", klass);
|
||||
}
|
||||
|
||||
return RCLASS_ATTACHED_OBJECT(klass);
|
||||
const VALUE obj = RCLASS_ATTACHED_OBJECT(klass);
|
||||
|
||||
/* A singleton class is shareable whatever it is attached to, so another Ractor can
|
||||
* hold one attached to an unshareable object. Returning it would share it. */
|
||||
if (rb_objspace_foreign_object_p(obj) && !RB_OBJ_SHAREABLE_P(obj)) {
|
||||
/* No klass in the message: naming a singleton class inspects the very object we
|
||||
* must not touch from here. */
|
||||
rb_raise(rb_eRactorIsolationError,
|
||||
"can not get an unshareable attached object from another Ractor");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -958,4 +958,30 @@ class TestRactor < Test::Unit::TestCase
|
||||
$stdout.puts "sent"
|
||||
RUBY
|
||||
end
|
||||
|
||||
def test_attached_object_of_unshareable_object
|
||||
omit 'objspace per Ractor is how an object\'s owner is known' unless GC.config[:implementation] == 'default'
|
||||
assert_ractor(<<~'RUBY')
|
||||
# A singleton class is shareable whatever it is attached to, so sending one used to
|
||||
# hand the attached object to another Ractor through #attached_object.
|
||||
o = Object.new
|
||||
assert_equal true, Ractor.shareable?(o.singleton_class)
|
||||
assert_same o, o.singleton_class.attached_object
|
||||
|
||||
r = Ractor.new(o.singleton_class) do |sc|
|
||||
begin
|
||||
sc.attached_object
|
||||
rescue Ractor::IsolationError
|
||||
:isolated
|
||||
end
|
||||
end
|
||||
assert_equal :isolated, r.value
|
||||
|
||||
# A shareable attached object, and a Ractor's own unshareable one, are fine.
|
||||
shareable = Ractor.make_shareable(Object.new)
|
||||
assert_same shareable, Ractor.new(shareable.singleton_class) { |sc| sc.attached_object }.value
|
||||
assert_same String, Ractor.new(String.singleton_class) { |sc| sc.attached_object }.value
|
||||
assert_equal true, Ractor.new { own = Object.new; own.singleton_class.attached_object.equal?(own) }.value
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user