[#120465] [Ruby master Bug#20998] rb_str_locktmp() changes flags of frozen strings and string literals — "Eregon (Benoit Daloze) via ruby-core" <ruby-core@...>
Issue #20998 has been reported by Eregon (Benoit Daloze).
17 messages
2025/01/03
[ruby-core:120646] [Ruby master Feature#21033] Allow lambdas that don't access `self` to be Ractor shareable
From:
"tenderlovemaking (Aaron Patterson) via ruby-core" <ruby-core@...>
Date:
2025-01-14 00:01:33 UTC
List:
ruby-core #120646
Issue #21033 has been updated by tenderlovemaking (Aaron Patterson).
File 0001-Allow-lambdas-that-don-t-access-self-to-be-made-shar.patch added
I guess GitHub is not working right now, so I'll just attach the patch here.
----------------------------------------
Feature #21033: Allow lambdas that don't access `self` to be Ractor shareable
https://bugs.ruby-lang.org/issues/21033#change-111474
* Author: tenderlovemaking (Aaron Patterson)
* Status: Open
----------------------------------------
Hi,
I would like to allow lambdas that don't access `self` to be eligible for Ractor shareability regardless of the shareability status of `self`.
Consider the following code:
```ruby
class Foo
def make_lambda
x = 123
lambda { x }
end
end
Ractor.make_shareable(Foo.new.make_lambda)
```
With Ruby 3.4.X, this will raise an exception. The reason is because `self`, which is an unfrozen instance of `Foo`, is not shareable. However, we can see from the code that the lambda doesn't access `self`. I would like to make lambdas such as the ones above eligible for shareability, and I've submitted a patch [here](https://github.com/ruby/ruby/pull/12567).
I think we can detect access to `self` by scanning the instructions in the lambda. Any references to `putself`, `getinstancevariable`, or `setinstancevariable` will result in using the default behavior (checking the frozen status of `self`).
## Considerations
### What about `eval`?
I think that `eval` is not a problem because calling eval has an implicit reference to `self`:
```
$ ./miniruby --dump=insns -e 'lambda { eval("123") }'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,22)>
0000 putself ( 1)[Li]
0001 send <calldata!mid:lambda, argc:0, FCALL>, block in <main>
0004 leave
== disasm: #<ISeq:block in <main>@-e:1 (1,7)-(1,22)>
0000 putself ( 1)[LiBc]
0001 putchilledstring "123"
0003 opt_send_without_block <calldata!mid:eval, argc:1, FCALL|ARGS_SIMPLE>
0005 leave [Br]
```
If we try to call `eval` inside the lambda, there will be an implicit `putself` instruction added which means we will fall back to the old behavior.
### What about `binding`?
If you call `binding` from inside the `lambda` there will be a `putself` instruction so we fall back to the old behavior. This is the same as the `eval` case.
### What about `binding` via a local?
If you assign `binding` to a local, shareability will fail because the `lambda` references an unshareable local:
```ruby
class Foo
def make_lambda
x = binding
lambda { x }
end
end
b = Foo.new.make_lambda
# exception because local `x` is not shareable
Ractor.make_shareable(b)
```
### What about accessing `binding` via the proc itself?
The lambda can references itself via a local and access binding, but again this will fail isolation when locals are scanned:
```ruby
class Foo
def make_lambda
x = lambda { x.binding.eval("self") }
end
end
b = Foo.new.make_lambda
# exception because local `x` is not shareable
Ractor.make_shareable(b)
```
I _think_ I've covered all cases where `self` can possibly escape. I would appreciate any feedback.
Again, [here is the patch](https://github.com/ruby/ruby/pull/12567).
Thanks.
---Files--------------------------------
0001-Allow-lambdas-that-don-t-access-self-to-be-made-shar.patch (6.51 KB)
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/