ractor-shim 0.1.0 → 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 +23 -2
- data/lib/ractor/shim.rb +118 -79
- 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 +20 -6
- data/test/test_ractor.rb +49 -13
- metadata +8 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba3ed0a7cfb270c23651d31c50286068defa1b0492459286cabc055da64ae2c8
|
|
4
|
+
data.tar.gz: 98ed41fb52ac1676960b4d6a49d49e66920c4465a779da9b1e5f12073427cee6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55647c412c0fdecac6feb0b7fcf353c86d2114979eb6aa9ee8c1c0521c3a05e35123ea9db82ba2d808f483bfe244add8b2140cad1267121d8f1cfdeb329250c9
|
|
7
|
+
data.tar.gz: 10dd59ab7425af30137bb2fa0edf416447cdd01a16aa6601d3df57a396b1833638e78197ca4e827169223ab23d9a94fe48f7ef6ff9f8e85522303a6cd8e9dc6a
|
data/README.md
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
# ractor-shim
|
|
2
2
|
|
|
3
|
-
A shim to define `Ractor` by using `Thread`, if `Ractor` is not already defined.
|
|
3
|
+
A shim to define `Ractor` by using `Thread`, `Queue`, etc if `Ractor` is not already defined.
|
|
4
4
|
|
|
5
|
-
This is notably useful to run programs
|
|
5
|
+
This is notably useful to run programs relying on `Ractor` on Ruby implementations which don't define `Ractor` such as TruffleRuby and JRuby.
|
|
6
|
+
|
|
7
|
+
Note that TruffleRuby and JRuby both run Ruby code in threads in parallel, so this gem enables using the Ractor API on these Rubies and run Ractors in parallel.
|
|
8
|
+
|
|
9
|
+
The gem also provides the Ruby 3.5 Ractor API (`Ractor::Port`, `Ractor#{join,value,monitor}`, etc) for CRuby 2.7 to 3.4.
|
|
10
|
+
|
|
11
|
+
When `Ractor` is not already defined, this gem implements `Ractor.make_shareable(object)` by just returning `object` to avoid unnecessary overhead.
|
|
12
|
+
The main reason is that the CRuby implementation of `Ractor.make_shareable` returns an immutable object, so it won't be mutated by the program anyway and there is no need to deep freeze.
|
|
13
|
+
Another reason is many gems do `Ractor.make_shareable(object) if defined?(Ractor)` and so that becomes dependent on whether `Ractor` is defined before that gem loads. That problem disappears if `Ractor.make_shareable(object)` is noop.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
$ gem install ractor-shim
|
|
19
|
+
```
|
|
20
|
+
or
|
|
21
|
+
```ruby
|
|
22
|
+
# In Gemfile
|
|
23
|
+
gem "ractor-shim"
|
|
24
|
+
```
|
|
6
25
|
|
|
7
26
|
## Usage
|
|
8
27
|
|
|
9
28
|
```ruby
|
|
10
29
|
require 'ractor/shim'
|
|
30
|
+
|
|
31
|
+
Ractor.new { ... }
|
|
11
32
|
```
|
|
12
33
|
|
|
13
34
|
## Development
|
data/lib/ractor/shim.rb
CHANGED
|
@@ -6,6 +6,83 @@ end
|
|
|
6
6
|
Ractor.define_singleton_method(:builtin?) { builtin_ractor }
|
|
7
7
|
Ractor.define_singleton_method(:shim?) { !builtin_ractor }
|
|
8
8
|
|
|
9
|
+
# Ractor::Port
|
|
10
|
+
|
|
11
|
+
if Ractor.builtin?
|
|
12
|
+
class Ractor::Port
|
|
13
|
+
QUIT = Object.new.freeze
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@pipe = Ractor.new do
|
|
17
|
+
while true
|
|
18
|
+
msg = Ractor.receive
|
|
19
|
+
break if QUIT.equal?(msg)
|
|
20
|
+
Ractor.yield msg
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send(message)
|
|
26
|
+
@pipe.send(message)
|
|
27
|
+
end
|
|
28
|
+
alias_method :<<, :send
|
|
29
|
+
|
|
30
|
+
def receive
|
|
31
|
+
@pipe.take
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def close
|
|
35
|
+
@pipe.send(QUIT)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def closed?
|
|
39
|
+
@pipe.inspect.end_with?(' terminated>')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inspect
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
end unless defined?(Ractor::Port)
|
|
46
|
+
else
|
|
47
|
+
class Ractor::Port
|
|
48
|
+
attr_reader :queue
|
|
49
|
+
private :queue
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@queue = Queue.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def send(message, move: false)
|
|
56
|
+
Ractor::SELECT_MUTEX.synchronize {
|
|
57
|
+
@queue << message
|
|
58
|
+
Ractor::SELECT_CV.broadcast
|
|
59
|
+
}
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
alias_method :<<, :send
|
|
63
|
+
|
|
64
|
+
def receive
|
|
65
|
+
@queue.pop
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def close
|
|
69
|
+
Ractor::SELECT_MUTEX.synchronize {
|
|
70
|
+
@queue.close
|
|
71
|
+
Ractor::SELECT_CV.broadcast
|
|
72
|
+
}
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def closed?
|
|
77
|
+
@queue.closed?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def inspect
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
9
86
|
if Ractor.shim?
|
|
10
87
|
class Ractor
|
|
11
88
|
class Error < RuntimeError
|
|
@@ -56,6 +133,14 @@ if Ractor.shim?
|
|
|
56
133
|
Ractor.current.__send__(:receive)
|
|
57
134
|
end
|
|
58
135
|
|
|
136
|
+
class << self
|
|
137
|
+
alias_method :recv, :receive
|
|
138
|
+
|
|
139
|
+
def new(...)
|
|
140
|
+
super(...)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
59
144
|
# def self.select_simple_spinning(*ractors)
|
|
60
145
|
# raise ArgumentError, "specify at least one ractor or `yield_value`" if ractors.empty?
|
|
61
146
|
# while true
|
|
@@ -85,9 +170,9 @@ if Ractor.shim?
|
|
|
85
170
|
while true
|
|
86
171
|
ractors.each do |ractor_or_port|
|
|
87
172
|
if Ractor === ractor_or_port
|
|
88
|
-
queue = ractor_or_port.out_queue
|
|
173
|
+
queue = ractor_or_port.__send__(:out_queue)
|
|
89
174
|
elsif Ractor::Port === ractor_or_port
|
|
90
|
-
queue = ractor_or_port.queue
|
|
175
|
+
queue = ractor_or_port.__send__(:queue)
|
|
91
176
|
else
|
|
92
177
|
raise ArgumentError, "Unexpected argument for Ractor.select: #{ractor_or_port}"
|
|
93
178
|
end
|
|
@@ -115,6 +200,14 @@ if Ractor.shim?
|
|
|
115
200
|
true
|
|
116
201
|
end
|
|
117
202
|
|
|
203
|
+
def self.[](var)
|
|
204
|
+
Ractor.current[var]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def self.[]=(var, value)
|
|
208
|
+
Ractor.current[var] = value
|
|
209
|
+
end
|
|
210
|
+
|
|
118
211
|
def self.store_if_absent(var, &block)
|
|
119
212
|
Ractor.current.__send__(:store_if_absent, var, &block)
|
|
120
213
|
end
|
|
@@ -123,17 +216,18 @@ if Ractor.shim?
|
|
|
123
216
|
Kernel.require(feature)
|
|
124
217
|
end
|
|
125
218
|
|
|
126
|
-
attr_reader :name
|
|
219
|
+
attr_reader :name, :default_port
|
|
127
220
|
|
|
128
|
-
attr_reader :
|
|
221
|
+
attr_reader :out_queue
|
|
222
|
+
private :out_queue
|
|
129
223
|
|
|
130
224
|
def initialize(*args, name: nil, &block)
|
|
131
225
|
raise ArgumentError, "must be called with a block" unless block
|
|
132
226
|
initialize_common(name, block)
|
|
227
|
+
CHANGE_COUNT.call(1)
|
|
133
228
|
|
|
134
229
|
@thread = Thread.new {
|
|
135
230
|
Thread.current.thread_variable_set(:current_ractor, self)
|
|
136
|
-
CHANGE_COUNT.call(1)
|
|
137
231
|
begin
|
|
138
232
|
result = self.instance_exec(*args, &block)
|
|
139
233
|
SELECT_MUTEX.synchronize {
|
|
@@ -171,7 +265,7 @@ if Ractor.shim?
|
|
|
171
265
|
@id = GET_ID.call
|
|
172
266
|
@status = :running
|
|
173
267
|
@from = block ? block.source_location.join(":") : nil
|
|
174
|
-
@
|
|
268
|
+
@default_port = Ractor::Port.new
|
|
175
269
|
@out_queue = Queue.new
|
|
176
270
|
@storage = {}
|
|
177
271
|
@exception = nil
|
|
@@ -199,27 +293,27 @@ if Ractor.shim?
|
|
|
199
293
|
end
|
|
200
294
|
end
|
|
201
295
|
|
|
202
|
-
def send(message,
|
|
203
|
-
raise Ractor::ClosedError, "The port was already closed" if @status == :terminated || @
|
|
204
|
-
@
|
|
296
|
+
def send(message, ...)
|
|
297
|
+
raise Ractor::ClosedError, "The port was already closed" if @status == :terminated || @default_port.closed?
|
|
298
|
+
@default_port.send(message, ...)
|
|
205
299
|
self
|
|
206
300
|
end
|
|
207
301
|
alias_method :<<, :send
|
|
208
302
|
|
|
209
303
|
private def receive
|
|
210
|
-
@
|
|
304
|
+
@default_port.receive
|
|
211
305
|
end
|
|
212
306
|
|
|
213
307
|
# def take
|
|
214
308
|
# @out_queue.pop
|
|
215
309
|
# end
|
|
216
310
|
|
|
217
|
-
def close_incoming
|
|
218
|
-
@
|
|
311
|
+
private def close_incoming
|
|
312
|
+
@default_port.close
|
|
219
313
|
self
|
|
220
314
|
end
|
|
221
315
|
|
|
222
|
-
def close_outgoing
|
|
316
|
+
private def close_outgoing
|
|
223
317
|
SELECT_MUTEX.synchronize {
|
|
224
318
|
@out_queue.close
|
|
225
319
|
SELECT_CV.broadcast
|
|
@@ -227,6 +321,11 @@ if Ractor.shim?
|
|
|
227
321
|
self
|
|
228
322
|
end
|
|
229
323
|
|
|
324
|
+
def close
|
|
325
|
+
close_incoming
|
|
326
|
+
close_outgoing
|
|
327
|
+
end
|
|
328
|
+
|
|
230
329
|
def monitor(port)
|
|
231
330
|
@termination_mutex.synchronize {
|
|
232
331
|
if @status == :terminated
|
|
@@ -263,6 +362,7 @@ if Ractor.shim?
|
|
|
263
362
|
def inspect
|
|
264
363
|
["#<Ractor:##{@id}", @name, @from, "#{@status}>"].compact.join(' ')
|
|
265
364
|
end
|
|
365
|
+
alias_method :to_s, :inspect
|
|
266
366
|
|
|
267
367
|
MAIN_RACTOR = Ractor.allocate
|
|
268
368
|
MAIN_RACTOR.__send__(:initialize_main)
|
|
@@ -285,15 +385,12 @@ if Ractor.builtin?
|
|
|
285
385
|
self == Ractor.main
|
|
286
386
|
end
|
|
287
387
|
end
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
388
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
close_outgoing
|
|
389
|
+
unless method_defined?(:close)
|
|
390
|
+
def close
|
|
391
|
+
close_incoming
|
|
392
|
+
close_outgoing
|
|
393
|
+
end
|
|
297
394
|
end
|
|
298
395
|
end
|
|
299
396
|
end
|
|
@@ -325,61 +422,3 @@ class << Ractor
|
|
|
325
422
|
end
|
|
326
423
|
end
|
|
327
424
|
end
|
|
328
|
-
|
|
329
|
-
# Ractor::Port
|
|
330
|
-
|
|
331
|
-
if Ractor.builtin?
|
|
332
|
-
class Ractor::Port
|
|
333
|
-
QUIT = Object.new.freeze
|
|
334
|
-
|
|
335
|
-
def initialize
|
|
336
|
-
@pipe = Ractor.new do
|
|
337
|
-
while true
|
|
338
|
-
msg = Ractor.receive
|
|
339
|
-
break if QUIT.equal?(msg)
|
|
340
|
-
Ractor.yield msg
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
def <<(message)
|
|
346
|
-
@pipe.send(message)
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
def receive
|
|
350
|
-
@pipe.take
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
def close
|
|
354
|
-
@pipe.send(QUIT)
|
|
355
|
-
end
|
|
356
|
-
end unless defined?(Ractor::Port)
|
|
357
|
-
else
|
|
358
|
-
class Ractor::Port
|
|
359
|
-
attr_reader :queue
|
|
360
|
-
|
|
361
|
-
def initialize
|
|
362
|
-
@queue = Queue.new
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def <<(message)
|
|
366
|
-
Ractor::SELECT_MUTEX.synchronize {
|
|
367
|
-
@queue << message
|
|
368
|
-
Ractor::SELECT_CV.broadcast
|
|
369
|
-
}
|
|
370
|
-
self
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
def receive
|
|
374
|
-
@queue.pop
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def close
|
|
378
|
-
Ractor::SELECT_MUTEX.synchronize {
|
|
379
|
-
@queue.close
|
|
380
|
-
Ractor::SELECT_CV.broadcast
|
|
381
|
-
}
|
|
382
|
-
self
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
end
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Based on https://github.com/truffleruby/truffleruby/blob/master/spec/truffle/methods_spec.rb
|
|
2
|
+
|
|
3
|
+
# How to regenerate files:
|
|
4
|
+
#
|
|
5
|
+
# - switch to MRI, the version we are compatible with
|
|
6
|
+
# - run `jt -u ruby test spec/truffle/methods_spec.rb`
|
|
7
|
+
|
|
8
|
+
# jt test and jt tag can be used as normal,
|
|
9
|
+
# but instead of jt untag, jt purge must be used to remove tags:
|
|
10
|
+
# $ jt purge spec/truffle/methods_spec.rb
|
|
11
|
+
|
|
12
|
+
# socket modules are found with:
|
|
13
|
+
# m1=ObjectSpace.each_object(Module).to_a; require "socket"; m2=ObjectSpace.each_object(Module).to_a; p m2-m1
|
|
14
|
+
|
|
15
|
+
modules = %w[
|
|
16
|
+
Ractor Ractor.singleton_class
|
|
17
|
+
Ractor::Port Ractor::Port.singleton_class
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
def ruby(code, *flags)
|
|
21
|
+
IO.popen([RbConfig.ruby, *flags], "r+") { |pipe|
|
|
22
|
+
pipe.write code
|
|
23
|
+
pipe.close_write
|
|
24
|
+
pipe.read
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.5"
|
|
29
|
+
modules.each do |mod|
|
|
30
|
+
file = "#{__dir__}/methods/#{mod}.txt"
|
|
31
|
+
code = "puts #{mod}.public_instance_methods(false).sort"
|
|
32
|
+
methods = ruby(code)
|
|
33
|
+
methods = methods.lines.map { |line| line.chomp.to_sym }
|
|
34
|
+
contents = methods.map { |meth| "#{meth}\n" }.join
|
|
35
|
+
File.write file, contents
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
code = <<-RUBY
|
|
40
|
+
require "ractor/shim"
|
|
41
|
+
#{modules.inspect}.each { |m|
|
|
42
|
+
puts m
|
|
43
|
+
puts eval(m).public_instance_methods(false).sort
|
|
44
|
+
puts
|
|
45
|
+
}
|
|
46
|
+
RUBY
|
|
47
|
+
all_methods = {}
|
|
48
|
+
ruby(code, "-I#{File.dirname(__dir__)}/lib").rstrip.split("\n\n").each do |group|
|
|
49
|
+
mod, *methods = group.lines.map(&:chomp)
|
|
50
|
+
all_methods[mod] = methods.map(&:to_sym)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
modules.each do |mod|
|
|
54
|
+
file = "#{__dir__}/methods/#{mod}.txt"
|
|
55
|
+
expected = File.readlines(file).map { |line| line.chomp.to_sym }
|
|
56
|
+
methods = all_methods[mod]
|
|
57
|
+
|
|
58
|
+
if methods != expected
|
|
59
|
+
extras = methods - expected
|
|
60
|
+
missing = expected - methods
|
|
61
|
+
|
|
62
|
+
if mod == "Ractor.singleton_class"
|
|
63
|
+
extras -= %i[builtin? shim?] # intended extras
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if RUBY_ENGINE == "ruby" and RUBY_VERSION < "3.5"
|
|
67
|
+
if mod == "Ractor"
|
|
68
|
+
extras -= %i[take close_incoming close_outgoing] # expected extra on those versions
|
|
69
|
+
missing -= %i[default_port monitor unmonitor] # hard to implement on those versions
|
|
70
|
+
elsif mod == "Ractor.singleton_class"
|
|
71
|
+
extras -= %i[yield receive_if] # expected extra on those versions
|
|
72
|
+
if RUBY_VERSION < "3.4"
|
|
73
|
+
missing -= %i[[] []= store_if_absent _require] # hard to implement on those versions
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
raise "#{mod} methods should not include #{extras}" unless extras.empty?
|
|
79
|
+
raise "#{mod} methods should include #{missing}" unless missing.empty?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
puts "#{__FILE__}: OK"
|
data/test/run_tests.rb
CHANGED
|
@@ -10,7 +10,6 @@ BUILTIN_SKIPS = [
|
|
|
10
10
|
"SystemExit from a Thread inside a Ractor is re-raised", # Ractor::ClosedError on 3.4 instead of Ractor::RemoteError
|
|
11
11
|
"Access to global-variables are prohibited (read)", # different error message on 3.4
|
|
12
12
|
"Access to global-variables are prohibited (write)", # different error message on 3.4
|
|
13
|
-
"Ractor.make_shareable(a_proc) is not supported now.", # no error on 3.4
|
|
14
13
|
"Ractor-local storage", # more permissive on 3.4
|
|
15
14
|
"Now NoMethodError is copyable", # fails on 3.4
|
|
16
15
|
"bind_call in Ractor [Bug #20934]", # SEGV on 3.4
|
|
@@ -31,6 +30,7 @@ BUILTIN_SKIPS = [
|
|
|
31
30
|
"monitor returns false if the monitoring Ractor was terminated.", # Ractor#monitor
|
|
32
31
|
"monitor port returns `:aborted` when the monitering Ractor is aborted.", # Ractor#monitor
|
|
33
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
34
|
]
|
|
35
35
|
|
|
36
36
|
SHIM_SKIPS = [
|
|
@@ -61,7 +61,6 @@ SHIM_SKIPS = [
|
|
|
61
61
|
"ObjectSpace._id2ref can not handle unshareable objects with Ractors", # can't check
|
|
62
62
|
"Ractor.make_shareable(obj)", # expected due to no freeze
|
|
63
63
|
"Ractor.make_shareable(obj) doesn't freeze shareable objects", # expected due to no freeze
|
|
64
|
-
"Ractor.make_shareable(a_proc) is not supported now.", # can't check
|
|
65
64
|
"Ractor.shareable?(recursive_objects)", # expected, due to all are shareable
|
|
66
65
|
"Ractor.make_shareable(recursive_objects)", # expected, due to all are shareable
|
|
67
66
|
"Ractor.make_shareable(obj, copy: true) makes copied shareable object.", # expected, due to all are shareable
|
|
@@ -73,9 +72,11 @@ SHIM_SKIPS = [
|
|
|
73
72
|
"moved hashes can't be used", # expected due to no move
|
|
74
73
|
"moved composite types move their non-shareable parts properly", # expected due to no move
|
|
75
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
76
|
]
|
|
77
77
|
|
|
78
78
|
GLOBAL_SKIPS = [
|
|
79
|
+
"Ractor.make_shareable(a_proc) requires a shareable receiver", # because this harness uses Object as outer self
|
|
79
80
|
"threads in a ractor will killed", # leaks Ractors
|
|
80
81
|
"TracePoint with normal Proc should be Ractor local", # line numbers differ due to test harness
|
|
81
82
|
"Can yield back values while GC is sweeping [Bug #18117]", # too slow and leaks
|
|
@@ -90,6 +91,15 @@ GLOBAL_SKIPS = [
|
|
|
90
91
|
skips = GLOBAL_SKIPS
|
|
91
92
|
skips += BUILTIN_SKIPS if Ractor.builtin? && RUBY_VERSION < "3.5"
|
|
92
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
|
|
93
103
|
if Ractor.builtin? && RUBY_VERSION < "3.4"
|
|
94
104
|
# fails on 3.3
|
|
95
105
|
skips += [
|
|
@@ -97,7 +107,7 @@ if Ractor.builtin? && RUBY_VERSION < "3.4"
|
|
|
97
107
|
"ivar in shareable-objects are not allowed to access from non-main Ractor",
|
|
98
108
|
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)",
|
|
99
109
|
"ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)",
|
|
100
|
-
|
|
110
|
+
"defined? on ivar of module",
|
|
101
111
|
"moved objects have their shape properly set to original object's shape",
|
|
102
112
|
"Ractor-local storage with Thread inheritance of current Ractor",
|
|
103
113
|
"require in Ractor",
|
|
@@ -112,6 +122,7 @@ if Ractor.builtin? && RUBY_VERSION < "3.3"
|
|
|
112
122
|
# fails on 3.2
|
|
113
123
|
skips += [
|
|
114
124
|
"check moved object", # SEGV
|
|
125
|
+
"fstring pool 2", # spurious, probably a fstring table bug
|
|
115
126
|
]
|
|
116
127
|
end
|
|
117
128
|
if Ractor.builtin? && RUBY_VERSION < "3.1"
|
|
@@ -125,14 +136,11 @@ end
|
|
|
125
136
|
if Ractor.shim? && RUBY_ENGINE == "ruby" && RUBY_VERSION < "3.0"
|
|
126
137
|
# fails on 2.7
|
|
127
138
|
skips += [
|
|
128
|
-
"Ractor.count",
|
|
129
|
-
"fstring pool 2", # spurious, probably a fstring table bug
|
|
130
139
|
"check method cache invalidation", # syntax
|
|
131
140
|
]
|
|
132
141
|
end
|
|
133
142
|
if Ractor.shim? && RUBY_ENGINE == "jruby"
|
|
134
143
|
skips += [
|
|
135
|
-
"Ractor.count",
|
|
136
144
|
"ObjectSpace.each_object can not handle unshareable objects with Ractors",
|
|
137
145
|
"fstring pool 2", # spurious, probably a fstring table bug
|
|
138
146
|
]
|
|
@@ -204,6 +212,10 @@ def generic_assert(expected, code, check, &error_message)
|
|
|
204
212
|
|
|
205
213
|
# cleanup global state
|
|
206
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
|
|
207
219
|
end
|
|
208
220
|
|
|
209
221
|
def assert_equal(expected, code, frozen_string_literal: nil)
|
|
@@ -233,3 +245,5 @@ end
|
|
|
233
245
|
Warning[:experimental] = false
|
|
234
246
|
|
|
235
247
|
require_relative 'test_ractor'
|
|
248
|
+
|
|
249
|
+
require_relative 'ractor_methods_test'
|
data/test/test_ractor.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# From https://github.com/ruby/ruby/blob/
|
|
1
|
+
# From https://github.com/ruby/ruby/blob/390d77ba00f9e8f18a5408b404365db0b25fbf37/bootstraptest/test_ractor.rb
|
|
2
2
|
|
|
3
3
|
# Ractor.current returns a current ractor
|
|
4
4
|
assert_equal 'Ractor', %q{
|
|
@@ -255,7 +255,7 @@ assert_equal 30.times.map { 'ok' }.to_s, %q{
|
|
|
255
255
|
30.times.map{|i|
|
|
256
256
|
test i
|
|
257
257
|
}
|
|
258
|
-
} unless (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') # https://bugs.ruby-lang.org/issues/17878
|
|
258
|
+
} # unless (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') # https://bugs.ruby-lang.org/issues/17878
|
|
259
259
|
|
|
260
260
|
# Exception for empty select
|
|
261
261
|
assert_match /specify at least one ractor/, %q{
|
|
@@ -468,6 +468,7 @@ assert_equal "ok", <<~'RUBY', frozen_string_literal: false
|
|
|
468
468
|
echo_ractor = Ractor.new port do |port|
|
|
469
469
|
loop do
|
|
470
470
|
v = Ractor.receive
|
|
471
|
+
break if v == :stop
|
|
471
472
|
port << v
|
|
472
473
|
end
|
|
473
474
|
end
|
|
@@ -527,7 +528,7 @@ assert_equal "ok", <<~'RUBY', frozen_string_literal: false
|
|
|
527
528
|
}
|
|
528
529
|
|
|
529
530
|
if results.empty?
|
|
530
|
-
port.close; echo_ractor.
|
|
531
|
+
port.close; echo_ractor << :stop; echo_ractor.join; :ok
|
|
531
532
|
else
|
|
532
533
|
results.inspect
|
|
533
534
|
end
|
|
@@ -756,6 +757,37 @@ assert_equal 'ArgumentError', %q{
|
|
|
756
757
|
end
|
|
757
758
|
}
|
|
758
759
|
|
|
760
|
+
# eval with outer locals in a Ractor raises SyntaxError
|
|
761
|
+
# [Bug #21522]
|
|
762
|
+
assert_equal 'SyntaxError', %q{
|
|
763
|
+
outer = 42
|
|
764
|
+
r = Ractor.new do
|
|
765
|
+
eval("outer")
|
|
766
|
+
end
|
|
767
|
+
begin
|
|
768
|
+
r.value
|
|
769
|
+
rescue Ractor::RemoteError => e
|
|
770
|
+
e.cause.class
|
|
771
|
+
end
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
# eval of an undefined name in a Ractor raises NameError
|
|
775
|
+
assert_equal 'NameError', %q{
|
|
776
|
+
r = Ractor.new do
|
|
777
|
+
eval("totally_undefined_name")
|
|
778
|
+
end
|
|
779
|
+
begin
|
|
780
|
+
r.value
|
|
781
|
+
rescue Ractor::RemoteError => e
|
|
782
|
+
e.cause.class
|
|
783
|
+
end
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
# eval of a local defined inside the Ractor works
|
|
787
|
+
assert_equal '99', %q{
|
|
788
|
+
Ractor.new { inner = 99; eval("inner").to_s }.value
|
|
789
|
+
}
|
|
790
|
+
|
|
759
791
|
# ivar in shareable-objects are not allowed to access from non-main Ractor
|
|
760
792
|
assert_equal "can not get unshareable values from instance variables of classes/modules from non-main Ractors", <<~'RUBY', frozen_string_literal: false
|
|
761
793
|
class C
|
|
@@ -897,6 +929,7 @@ assert_equal '333', %q{
|
|
|
897
929
|
a + b + c + d + e + f
|
|
898
930
|
}
|
|
899
931
|
|
|
932
|
+
# defined? on ivar of module
|
|
900
933
|
assert_equal '["instance-variable", "instance-variable", nil]', %q{
|
|
901
934
|
class C
|
|
902
935
|
@iv1 = ""
|
|
@@ -1158,16 +1191,19 @@ assert_equal 'true', %q{
|
|
|
1158
1191
|
[a.frozen?, a[0].frozen?] == [true, false]
|
|
1159
1192
|
}
|
|
1160
1193
|
|
|
1161
|
-
# Ractor.make_shareable(a_proc)
|
|
1162
|
-
assert_equal '
|
|
1163
|
-
|
|
1194
|
+
# Ractor.make_shareable(a_proc) requires a shareable receiver
|
|
1195
|
+
assert_equal '[:ok, "Proc\'s self is not shareable:"]', %q{
|
|
1196
|
+
pr1 = nil.instance_exec { Proc.new{} }
|
|
1197
|
+
pr2 = Proc.new{}
|
|
1164
1198
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1199
|
+
[pr1, pr2].map do |pr|
|
|
1200
|
+
begin
|
|
1201
|
+
Ractor.make_shareable(pr)
|
|
1202
|
+
rescue Ractor::Error => e
|
|
1203
|
+
e.message[/^.+?:/]
|
|
1204
|
+
else
|
|
1205
|
+
:ok
|
|
1206
|
+
end
|
|
1171
1207
|
end
|
|
1172
1208
|
}
|
|
1173
1209
|
|
|
@@ -1265,7 +1301,7 @@ assert_equal '[:ok, :ok]', %q{
|
|
|
1265
1301
|
s = 'str'
|
|
1266
1302
|
trap(:INT){p s}
|
|
1267
1303
|
}.join
|
|
1268
|
-
rescue
|
|
1304
|
+
rescue Ractor::RemoteError
|
|
1269
1305
|
a << :ok
|
|
1270
1306
|
end
|
|
1271
1307
|
}
|
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ractor-shim
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Benoit Daloze
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email:
|
|
15
13
|
- eregontp@gmail.com
|
|
16
14
|
executables: []
|
|
@@ -21,6 +19,11 @@ files:
|
|
|
21
19
|
- README.md
|
|
22
20
|
- Rakefile
|
|
23
21
|
- lib/ractor/shim.rb
|
|
22
|
+
- test/methods/Ractor.singleton_class.txt
|
|
23
|
+
- test/methods/Ractor.txt
|
|
24
|
+
- test/methods/Ractor::Port.singleton_class.txt
|
|
25
|
+
- test/methods/Ractor::Port.txt
|
|
26
|
+
- test/ractor_methods_test.rb
|
|
24
27
|
- test/run_tests.rb
|
|
25
28
|
- test/test_ractor.rb
|
|
26
29
|
homepage: https://github.com/eregon/ractor-shim
|
|
@@ -29,7 +32,6 @@ licenses:
|
|
|
29
32
|
metadata:
|
|
30
33
|
homepage_uri: https://github.com/eregon/ractor-shim
|
|
31
34
|
source_code_uri: https://github.com/eregon/ractor-shim
|
|
32
|
-
post_install_message:
|
|
33
35
|
rdoc_options: []
|
|
34
36
|
require_paths:
|
|
35
37
|
- lib
|
|
@@ -44,8 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
46
|
- !ruby/object:Gem::Version
|
|
45
47
|
version: '0'
|
|
46
48
|
requirements: []
|
|
47
|
-
rubygems_version: 3.
|
|
48
|
-
signing_key:
|
|
49
|
+
rubygems_version: 3.6.9
|
|
49
50
|
specification_version: 4
|
|
50
51
|
summary: A shim to define Ractor by using Thread, if not already defined.
|
|
51
52
|
test_files: []
|