Issue #17273 has been updated by jeremyevans0 (Jeremy Evans).


Eregon (Benoit Daloze) wrote in #note-16:
> Eregon (Benoit Daloze) wrote in #note-14:
> > Of course, no gems should directly mutate constants of another gem.
> 
> I missed that the gem doesn't need to mutate `A` to break `OtherLib`.
> 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.

I agree that it's relatively rare and not a good idea.  However, Sequel does this (Sequel::DATABASES).  It's been around since 2008 and many external users rely on reading from it, and I haven't wanted to break backwards compatibility to remove it.  Internal access is always protected by a mutex for thread-safety, and it only gets mutated for new or removed database connections, so it isn't a problem in practice.

----------------------------------------
Feature #17273: shareable_constant_value pragma
https://bugs.ruby-lang.org/issues/17273#change-88299

* 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 #<Thread:0x000055952e40ffb0 /home/ko1/ruby/src/trunk/test.rb:3 run> (Ractor::Error)
```

## Implementation

https://github.com/ruby/ruby/pull/3681/files



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>