From: ko1@... Date: 2020-10-29T19:47:27+00:00 Subject: [ruby-core:100657] [Ruby master Feature#17273] shareable_constant_value pragma Issue #17273 has been updated by ko1 (Koichi Sasada). Eregon (Benoit Daloze) wrote in #note-16: > I guess it's relatively rare that a gem would (intentionally) expose a non-frozen constant as part of its API, and that the gem relies on being able to mutate it. To confirm it, we will introduce experimental radical API. Other verification methods are welcome. ---------------------------------------- Feature #17273: shareable_constant_value pragma https://bugs.ruby-lang.org/issues/17273#change-88297 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal ---------------------------------------- This proposal is to introduce `# shareable_constant_value: true` pragma to make constant values shareable objects. With this pragma, you don't need to add `freeze` to access from non-main ractors. ```ruby # shareable_constant_value: true A = [1, [2, [3, 4]]] H = {a: "a"} Ractor.new do p A p H end.take ``` ## Background Now, we can not access constants which contains a unshareable object from the non-main Ractor. ```ruby A = [1, [2, [3, 4]]] H = {a: "a"} Ractor.new do p A #=> can not access non-sharable objects in constant Object::A by non-main Ractor. (NameError) p H end.take ``` If we know we don't modify `A` and `H` is frozen object, we can freeze them, and other ractors can access them. ```ruby A = [1, [2, [3, 4].freeze].freeze].freeze H = {a: "a".freeze}.freeze Ractor.new do p A #=> [1, [2, [3, 4]]] p H #=> {:a=>"a"} end.take ``` Adding nesting data structure, we need many `.freeze` method. Recently, I added `Ractor.make_shareable(obj)` makes `obj` shareable with freezing objects deeply (see [Feature #17274]). We only need to introduce this method for each constant. ```ruby A = Ractor.make_shareable( [1, [2, [3, 4]]] ) H = Ractor.make_shareable( {a: "a"} ) Ractor.new do p A #=> [1, [2, [3, 4]]] p H #=> {:a=>"a"} end.take ``` However, if we have 100 constants, it is troublesome. ## Proposal With `# shareable_constant_value: true`, you can specify all constants are shareable. ```ruby # shareable_constant_value: true A = [1, [2, [3, 4]]] # compiled with: A = Ractor.make_shareable( [1, [2, [3, 4]]] ) H = {a: "a"} # compiled with: H = Ractor.make_shareable( {a: "a"} ) Ractor.new do p A p H end.take ``` (Strictly speaking, don't call `Ractor.make_shareable`, but apply same effect. This means rewriting `Ractor.make_shareable` doesn't affect this behavior) You can specify `# shareable_constant_value: false` in the middle of the place. ```ruby # shareable_constant_value: true S1 = 'str' # p S1.frozen? #=> true # shareable_constant_value: false S2 = 'str' # p S2.frozen? #=> false ``` The effect of this pragma is closed to the scope. ```ruby class C # shareable_constant_value: true A = 'str' p A.frozen? #=> true 1.times do # shareable_constant_value: false B = 'str' p B.frozen? #=> false end end X = 'str' p X.frozen? #=> false ``` `Ractor.make_shareable(obj)` doesn't affect anything to shareable objects. ```ruby # shareable_constant_value: true class C; end D = C p D.frozen? #=> false ``` Some objects can not become shareable objects, so it raises an exception: ```ruby # shareable_constant_value: true T = Thread.new{} #=> `make_shareable': can not make shareable object for # (Ractor::Error) ``` ## Implementation https://github.com/ruby/ruby/pull/3681/files -- https://bugs.ruby-lang.org/ Unsubscribe: