@@ -1361,6 +1361,28 @@ def /(other)
13611361 Ractor.shareable?(pr)
13621362}
13631363
1364+ # Ractor.make_shareable(a_proc) makes inner structure shareable and freezes it
1365+ assert_equal 'true,true,true,true' , %q{
1366+ class Proc
1367+ attr_reader :obj
1368+ def initialize
1369+ @obj = Object.new
1370+ end
1371+ end
1372+
1373+ pr = Ractor.current.instance_eval do
1374+ Proc.new {}
1375+ end
1376+
1377+ results = []
1378+ Ractor.make_shareable(pr)
1379+ results << Ractor.shareable?(pr)
1380+ results << pr.frozen?
1381+ results << Ractor.shareable?(pr.obj)
1382+ results << pr.obj.frozen?
1383+ results.map(&:to_s).join(',')
1384+ }
1385+
13641386# Ractor.shareable?(recursive_objects)
13651387assert_equal '[false, false]' , %q{
13661388 y = []
@@ -1389,6 +1411,21 @@ module M; end
13891411 Ractor.make_shareable(ary = [C, M])
13901412}
13911413
1414+ # Ractor.make_shareable with curried proc checks isolation of original proc
1415+ assert_equal 'isolation error' , %q{
1416+ a = Object.new
1417+ orig = proc { a }
1418+ curried = orig.curry
1419+
1420+ begin
1421+ Ractor.make_shareable(curried)
1422+ rescue Ractor::IsolationError
1423+ 'isolation error'
1424+ else
1425+ 'no error'
1426+ end
1427+ }
1428+
13921429# define_method() can invoke different Ractor's proc if the proc is shareable.
13931430assert_equal '1' , %q{
13941431 class C
@@ -1599,7 +1636,7 @@ class C
15991636
16001637 1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
16011638 "ok"
1602- } unless yjit_enabled? # flaky
1639+ } if ! yjit_enabled? && ENV [ 'GITHUB_WORKFLOW' ] != 'ModGC' # flaky
16031640
16041641assert_equal "ok" , %q{
16051642 def foo(*); ->{ super }; end
@@ -1950,3 +1987,138 @@ def require feature
19501987 GC.start
19511988 :ok.itself
19521989}
1990+
1991+ # moved objects being corrupted if embeded (String)
1992+ assert_equal 'ok' , %q{
1993+ ractor = Ractor.new { Ractor.receive }
1994+ obj = "foobarbazfoobarbazfoobarbazfoobarbaz"
1995+ ractor.send(obj.dup, move: true)
1996+ roundtripped_obj = ractor.take
1997+ roundtripped_obj == obj ? :ok : roundtripped_obj
1998+ }
1999+
2000+ # moved objects being corrupted if embeded (Array)
2001+ assert_equal 'ok' , %q{
2002+ ractor = Ractor.new { Ractor.receive }
2003+ obj = Array.new(10, 42)
2004+ ractor.send(obj.dup, move: true)
2005+ roundtripped_obj = ractor.take
2006+ roundtripped_obj == obj ? :ok : roundtripped_obj
2007+ }
2008+
2009+ # moved objects being corrupted if embeded (Hash)
2010+ assert_equal 'ok' , %q{
2011+ ractor = Ractor.new { Ractor.receive }
2012+ obj = { foo: 1, bar: 2 }
2013+ ractor.send(obj.dup, move: true)
2014+ roundtripped_obj = ractor.take
2015+ roundtripped_obj == obj ? :ok : roundtripped_obj
2016+ }
2017+
2018+ # moved objects being corrupted if embeded (MatchData)
2019+ assert_equal 'ok' , %q{
2020+ ractor = Ractor.new { Ractor.receive }
2021+ obj = "foo".match(/o/)
2022+ ractor.send(obj.dup, move: true)
2023+ roundtripped_obj = ractor.take
2024+ roundtripped_obj == obj ? :ok : roundtripped_obj
2025+ }
2026+
2027+ # moved objects being corrupted if embeded (Struct)
2028+ assert_equal 'ok' , %q{
2029+ ractor = Ractor.new { Ractor.receive }
2030+ obj = Struct.new(:a, :b, :c, :d, :e, :f).new(1, 2, 3, 4, 5, 6)
2031+ ractor.send(obj.dup, move: true)
2032+ roundtripped_obj = ractor.take
2033+ roundtripped_obj == obj ? :ok : roundtripped_obj
2034+ }
2035+
2036+ # moved objects being corrupted if embeded (Object)
2037+ assert_equal 'ok' , %q{
2038+ ractor = Ractor.new { Ractor.receive }
2039+ class SomeObject
2040+ attr_reader :a, :b, :c, :d, :e, :f
2041+ def initialize
2042+ @a = @b = @c = @d = @e = @f = 1
2043+ end
2044+
2045+ def ==(o)
2046+ @a == o.a &&
2047+ @b == o.b &&
2048+ @c == o.c &&
2049+ @d == o.d &&
2050+ @e == o.e &&
2051+ @f == o.f
2052+ end
2053+ end
2054+
2055+ SomeObject.new # initial non-embeded
2056+
2057+ obj = SomeObject.new
2058+ ractor.send(obj.dup, move: true)
2059+ roundtripped_obj = ractor.take
2060+ roundtripped_obj == obj ? :ok : roundtripped_obj
2061+ }
2062+
2063+ # moved arrays can't be used
2064+ assert_equal 'ok' , %q{
2065+ ractor = Ractor.new { Ractor.receive }
2066+ obj = [1]
2067+ ractor.send(obj, move: true)
2068+ begin
2069+ [].concat(obj)
2070+ rescue TypeError
2071+ :ok
2072+ else
2073+ :fail
2074+ end
2075+ }
2076+
2077+ # moved strings can't be used
2078+ assert_equal 'ok' , %q{
2079+ ractor = Ractor.new { Ractor.receive }
2080+ obj = "hello"
2081+ ractor.send(obj, move: true)
2082+ begin
2083+ "".replace(obj)
2084+ rescue TypeError
2085+ :ok
2086+ else
2087+ :fail
2088+ end
2089+ }
2090+
2091+ # moved hashes can't be used
2092+ assert_equal 'ok' , %q{
2093+ ractor = Ractor.new { Ractor.receive }
2094+ obj = { a: 1 }
2095+ ractor.send(obj, move: true)
2096+ begin
2097+ {}.merge(obj)
2098+ rescue TypeError
2099+ :ok
2100+ else
2101+ :fail
2102+ end
2103+ }
2104+
2105+ # move objects inside frozen containers
2106+ assert_equal 'ok' , %q{
2107+ ractor = Ractor.new { Ractor.receive }
2108+ obj = Array.new(10, 42)
2109+ original = obj.dup
2110+ ractor.send([obj].freeze, move: true)
2111+ roundtripped_obj = ractor.take[0]
2112+ roundtripped_obj == original ? :ok : roundtripped_obj
2113+ }
2114+
2115+ # move object with generic ivar
2116+ assert_equal 'ok' , %q{
2117+ ractor = Ractor.new { Ractor.receive }
2118+ obj = Array.new(10, 42)
2119+ obj.instance_variable_set(:@array, [1])
2120+
2121+ ractor.send(obj, move: true)
2122+ roundtripped_obj = ractor.take
2123+ roundtripped_obj.instance_variable_get(:@array) == [1] ? :ok : roundtripped_obj
2124+ }
0 commit comments