From: "Eregon (Benoit Daloze) via ruby-core" Date: 2025-01-15T10:12:49+00:00 Subject: [ruby-core:120695] [Ruby master Feature#21033] Allow lambdas that don't access `self` to be Ractor shareable Issue #21033 has been updated by Eregon (Benoit Daloze). tenderlovemaking (Aaron Patterson) wrote in #note-15: > Until this ticket, I was operating under the assumption it worked this way ����. I agree with you. Glad we agree on this :) I extracted that to https://bugs.ruby-lang.org/issues/21039. > I would like that, but as I mentioned, I don't want to change all call sites (if possible). At least new code could take advantage of such a syntax. I think it's not possible (in general at least) to not change call sites without breaking the existing's block intention and semantics. If a block doesn't use self and doesn't use captured variables (or they are never written to) then yes it should be mostly transparent (except when e.g. adding `p` for debugging), but that seems too difficult to detect and would only cover some of the blocks which need to be passed to Ractors. I see it a bit like marking C extensions as Ractor-safe, by default it's unsafe for good reasons (it would likely segfault otherwise or expose broken behavior), so we need to review the code and mark as safe where feasible or needed. For blocks, that marking would be some kind of change in the code where it's declared, because that's where it can be understood to have different semantics than normal Ruby blocks. I wrote some more ideas about ways to mark such blocks in #21039 (sorry for spreading the discussion). I also linked this issue to previous tickets discussing basically the same thing. ---------------------------------------- Feature #21033: Allow lambdas that don't access `self` to be Ractor shareable https://bugs.ruby-lang.org/issues/21033#change-111529 * 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/