🎉

[Feature #21767] freeze されている proc を暗黙的に共有可能オブジェクトとして扱えるようにする提案

に公開

[Feature #21767] Consider procs which self is Ractor-shareable as Ractor shareable

  • Ractorproc を参照する場合には通常 Ractor.make_shareable で共有可能オブジェクトとして定義しておく必要がある
class C
  ARRAY = [1, 2, 3].freeze
  PROC = Ractor.make_shareable proc { ARRAY }
end

p Ractor.new { C::PROC.call }.value
# => [1, 2, 3]
  • またこれは procself が共有可能オブジェクトのときのみ proc を共有可能オブジェクトとして扱える
ARRAY = [1, 2, 3].freeze

# self が共有可能オブジェクトでない場合はエラーになる
PROC = Ractor.make_shareable proc { ARRAY }
# => error: Proc's self is not shareable: #<Proc:0x0000751808c36500 test.rb:2> (Ractor::IsolationError)
  • このチケットでは Ractor.make_shareable で共有可能オブジェクト化するのではなくて procfreeze されている場合に『共有可能オブジェクトとして扱えるようにする』という提案になる
class C
  ARRAY = [1, 2, 3].freeze
  # Ractor.make_shareable ではなくて freeze されていれば共有可能オブジェクトとして許容する
  PROC = proc { ARRAY }.freeze
end

# 現状はエラーになるがこれを許容する
p Ractor.new { C::PROC.call }.value
  • 具体的には以下の条件に当てはまる場合には proc を共有可能オブジェクトとして扱えるようにする
    • procfrozen になっている
    • procself が共有可能オブジェクトである
  • また、以下のように proc 内で共有可能オブジェクトでないオブジェクトを参照し、それを Ractor から呼び出した場合には対応前と対応後で以下のような『例外が発生するタイミング』が異なるみたいですね
class C
  # 共有可能オブジェクトではない定数を proc で参照している
  ARRAY = []
  # 対応後は p ARRAY で例外が発生する
  PROC = proc { p ARRAY }
end

# 現状では C::PROC.call で例外が発生する
Ractor.new { C::PROC.call }.value
  • あとコメントで Ractor.shareable_proc と同じように『共有可能オブジェクトでない値を参照した場合』にエラーにする必要があると指摘されているんですがここでも非互換になる可能性があるみたですね
  • どういうことかというと以下のように eval で外部の共有可能でないオブジェクトを参照している場合に proc を呼び出すとエラーになる
class Foo
  a = "homu"
  SOME_PROC = -> { eval "a" }.freeze
  SOME_PROC_SHAREABLE = Ractor.shareable_proc(&SOME_PROC)
end

Foo::SOME_PROC_SHAREABLE.call
# => error: can not access variable 'a' from isolated Proc (SyntaxError)
  • なので同様に SOME_PROC が暗黙的に共有可能オブジェクトとして扱われ SOME_PROC.call を呼び出すと同様にエラーになる可能性がある
    • これは Ractor を使用していない場合でもエラーになってしまう
  • このあたりの互換性をどこまで考慮すべきなのか、って話になりそうですがそもそも procfreeze しているケースは少ないだろうと言うことで無視していいんじゃないか、とも意見されてしますね
GitHubで編集を提案

Discussion