Project

General

Profile

Actions

Feature #21767

open

Consider procs which `self` is Ractor-shareable as Ractor shareable

Feature #21767: Consider procs which `self` is Ractor-shareable as Ractor shareable

Added by osyoyu (Daisuke Aritomo) about 9 hours ago. Updated about 1 hour ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:124051]

Description

I would like to allow procs which self is Ractor-shareable to be automatically eligible as shareable, without an explicit Ractor.make_shareable call.

class C
  PROC = proc { p ARRAY }.freeze
end

Ractor.new { C::PROC.call }.value # Allow this, since `C` is shareable

Proposal is: Consider procs/lambdas which meet the following condition as Ractor-shareable.

  • The proc is frozen.
  • The proc's self is shareable.

This proposal is has taken inspiration from #21033 .

Usecase

The main usecase in mind is procs/lambdas in class-level constants. Some libraries store procs in constants as a convenient place for library-wide logic. Those procs usually do not access unshareable state, thus conceptually safe to be shared across Ractors. However, the current limitation completely blocks this.

Examples may be found in ruby/ruby, and I have submitted a pull request to migrate one to Ractor.shareable_proc.

class Pathname
  SAME_PATHS = if File::FNM_SYSCASE.nonzero?
    # Avoid #zero? here because #casecmp can return nil.
    proc {|a, b| a.casecmp(b) == 0}
  else
    proc {|a, b| a == b}
  end
end

https://github.com/search?q=repo%3Aruby%2Fruby%20%2F(%3F-i)%5BA-Z%5D%20%3D%20(proc%7Clambda)%2F&type=code

More examples can be found in public code.

https://github.com/search?q=language%3Aruby+%2F%28%3F-i%29%5BA-Z%5D+%3D+%28proc%7Clambda%29%2F&type=code

It can be observed that a good portion of these do not access unshareable state.

Appending .freeze would be much more acceptable than redefining using Ractor.shareable_proc, which is a Ruby 4.0-only feature.

Discussion: Change of behavior when illegal access occurs in proc

Consider this code. The proc accesses non-frozen C::ARRAY, which is against the rules of Ractors regardless of this patch.

class C
  ARRAY = []
  PROC = proc { p ARRAY }
end

# Still illegal since C::ARRAY is not shareable
Ractor.new { C::PROC.call }.value

This code used to raise on C::PROC.call (can not access non-shareable objects in constant C::PROC by non-main Ractor.). When this patch is applied, it will raise on p ARRAY (can not access non-shareable objects in constant C::ARRAY by non-main ractor.).

This could be debatable change. If this is not acceptable, I'd like to revisit #21033 .


Related issues 1 (0 open1 closed)

Related to Ruby - Feature #21557: Ractor.shareable_proc to make sharable Proc objects, safely and flexiblyClosedko1 (Koichi Sasada)Actions

Updated by Eregon (Benoit Daloze) about 2 hours ago Actions #1

  • Related to Feature #21557: Ractor.shareable_proc to make sharable Proc objects, safely and flexibly added

Updated by Eregon (Benoit Daloze) about 1 hour ago Actions #2 [ruby-core:124053]

Naturally it would need to follow the rules of Ractor.shareable_proc (#21557) regarding accessing captured variables:

  • Any captured variable must not be reassigned
  • The value of every captured variable must be shareable (not sure how common that is the case, so this might limit the usefulness of this proposal quite a bit in practice)

Besides that, it sounds convenient that Procs which satisfy those rules (i.e. no exception when Ractor.shareable_proc is called on them) + already have a shareable self are automatically shareable.
It's just of course that Procs already having a shareable self are not very common.

There is one incompatibility though, that by making that Proc shareable it can behave differently on the main Ractor than before this proposal:

class Foo
  a = 42
  SOME_PROC = -> { eval "a" }
  a += 1
  SOME_PROC_SHAREABLE = Ractor.shareable_proc(&SOME_PROC)
end

p Foo::SOME_PROC.call
puts
Foo::SOME_PROC_SHAREABLE.call

SOME_PROC is not detected as accessing captured variables because of eval.
If the Proc is not shareable it still works as it always did (return 43).
If SOME_PROC is made automatically shareable by this proposal then it now raises an error, as simulated by SOME_PROC_SHAREABLE:

$ ruby auto_shareable_proc.rb
43

(eval at auto_shareable_proc.rb:3): (eval at auto_shareable_proc.rb:3):1: can not access variable 'a' from isolated Proc (SyntaxError)

And the same when using binding inside the block.
Also SOME_PROC.binding doesn't work for a shareable Proc.

Actions

Also available in: PDF Atom