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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bb91e5a7525967249c155d6193ebacf3b261d6db8c93c06e904aabc11810c79
4
- data.tar.gz: 81882745a9a7393a06a1916c19b5324272ff5001a9883c61ea4d7d2fadb032c2
3
+ metadata.gz: ba3ed0a7cfb270c23651d31c50286068defa1b0492459286cabc055da64ae2c8
4
+ data.tar.gz: 98ed41fb52ac1676960b4d6a49d49e66920c4465a779da9b1e5f12073427cee6
5
5
  SHA512:
6
- metadata.gz: 80866fae0411eb23c5e1900430763c7d1ce552f0704c911e9c65a7a7b518959f3ee85d2ce0836f4ea33291c37e0061ffa8266086ad2f78d1fc5509991729690a
7
- data.tar.gz: 42fae31b67236b12e74f84b4d4942e34fe7a4f73fa9280fa971733e30c16edef9a60a8a29312aca99fcac9374050a328d86c0148121c1fffe0b9a5dfd0249477
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 needing `Ractor` on Ruby implementations which don't define `Ractor.
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 :in_queue, :out_queue
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
- @in_queue = Queue.new
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, move: false)
203
- raise Ractor::ClosedError, "The port was already closed" if @status == :terminated || @in_queue.closed?
204
- @in_queue << message
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
- @in_queue.pop
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
- @in_queue.close
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
- # common
292
- class Ractor
293
- unless method_defined?(:close)
294
- def close
295
- close_incoming
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
@@ -0,0 +1,16 @@
1
+ []
2
+ []=
3
+ _require
4
+ count
5
+ current
6
+ main
7
+ main?
8
+ make_shareable
9
+ new
10
+ receive
11
+ recv
12
+ select
13
+ shareable?
14
+ shareable_lambda
15
+ shareable_proc
16
+ store_if_absent
@@ -0,0 +1,13 @@
1
+ <<
2
+ []
3
+ []=
4
+ close
5
+ default_port
6
+ inspect
7
+ join
8
+ monitor
9
+ name
10
+ send
11
+ to_s
12
+ unmonitor
13
+ value
File without changes
@@ -0,0 +1,6 @@
1
+ <<
2
+ close
3
+ closed?
4
+ inspect
5
+ receive
6
+ send
@@ -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
- 900,
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/master/bootstraptest/test_ractor.rb
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.close; :ok
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) is not supported now.
1162
- assert_equal 'true', %q{
1163
- pr = Proc.new{}
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
- begin
1166
- Ractor.make_shareable(pr)
1167
- rescue Ractor::Error
1168
- true
1169
- else
1170
- false
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 => Ractor::RemoteError
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.0
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: 2025-10-29 00:00:00.000000000 Z
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.5.22
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: []