ractor-shim 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +24 -3
- data/Rakefile +5 -1
- data/lib/ractor/shim.rb +422 -1
- data/test/methods/Ractor.singleton_class.txt +16 -0
- data/test/methods/Ractor.txt +13 -0
- data/test/methods/Ractor::Port.singleton_class.txt +0 -0
- data/test/methods/Ractor::Port.txt +6 -0
- data/test/ractor_methods_test.rb +83 -0
- data/test/run_tests.rb +249 -0
- data/test/test_ractor.rb +2345 -0
- metadata +8 -2
- data/lib/ractor/shim/version.rb +0 -7
data/test/run_tests.rb
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ractor/shim'
|
|
4
|
+
|
|
5
|
+
BUILTIN_SKIPS = [
|
|
6
|
+
"send shareable and unshareable objects", # leaks Ractors on 3.3
|
|
7
|
+
"Ractor::IsolationError cases", # Ractor.make_shareable too permissive on 3.4
|
|
8
|
+
"Ractor.select from two ractors.", # Ractor.select with ports doesn't work on 3.4
|
|
9
|
+
"SystemExit from a Ractor is re-raised", # Ractor::ClosedError on 3.4 instead of Ractor::RemoteError
|
|
10
|
+
"SystemExit from a Thread inside a Ractor is re-raised", # Ractor::ClosedError on 3.4 instead of Ractor::RemoteError
|
|
11
|
+
"Access to global-variables are prohibited (read)", # different error message on 3.4
|
|
12
|
+
"Access to global-variables are prohibited (write)", # different error message on 3.4
|
|
13
|
+
"Ractor-local storage", # more permissive on 3.4
|
|
14
|
+
"Now NoMethodError is copyable", # fails on 3.4
|
|
15
|
+
"bind_call in Ractor [Bug #20934]", # SEGV on 3.4
|
|
16
|
+
"moved objects being corrupted if embeded (Hash)", # broken on 3.4
|
|
17
|
+
"moved objects being corrupted if embeded (MatchData)", # SEGV on 3.4
|
|
18
|
+
"moved objects being corrupted if embeded (Struct)", # broken on 3.4
|
|
19
|
+
"moved objects being corrupted if embeded (Object)", # broken on 3.4
|
|
20
|
+
"moved arrays can't be used", # broken on 3.4
|
|
21
|
+
"moved strings can't be used", # broken on 3.4
|
|
22
|
+
"moved hashes can't be used", # broken on 3.4
|
|
23
|
+
"move objects inside frozen containers", # broken on 3.4
|
|
24
|
+
"moved composite types move their non-shareable parts properly", # broken on 3.4
|
|
25
|
+
"Creating classes inside of Ractors", # OOM on 3.4
|
|
26
|
+
"Ractor#join raises RemoteError when the remote Ractor aborted with an exception", # needs join semantics for exceptions
|
|
27
|
+
"Only one Ractor can call Ractor#value", # need value semantics
|
|
28
|
+
"monitor port returns `:exited` when the monitering Ractor terminated.", # Ractor#monitor
|
|
29
|
+
"monitor port returns `:exited` even if the monitoring Ractor was terminated.", # Ractor#monitor
|
|
30
|
+
"monitor returns false if the monitoring Ractor was terminated.", # Ractor#monitor
|
|
31
|
+
"monitor port returns `:aborted` when the monitering Ractor is aborted.", # Ractor#monitor
|
|
32
|
+
"monitor port returns `:aborted` even if the monitoring Ractor was aborted.", # Ractor#monitor
|
|
33
|
+
"eval with outer locals in a Ractor raises SyntaxError", # can't check
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
SHIM_SKIPS = [
|
|
37
|
+
"Ractor.allocate is not supported",
|
|
38
|
+
"Ractor::IsolationError cases",
|
|
39
|
+
"$DEBUG, $VERBOSE are Ractor local", # mutates global variables like $DEBUG and $VERBOSE
|
|
40
|
+
"SystemExit from a Thread inside a Ractor is re-raised", # internal error on TruffleRuby
|
|
41
|
+
"unshareable object are copied", # expected due to no copy
|
|
42
|
+
"To copy the object, now Marshal#dump is used", # expected due to no copy
|
|
43
|
+
"send shareable and unshareable objects", # expected due to no copy
|
|
44
|
+
"frozen Objects are shareable", # expected due to no copy
|
|
45
|
+
"touching moved object causes an error", # expected due to no move
|
|
46
|
+
"move example2: Array", # expected due to no move
|
|
47
|
+
"Access to global-variables are prohibited (read)", # can't check
|
|
48
|
+
"Access to global-variables are prohibited (write)", # can't check
|
|
49
|
+
"$stdin,out,err is Ractor local, but shared fds", # can't do
|
|
50
|
+
"given block Proc will be isolated, so can not access outer variables.", # can't check
|
|
51
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor", # not checked
|
|
52
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)", # not checked
|
|
53
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)", # not checked
|
|
54
|
+
"and instance variables of classes/modules are accessible if they refer shareable objects", # not checked
|
|
55
|
+
"cvar in shareable-objects are not allowed to access from non-main Ractor", # can't check
|
|
56
|
+
"also cached cvar in shareable-objects are not allowed to access from non-main Ractor", # can't check
|
|
57
|
+
"Getting non-shareable objects via constants by other Ractors is not allowed", # can't check
|
|
58
|
+
"Constant cache should care about non-shareable constants", # can't check
|
|
59
|
+
"Setting non-shareable objects into constants by other Ractors is not allowed", # can't check
|
|
60
|
+
"define_method is not allowed", # can't check
|
|
61
|
+
"ObjectSpace._id2ref can not handle unshareable objects with Ractors", # can't check
|
|
62
|
+
"Ractor.make_shareable(obj)", # expected due to no freeze
|
|
63
|
+
"Ractor.make_shareable(obj) doesn't freeze shareable objects", # expected due to no freeze
|
|
64
|
+
"Ractor.shareable?(recursive_objects)", # expected, due to all are shareable
|
|
65
|
+
"Ractor.make_shareable(recursive_objects)", # expected, due to all are shareable
|
|
66
|
+
"Ractor.make_shareable(obj, copy: true) makes copied shareable object.", # expected, due to all are shareable
|
|
67
|
+
"Can not trap with not isolated Proc on non-main ractor", # can't check
|
|
68
|
+
"Ractor-local storage with Thread inheritance of current Ractor", # hard
|
|
69
|
+
"Chilled strings are not shareable", # expected, due to all are shareable
|
|
70
|
+
"moved arrays can't be used", # expected due to no move
|
|
71
|
+
"moved strings can't be used", # expected due to no move
|
|
72
|
+
"moved hashes can't be used", # expected due to no move
|
|
73
|
+
"moved composite types move their non-shareable parts properly", # expected due to no move
|
|
74
|
+
"Only one Ractor can call Ractor#value", # could be implemented
|
|
75
|
+
"eval with outer locals in a Ractor raises SyntaxError", # can't check
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
GLOBAL_SKIPS = [
|
|
79
|
+
"Ractor.make_shareable(a_proc) requires a shareable receiver", # because this harness uses Object as outer self
|
|
80
|
+
"threads in a ractor will killed", # leaks Ractors
|
|
81
|
+
"TracePoint with normal Proc should be Ractor local", # line numbers differ due to test harness
|
|
82
|
+
"Can yield back values while GC is sweeping [Bug #18117]", # too slow and leaks
|
|
83
|
+
"check experimental warning", # harness disables experimental warnings
|
|
84
|
+
"failed in autolaod in Ractor", # need more isolation
|
|
85
|
+
"fork after creating Ractor", # fork, leaks
|
|
86
|
+
"Ractors should be terminated after fork", # fork, hangs
|
|
87
|
+
"st_table, which can call malloc.", # leaks
|
|
88
|
+
"Creating classes inside of Ractors", # leaks
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
skips = GLOBAL_SKIPS
|
|
92
|
+
skips += BUILTIN_SKIPS if Ractor.builtin? && RUBY_VERSION < "3.5"
|
|
93
|
+
skips += SHIM_SKIPS if Ractor.shim?
|
|
94
|
+
if Ractor.builtin? && RUBY_VERSION >= "3.5"
|
|
95
|
+
# fails on 3.5+
|
|
96
|
+
skips += [
|
|
97
|
+
"move object with generic ivar", # SEGV
|
|
98
|
+
"move object with many generic ivars", # SEGV
|
|
99
|
+
"move object with complex generic ivars", # SEGV
|
|
100
|
+
"moved composite types move their non-shareable parts properly", # SEGV
|
|
101
|
+
]
|
|
102
|
+
end
|
|
103
|
+
if Ractor.builtin? && RUBY_VERSION < "3.4"
|
|
104
|
+
# fails on 3.3
|
|
105
|
+
skips += [
|
|
106
|
+
"unshareable frozen objects should still be frozen in new ractor after move",
|
|
107
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor",
|
|
108
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)",
|
|
109
|
+
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)",
|
|
110
|
+
"defined? on ivar of module",
|
|
111
|
+
"moved objects have their shape properly set to original object's shape",
|
|
112
|
+
"Ractor-local storage with Thread inheritance of current Ractor",
|
|
113
|
+
"require in Ractor",
|
|
114
|
+
"autolaod in Ractor",
|
|
115
|
+
"moved objects being corrupted if embeded (String)",
|
|
116
|
+
"move object with generic ivar",
|
|
117
|
+
"move object with many generic ivars", # SEGV
|
|
118
|
+
"move object with complex generic ivars",
|
|
119
|
+
]
|
|
120
|
+
end
|
|
121
|
+
if Ractor.builtin? && RUBY_VERSION < "3.3"
|
|
122
|
+
# fails on 3.2
|
|
123
|
+
skips += [
|
|
124
|
+
"check moved object", # SEGV
|
|
125
|
+
"fstring pool 2", # spurious, probably a fstring table bug
|
|
126
|
+
]
|
|
127
|
+
end
|
|
128
|
+
if Ractor.builtin? && RUBY_VERSION < "3.1"
|
|
129
|
+
# fails on 3.0
|
|
130
|
+
skips += [
|
|
131
|
+
"and instance variables of classes/modules are accessible if they refer shareable objects",
|
|
132
|
+
"define_method is not allowed",
|
|
133
|
+
"check method cache invalidation", # SEGV
|
|
134
|
+
]
|
|
135
|
+
end
|
|
136
|
+
if Ractor.shim? && RUBY_ENGINE == "ruby" && RUBY_VERSION < "3.0"
|
|
137
|
+
# fails on 2.7
|
|
138
|
+
skips += [
|
|
139
|
+
"check method cache invalidation", # syntax
|
|
140
|
+
]
|
|
141
|
+
end
|
|
142
|
+
if Ractor.shim? && RUBY_ENGINE == "jruby"
|
|
143
|
+
skips += [
|
|
144
|
+
"ObjectSpace.each_object can not handle unshareable objects with Ractors",
|
|
145
|
+
"fstring pool 2", # spurious, probably a fstring table bug
|
|
146
|
+
]
|
|
147
|
+
end
|
|
148
|
+
if $DEBUG
|
|
149
|
+
# fails probably Due to Thread.abort_on_exception being true with $DEBUG true
|
|
150
|
+
skips += [
|
|
151
|
+
"an exception in a Ractor non-main thread will not be re-raised at Ractor#receive",
|
|
152
|
+
]
|
|
153
|
+
end
|
|
154
|
+
SKIPS = skips
|
|
155
|
+
|
|
156
|
+
def new_empty_binding
|
|
157
|
+
# we need a shareable self for shareable procs/lambdas, but also preserve the default definee of Object
|
|
158
|
+
Object.class_eval { binding }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def skip(reason = nil)
|
|
162
|
+
throw :skip, :skip
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
TEST_LINES = [""] + File.readlines("#{__dir__}/test_ractor.rb").map(&:chomp)
|
|
166
|
+
|
|
167
|
+
def generic_assert(expected, code, check, &error_message)
|
|
168
|
+
test_info = caller_locations(2, 1)[0]
|
|
169
|
+
line = test_info.lineno
|
|
170
|
+
test_name = TEST_LINES[line-1]
|
|
171
|
+
test_name = TEST_LINES[line-2] if test_name.start_with?("# [Bug")
|
|
172
|
+
warn "Could not find test name for test at line #{line}" unless test_name.start_with?("# ")
|
|
173
|
+
test_name = test_name[2..-1]
|
|
174
|
+
|
|
175
|
+
print "Running test from line #{line}: "
|
|
176
|
+
if SKIPS.include?(line) or SKIPS.include?(test_name)
|
|
177
|
+
puts "SKIP"
|
|
178
|
+
return
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
actual = catch(:skip) do
|
|
183
|
+
eval(code, new_empty_binding, test_info.path, line).to_s
|
|
184
|
+
end
|
|
185
|
+
return if :skip == actual
|
|
186
|
+
rescue => e
|
|
187
|
+
puts "ERROR"
|
|
188
|
+
e.set_backtrace(e.backtrace.reject { |b|
|
|
189
|
+
b.include?(__FILE__)
|
|
190
|
+
}.map { |b|
|
|
191
|
+
b.sub(/(Object#)?new_empty_binding/, 'test')
|
|
192
|
+
})
|
|
193
|
+
raise e
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
if check.call(actual, expected)
|
|
197
|
+
puts "PASS #{actual}"
|
|
198
|
+
else
|
|
199
|
+
puts "FAIL"
|
|
200
|
+
raise RuntimeError, error_message.call(actual, expected), caller(2)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
wait_ms = 10
|
|
204
|
+
waited = 0
|
|
205
|
+
while (leaked = Ractor.count - 1) > 0 and waited < wait_ms
|
|
206
|
+
sleep 0.001
|
|
207
|
+
waited += 1
|
|
208
|
+
end
|
|
209
|
+
unless leaked == 0
|
|
210
|
+
raise "Test at line #{line} leaked #{leaked} Ractors"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# cleanup global state
|
|
214
|
+
Object.send(:remove_const, :A) if Object.const_defined?(:A)
|
|
215
|
+
|
|
216
|
+
unless Ractor::RemoteError.is_a? Class
|
|
217
|
+
raise "Ractor::RemoteError is no longer a Class!: #{Ractor::RemoteError.inspect}"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def assert_equal(expected, code, frozen_string_literal: nil)
|
|
222
|
+
generic_assert(expected, code, -> a, e { e == a }) { |actual, expected|
|
|
223
|
+
"Expected #{expected.inspect} but got #{actual.inspect}"
|
|
224
|
+
}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def assert_match(expected, code, frozen_string_literal: nil)
|
|
228
|
+
generic_assert(expected, code, -> a, e { e.match?(a) }) { |actual, expected|
|
|
229
|
+
"Expected #{expected} =~ #{actual.inspect} but it was false"
|
|
230
|
+
}
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def assert_normal_exit(code)
|
|
234
|
+
# Skip because such tests need to run in their own process
|
|
235
|
+
return
|
|
236
|
+
# generic_assert(:unused, code, -> a, e { true }) { |actual, expected|
|
|
237
|
+
# raise "unreachable"
|
|
238
|
+
# }
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def yjit_enabled?
|
|
242
|
+
false
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
Warning[:experimental] = false
|
|
246
|
+
|
|
247
|
+
require_relative 'test_ractor'
|
|
248
|
+
|
|
249
|
+
require_relative 'ractor_methods_test'
|