mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
Include ivar name and class name in the Ractor isolation error message
- ### Problem
When a Ractor isolation error happens due to setting an ivar on a
class, the message doesn't include the name of the ivar and neither
the name of the class. It makes it difficult to track down where the
isolation problem comes from.
I'd like the message to be similar as what we get when a Ractor
access an unshareable ivar.
This is the current behaviour
```ruby
class Foo
def self.foo
@foo ||= "test"
end
end
Ractor.new do
Foo.foo # can not set instance variables of classes/modules by non-main Ractors
end.join
Foo.foo
Ractor.new do
Foo.foo # can not get unshareable values from instance variables of classes/modules from non-main Ractors (@foo from Foo)
end.join
```
### Solution
Modify the error message to include the ivar name and the class/module name.
Co-authored-by: Yaroslav Markin <yaroslav@markin.net>
This commit is contained in:
1 parent
7a39a7a957
commit
cc57f9c818
2 files changed
+100
-4
Notes:
git
2026-10-22 21:51:53 +00:00
No files matched your search
@@ -888,6 +888,91 @@ assert_equal "can not get unshareable values from instance variables of classes/
|
||||
end
|
||||
RUBY
|
||||
|
||||
# setting an ivar of a class/module from a non-owner Ractor reports the ivar and class
|
||||
assert_equal "can not set instance variables of classes/modules created by another Ractor (@iv from C)", <<~'RUBY', frozen_string_literal: false
|
||||
class C
|
||||
@iv = 'str'
|
||||
end
|
||||
|
||||
r = Ractor.new do
|
||||
class C
|
||||
@iv = 'other'
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
r.value
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
RUBY
|
||||
|
||||
# a non-main Ractor can read, set, and remove ivars of classes/modules it created
|
||||
assert_equal 'ok', <<~'RUBY', frozen_string_literal: false
|
||||
Ractor.new do
|
||||
[Class.new, Module.new].each do |mod|
|
||||
mod.instance_variable_set(:@iv, 'str')
|
||||
raise unless mod.instance_variable_get(:@iv) == 'str'
|
||||
raise unless mod.remove_instance_variable(:@iv) == 'str'
|
||||
end
|
||||
:ok
|
||||
end.value
|
||||
RUBY
|
||||
|
||||
# even main cannot modify another Ractor's module; reporting its name must not call to_s
|
||||
assert_equal 'true', <<~'RUBY', frozen_string_literal: false
|
||||
port = Ractor::Port.new
|
||||
r = Ractor.new(port) do |port|
|
||||
mod = Module.new
|
||||
mod.set_temporary_name('worker_module')
|
||||
mod.instance_variable_set(:@iv, 'str')
|
||||
def mod.to_s = raise('to_s must not be called')
|
||||
port << mod
|
||||
Ractor.receive
|
||||
end
|
||||
mod = port.receive
|
||||
|
||||
begin
|
||||
messages = [[:instance_variable_set, :@iv, 'other'], [:remove_instance_variable, :@iv]].map do |args|
|
||||
begin
|
||||
mod.public_send(*args)
|
||||
rescue Ractor::IsolationError => e
|
||||
e.message
|
||||
end
|
||||
end
|
||||
messages == ["can not set instance variables of classes/modules created by another Ractor (@iv from worker_module)"] * 2
|
||||
ensure
|
||||
r << nil
|
||||
r.join
|
||||
end
|
||||
RUBY
|
||||
|
||||
# normal and JIT-compiled ivar reads report the same names without calling to_s
|
||||
assert_equal 'true', <<~'RUBY', frozen_string_literal: false
|
||||
class C
|
||||
@iv = 'str'
|
||||
def self.read = @iv
|
||||
def self.to_s = raise('to_s must not be called')
|
||||
end
|
||||
|
||||
Ractor.new do
|
||||
messages = []
|
||||
begin
|
||||
C.instance_variable_get(:@iv)
|
||||
rescue Ractor::IsolationError => e
|
||||
messages << e.message
|
||||
end
|
||||
40.times do
|
||||
begin
|
||||
C.read
|
||||
rescue Ractor::IsolationError => e
|
||||
messages << e.message
|
||||
end
|
||||
end
|
||||
messages == ["can not get unshareable values from instance variables of classes/modules created by another Ractor (@iv from C)"] * 41
|
||||
end.value
|
||||
RUBY
|
||||
|
||||
# setting an ivar on a shareable but unfrozen object is not allowed, by instance_variable_set
|
||||
assert_equal "can't modify instance variables of a shareable Ractor", %q{
|
||||
shared = Ractor.new{}
|
||||
|
||||
+15
-4
@@ -1274,7 +1274,13 @@ class_ivar_set_ractor_check(VALUE klass, ID id)
|
||||
{
|
||||
if (rb_is_instance_id(id) && // check only normal ivars
|
||||
UNLIKELY(!rb_class_owned_p(klass))) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not set instance variables of classes/modules created by another Ractor");
|
||||
rb_raise(
|
||||
rb_eRactorIsolationError,
|
||||
"can not set instance variables of classes/modules "
|
||||
"created by another Ractor (%"PRIsVALUE" from %"PRIsVALUE")",
|
||||
rb_id2str(id),
|
||||
rb_class_path(klass)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,7 +1633,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
||||
"can not get unshareable values from instance variables of classes/modules "
|
||||
"created by another Ractor (%"PRIsVALUE" from %"PRIsVALUE")",
|
||||
rb_id2str(id),
|
||||
obj
|
||||
rb_class_path(obj)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1659,8 +1665,13 @@ rb_ivar_get_at(VALUE obj, attr_index_t index, ID id)
|
||||
VALUE val = rb_imemo_fields_ptr(fields_obj)[index];
|
||||
|
||||
if (UNLIKELY(!rb_class_owned_p(obj)) && !rb_ractor_shareable_p(val)) {
|
||||
rb_raise(rb_eRactorIsolationError,
|
||||
"can not get unshareable values from instance variables of classes/modules created by another Ractor");
|
||||
rb_raise(
|
||||
rb_eRactorIsolationError,
|
||||
"can not get unshareable values from instance variables of classes/modules "
|
||||
"created by another Ractor (%"PRIsVALUE" from %"PRIsVALUE")",
|
||||
rb_id2str(id),
|
||||
rb_class_path(obj)
|
||||
);
|
||||
}
|
||||
|
||||
return val;
|
||||
|
||||
Reference in new issue
Block a user