Issue #22291 has been updated by matz (Yukihiro Matsumoto). Accepted. I agree that freezing means the object's own state is immutable, not just its instance variables, so we should not freeze these objects. Forbidding instance variables on them is the right approach. Please define it as an invariant: a shareable object that is not frozen never has instance variables. This should also hold when C extensions define such objects in the future. Since `ENV` is not an experimental API, please check the impact on existing code that sets instance variables on it. This is a spec change, so no backport. Matz. ---------------------------------------- Bug #22291: Instance variables should be forbidden on Ractor-shareable objects https://bugs.ruby-lang.org/issues/22291#change-118899 * Author: jhawthorn (John Hawthorn) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Usually Ractor-shareable objects are frozen, but we have a number of objects which are shareable even though they aren't frozen (and likely will have more in the future from C extensions). * Classes/Modules (ignored in this issue, they have special ivar handling) * `Ractor` * `Ractor::Port` * Isolated procs/lambdas from `Ractor.shareable_proc` * `ENV` These are set shareable without the usual checks that all referenced objects are also shareable. This causes a potential issue, because these objects could have unshareable instance variables, breaking the Ractor invariant (potentially causing race conditions and segmentation faults). Currently, we attempt to avoid this by having a check on ivars read that forbids reading these from a non-main Ractor ``` R = Ractor.new {} R.instance_variable_set(:@iv, +"mutable") Ractor.new { R.instance_variable_get(:@iv) }.value #=> Ractor::IsolationError: can not access instance variables of shareable objects from non-main Ractors ``` This is detected by checking on ivar read for objects which are shareable but **not** frozen. However this isn't sound because most of these unshareable objects can be frozen, turning the check off. ```ruby R = Ractor.new {} R.instance_variable_set(:@iv, +"mutable") R.freeze Ractor.new { R.instance_variable_get(:@iv) }.value # => no error, created Ractor got access to an object it shouldn't ``` (weird quirk: `ENV#freeze` raises, but you can `Kernel.instance_method(:freeze).bind_call(ENV)` so it's still an issue) I propose that we fix this by forbidding instance variable writes to objects which are shareable (whether or not thy are frozen). This removes the need to check on read and maintains the Ractor invariant. ``` R = Ractor.new {} R.instance_variable_set(:@iv, 123) #=> Ractor::IsolationError: can not set instance variables of shareable Ractor objects Ractor.new { R.instance_variable_get(:@foo) # => always nil, because ivars are forbidden } ``` These objects behave essentially as though they're frozen, but only the IVs are frozen. This is an important issue to solve both for correctness, and because I want us to decide the semantics so that they can be implemented in ZJIT. Currently ZJIT won't compile ivar reads on multi-ractor mode because of this issue. (I will link a patch implementing this shortly) -- https://bugs.ruby-lang.org/