🙌

[Feature #21028] Ractor の共有可能でないオブジェクトを見つける方法がほしいというチケット

に公開

[Feature #21028] Method for finding why an object isn't Ractor shareable

  • Ractor の共有可能でないオブジェクトを見つける方法がほしいというチケット
  • 任意のオブジェクトが共有可能オブジェクトかどうかは Ractor.shareable? で判定できる
# 通常のオブジェクトは共有可能オブジェクトではない
obj = [1, 2, 3]
pp Ractor.shareable? obj # => false

# frozen の場合は共有可能オブジェクトとして扱われる
obj = [1, 2, 3].freeze
pp Ractor.shareable? obj # => true
  • これはオブジェクトがネストしている場合にも判定できる
# すべてが frozen の場合は共有可能オブジェクトとして扱われる
obj = [
  "homu".freeze,
  "mami".freeze,
  "mado".freeze,
].freeze

pp Ractor.shareable? obj
# => true
  • こういう複雑な構造のオブジェクトの場合に『どのオブジェクトが共有可能オブジェクトでないのか』を見つける方法がほしいという意図になる
# 一部の要素が共有可能オブジェクトでなければ共有可能オブジェクトとして扱えない
obj = [
  "homu".freeze,
  "mami",
  "mado".freeze,
].freeze

# これだけ見たときに『どれが共有可能オブジェクトでないのか』がわからない
pp Ractor.shareable? obj
# => false
  • 具体的な案としては Ractor.unshareable_references? や Ractor.shareable?(obj) { |not_shareable_obj| } みたいなコードが提示されていますね
  • これなんですがコメントでは ObjectSpace.reachable_objects_from を利用した実装が提示されていますね
    • ObjectSpace.reachable_objects_from は『obj から到達可能なすべてのオブジェクトを返す』メソッド
    • ObjectSpace.#reachable_objects_from (Ruby 3.4 リファレンスマニュアル)
    • これは知らなかった
    • 要は obj に対して ObjectSpace.reachable_objects_from を利用してネストした走査を行い shareable? でないオブジェクトを探す、みたいな感じですかね
require "objspace"

def find_path_to_unshareable_object(obj, path = [], visited = {})
  path += [obj]
  return if visited[obj]
  visited[obj] = true

  return if Ractor.shareable?(obj)

  objs = ObjectSpace.reachable_objects_from(obj)
  return path if objs.all? { Ractor.shareable?(it) }

  objs.each do |obj2|
    res = find_path_to_unshareable_object(obj2, path, visited)
    return res if res
  end
  return nil
end

pp *find_path_to_unshareable_object([1, 2, 3, "str".freeze, { :key => -> {} }])
#=> [1, 2, 3, "str", {key: #<Proc:0x00007f0a38e0ff30 t.rb:20 (lambda)>}]
#=> {key: #<Proc:0x00007f0a38e0ff30 t.rb:20 (lambda)>}
#=> #<Proc:0x00007f0a38e0ff30 t.rb:20 (lambda)>
#=> main
GitHubで編集を提案

Discussion