diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 51d2762d939..cedc73da789 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -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{} diff --git a/variable.c b/variable.c index 5d6e1b17637..e23c5056f51 100644 --- a/variable.c +++ b/variable.c @@ -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;