Skip to content

Commit 01f7338

Browse files
osyoyumame
andcommitted
[Feature #21028] ObjectSpace#find_path_to_unshareable_object
Co-authored-by: Yusuke Endoh <mame@ruby-lang.org>
1 parent d21e4e7 commit 01f7338

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

ext/objspace/lib/objspace.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,28 @@ def dump_shapes(output: :file, since: 0)
132132
return nil if output == :stdout
133133
ret
134134
end
135+
136+
# call-seq:
137+
# ObjectSpace.find_path_to_unshareable_object(obj) -> array | nil
138+
#
139+
# Find a path from obj to an unshareable object that prevents obj from being shareable.
140+
#
141+
# Returns an array showing the path from obj to the unshareable object, or nil if
142+
# obj is already shareable.
143+
def find_path_to_unshareable_object(obj, path = [], visited = {})
144+
path += [obj]
145+
return if visited[obj]
146+
visited[obj] = true
147+
148+
return if Ractor.shareable?(obj)
149+
150+
objs = ObjectSpace.reachable_objects_from(obj)
151+
return path if objs.all? { Ractor.shareable?(it) }
152+
153+
objs.each do |obj2|
154+
res = find_path_to_unshareable_object(obj2, path, visited)
155+
return res if res
156+
end
157+
return nil
158+
end
135159
end

test/objspace/test_ractor.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "test/unit"
2+
require "objspace"
23

34
class TestObjSpaceRactor < Test::Unit::TestCase
45
def test_tracing_does_not_crash
@@ -52,4 +53,50 @@ def fin
5253
ractors.each(&:join)
5354
RUBY
5455
end
56+
57+
def test_find_path_to_unshareable_object
58+
# Direct shareable object
59+
assert_nil(ObjectSpace.find_path_to_unshareable_object(1))
60+
assert_nil(ObjectSpace.find_path_to_unshareable_object(true))
61+
assert_nil(ObjectSpace.find_path_to_unshareable_object(:symbol))
62+
assert_nil(ObjectSpace.find_path_to_unshareable_object("frozen".freeze))
63+
64+
# Direct unshareable object
65+
obj = "unfrozen"
66+
path = ObjectSpace.find_path_to_unshareable_object(obj)
67+
assert_equal([obj], path)
68+
69+
# Hash containing unshareable object
70+
obj = { a: 1, b: "frozen".freeze, c: "unfrozen" }
71+
path = ObjectSpace.find_path_to_unshareable_object(obj)
72+
assert_equal([
73+
{ a: 1, b: "frozen".freeze, c: "unfrozen" },
74+
"unfrozen"
75+
], path)
76+
assert_equal(true, path.include?(obj[:c]))
77+
78+
# Array containing unshareable object
79+
obj = [1, 2, "unfrozen", "frozen".freeze]
80+
path = ObjectSpace.find_path_to_unshareable_object(obj)
81+
assert_not_nil(path)
82+
assert_equal(obj, path[0])
83+
assert_equal(true, path.include?(obj[2]))
84+
85+
# Custom class
86+
klass = Class.new do
87+
attr_accessor :value
88+
end
89+
obj = klass.new
90+
obj.value = "unfrozen"
91+
path = ObjectSpace.find_path_to_unshareable_object(obj)
92+
assert_equal(obj, path[0])
93+
assert_equal(true, path.include?(obj.value))
94+
95+
# Circular reference
96+
obj1 = { name: "obj1" }
97+
obj2 = { name: "obj2", ref: obj1 }
98+
obj1[:ref] = obj2
99+
path = ObjectSpace.find_path_to_unshareable_object(obj1)
100+
assert(path.any? { |o| o.is_a?(String) && !o.frozen? })
101+
end
55102
end

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy