From: "tenderlovemaking (Aaron Patterson) via ruby-core" Date: 2025-01-14T17:05:22+00:00 Subject: [ruby-core:120665] [Ruby master Feature#21033] Allow lambdas that don't access `self` to be Ractor shareable Issue #21033 has been updated by tenderlovemaking (Aaron Patterson). Good catches, thank you. I knew there must be something I was missing, but wasn't sure what. Eregon (Benoit Daloze) wrote in #note-9: > I think it's quite brittle. I agree, but it was the simplest patch. > Things like https://github.com/ko1/debug_inspector would also break it. > And when it breaks it's a potential segfault with Ractors. > > I think something explicit like the existing `nil.instance_exec { lambda { ... } }` is better. > Or maybe some special syntax for `-> {}` lambdas to explicitly not capture self like `-> self=nil {}` or something. I really don't like the idea of new syntax. Adding syntax makes it much harder to port existing applications to use Ractors. I'm specifically looking at adding Ractor support to Rails at the moment, and there are blocks that are shared between threads and would need to be shared between Ractors. New syntax / idioms would mean we have to change all lambda creation sites. Maybe we could add a compilation pass that does the escape analysis? Alternatively, maybe calling `Ractor.make_shareable()` on a proc would set `self` on the proc to nil? ---------------------------------------- Feature #21033: Allow lambdas that don't access `self` to be Ractor shareable https://bugs.ruby-lang.org/issues/21033#change-111490 * 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: #@-e:1 (1,0)-(1,22)> 0000 putself ( 1)[Li] 0001 send , block in
0004 leave == disasm: #@-e:1 (1,7)-(1,22)> 0000 putself ( 1)[LiBc] 0001 putchilledstring "123" 0003 opt_send_without_block 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/