Skip to content

Commit 55b1ba3

Browse files
committed
Ractor.shareable_proc
call-seq: Ractor.sharable_proc(self: nil){} -> sharable proc It returns shareable Proc object. The Proc object is shareable and the self in a block will be replaced with the value passed via `self:` keyword. In a shareable Proc, the outer variables should * (1) refer shareable objects * (2) be not be overwritten ```ruby a = 42 Ractor.shareable_proc{ p a } #=> OK b = 43 Ractor.shareable_proc{ p b; b = 44 } #=> Ractor::IsolationError because 'b' is reassigned in the block. c = 44 Ractor.shareable_proc{ p c } #=> Ractor::IsolationError because 'c' will be reassigned outside of the block. c = 45 d = 45 d = 46 if cond Ractor.shareable_proc{ p d } #=> Ractor::IsolationError because 'd' was reassigned outside of the block. ``` The last `d`'s case can be relaxed in a future version. The above check will be done in a static analysis at compile time, so the reflection feature such as `Binding#local_varaible_set` can not be detected. ```ruby e = 42 shpr = Ractor.shareable_proc{ p e } #=> OK binding.local_variable_set(:e, 43) shpr.call #=> 42 (returns captured timing value) ``` Ractor.sharaeble_lambda is also introduced. [Feature #21550] [Feature #21557]
1 parent c05ea92 commit 55b1ba3

File tree

12 files changed

+254
-173
lines changed

12 files changed

+254
-173
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ Note: We're only listing outstanding class updates.
117117
118118
* `Ractor#close_incoming` and `Ractor#close_outgoing` were removed.
119119
120+
* `Ractor.sharealbe_proc` and `Ractor.shareable_lambda` is introduced
121+
to make shareable Proc or lambda.
122+
[[Feature #21550]], [[Feature #21557]]
123+
120124
* `Set`
121125
122126
* `Set` is now a core class, instead of an autoloaded stdlib class.
@@ -316,3 +320,5 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
316320
[Feature #21347]: https://bugs.ruby-lang.org/issues/21347
317321
[Feature #21360]: https://bugs.ruby-lang.org/issues/21360
318322
[Feature #21527]: https://bugs.ruby-lang.org/issues/21527
323+
[Feature #21550]: https://bugs.ruby-lang.org/issues/21550
324+
[Feature #21557]: https://bugs.ruby-lang.org/issues/21557

bootstraptest/test_ractor.rb

Lines changed: 44 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,47 @@
145145
}.map(&:value)
146146
}
147147

148+
assert_equal "42", %q{
149+
a = 42
150+
Ractor.shareable_lambda{ a }.call
151+
}
152+
148153
# Ractor.make_shareable issue for locals in proc [Bug #18023]
149154
assert_equal '[:a, :b, :c, :d, :e]', %q{
150155
v1, v2, v3, v4, v5 = :a, :b, :c, :d, :e
151-
closure = Ractor.current.instance_eval{ Proc.new { [v1, v2, v3, v4, v5] } }
152-
153-
Ractor.make_shareable(closure).call
156+
closure = Proc.new { [v1, v2, v3, v4, v5] }
157+
Ractor.shareable_proc(&closure).call
154158
}
155159

156-
# Ractor.make_shareable issue for locals in proc [Bug #18023]
157-
assert_equal '[:a, :b, :c, :d, :e, :f, :g]', %q{
158-
a = :a
159-
closure = Ractor.current.instance_eval do
160-
-> {
161-
b, c, d = :b, :c, :d
162-
-> {
163-
e, f, g = :e, :f, :g
164-
-> { [a, b, c, d, e, f, g] }
165-
}.call
166-
}.call
160+
# Ractor::IsolationError cases
161+
assert_equal '3', %q{
162+
ok = 0
163+
164+
begin
165+
a = 1
166+
Ractor.shareable_proc{a}
167+
a = 2
168+
rescue Ractor::IsolationError => e
169+
ok += 1
167170
end
168171
169-
Ractor.make_shareable(closure).call
172+
begin
173+
cond = false
174+
a = 1
175+
a = 2 if cond
176+
Ractor.shareable_proc{a}
177+
rescue Ractor::IsolationError => e
178+
ok += 1
179+
end
180+
181+
begin
182+
1.times{|i|
183+
i = 2
184+
Ractor.shareable_proc{i}
185+
}
186+
rescue Ractor::IsolationError => e
187+
ok += 1
188+
end
170189
}
171190

172191
###
@@ -967,7 +986,7 @@ class C
967986
end
968987
RUBY
969988

970-
# Constant cache should care about non-sharable constants
989+
# Constant cache should care about non-shareable constants
971990
assert_equal "can not access non-shareable objects in constant Object::STR by non-main Ractor.", <<~'RUBY', frozen_string_literal: false
972991
STR = "hello"
973992
def str; STR; end
@@ -1137,41 +1156,17 @@ def /(other)
11371156
[a.frozen?, a[0].frozen?] == [true, false]
11381157
}
11391158

1140-
# Ractor.make_shareable(a_proc) makes a proc shareable.
1159+
# Ractor.make_shareable(a_proc) is not supported now.
11411160
assert_equal 'true', %q{
1142-
a = [1, [2, 3], {a: "4"}]
1143-
1144-
pr = Ractor.current.instance_eval do
1145-
Proc.new do
1146-
a
1147-
end
1148-
end
1161+
pr = Proc.new{}
11491162
1150-
Ractor.make_shareable(a) # referred value should be shareable
1151-
Ractor.make_shareable(pr)
1152-
Ractor.shareable?(pr)
1153-
}
1154-
1155-
# Ractor.make_shareable(a_proc) makes inner structure shareable and freezes it
1156-
assert_equal 'true,true,true,true', %q{
1157-
class Proc
1158-
attr_reader :obj
1159-
def initialize
1160-
@obj = Object.new
1161-
end
1162-
end
1163-
1164-
pr = Ractor.current.instance_eval do
1165-
Proc.new {}
1163+
begin
1164+
Ractor.make_shareable(pr)
1165+
rescue Ractor::Error
1166+
true
1167+
else
1168+
false
11661169
end
1167-
1168-
results = []
1169-
Ractor.make_shareable(pr)
1170-
results << Ractor.shareable?(pr)
1171-
results << pr.frozen?
1172-
results << Ractor.shareable?(pr.obj)
1173-
results << pr.obj.frozen?
1174-
results.map(&:to_s).join(',')
11751170
}
11761171

11771172
# Ractor.shareable?(recursive_objects)
@@ -1202,50 +1197,16 @@ module M; end
12021197
Ractor.make_shareable(ary = [C, M])
12031198
}
12041199

1205-
# Ractor.make_shareable with curried proc checks isolation of original proc
1206-
assert_equal 'isolation error', %q{
1207-
a = Object.new
1208-
orig = proc { a }
1209-
curried = orig.curry
1210-
1211-
begin
1212-
Ractor.make_shareable(curried)
1213-
rescue Ractor::IsolationError
1214-
'isolation error'
1215-
else
1216-
'no error'
1217-
end
1218-
}
1219-
12201200
# define_method() can invoke different Ractor's proc if the proc is shareable.
12211201
assert_equal '1', %q{
12221202
class C
12231203
a = 1
1224-
define_method "foo", Ractor.make_shareable(Proc.new{ a })
1225-
a = 2
1204+
define_method "foo", Ractor.shareable_proc{ a }
12261205
end
12271206
12281207
Ractor.new{ C.new.foo }.value
12291208
}
12301209

1231-
# Ractor.make_shareable(a_proc) makes a proc shareable.
1232-
assert_equal 'can not make a Proc shareable because it accesses outer variables (a).', %q{
1233-
a = b = nil
1234-
pr = Ractor.current.instance_eval do
1235-
Proc.new do
1236-
c = b # assign to a is okay because c is block local variable
1237-
# reading b is okay
1238-
a = b # assign to a is not allowed #=> Ractor::Error
1239-
end
1240-
end
1241-
1242-
begin
1243-
Ractor.make_shareable(pr)
1244-
rescue => e
1245-
e.message
1246-
end
1247-
}
1248-
12491210
# Ractor.make_shareable(obj, copy: true) makes copied shareable object.
12501211
assert_equal '[false, false, true, true]', %q{
12511212
r = []
@@ -1471,42 +1432,6 @@ class C
14711432
"ok"
14721433
} if !yjit_enabled? && ENV['GITHUB_WORKFLOW'] != 'ModGC' # flaky
14731434

1474-
assert_equal "ok", %q{
1475-
def foo(*); ->{ super }; end
1476-
begin
1477-
Ractor.make_shareable(foo)
1478-
rescue Ractor::IsolationError
1479-
"ok"
1480-
end
1481-
}
1482-
1483-
assert_equal "ok", %q{
1484-
def foo(**); ->{ super }; end
1485-
begin
1486-
Ractor.make_shareable(foo)
1487-
rescue Ractor::IsolationError
1488-
"ok"
1489-
end
1490-
}
1491-
1492-
assert_equal "ok", %q{
1493-
def foo(...); ->{ super }; end
1494-
begin
1495-
Ractor.make_shareable(foo)
1496-
rescue Ractor::IsolationError
1497-
"ok"
1498-
end
1499-
}
1500-
1501-
assert_equal "ok", %q{
1502-
def foo((x), (y)); ->{ super }; end
1503-
begin
1504-
Ractor.make_shareable(foo([], []))
1505-
rescue Ractor::IsolationError
1506-
"ok"
1507-
end
1508-
}
1509-
15101435
# check method cache invalidation
15111436
assert_equal "ok", %q{
15121437
module M

0 commit comments

Comments
 (0)