diff options
Diffstat (limited to 'test/ruby/test_ractor.rb')
| -rw-r--r-- | test/ruby/test_ractor.rb | 186 |
1 files changed, 160 insertions, 26 deletions
diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index e61c6beffc..6ae511217a 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -3,38 +3,19 @@ require 'test/unit' class TestRactor < Test::Unit::TestCase def test_shareability_of_iseq_proc - y = nil.instance_eval do + assert_raise Ractor::IsolationError do foo = [] - proc { foo } + Ractor.shareable_proc{ foo } end - assert_unshareable(y, /unshareable object \[\] from variable 'foo'/) - - y = [].instance_eval { proc { self } } - assert_unshareable(y, /Proc's self is not shareable/) - - y = [].freeze.instance_eval { proc { self } } - assert_make_shareable(y) - end - - def test_shareability_of_curried_proc - x = nil.instance_eval do - foo = [] - proc { foo }.curry - end - assert_unshareable(x, /unshareable object \[\] from variable 'foo'/) - - x = nil.instance_eval do - foo = 123 - proc { foo }.curry - end - assert_make_shareable(x) end def test_shareability_of_method_proc + # TODO: fix with Ractor.shareable_proc/lambda +=begin str = +"" x = str.instance_exec { proc { to_s } } - assert_unshareable(x, /Proc's self is not shareable/) + assert_unshareable(x, /Proc\'s self is not shareable/) x = str.instance_exec { method(:to_s) } assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error) @@ -58,6 +39,7 @@ class TestRactor < Test::Unit::TestCase x = str.instance_exec { method(:itself).to_proc } assert_unshareable(x, "can not make shareable object for #<Method: String(Kernel)#itself()>", exception: Ractor::Error) +=end end def test_shareability_error_uses_inspect @@ -65,7 +47,7 @@ class TestRactor < Test::Unit::TestCase def x.to_s raise "this should not be called" end - assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error) + assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()> because it refers unshareable objects", exception: Ractor::Error) end def test_default_thread_group @@ -74,11 +56,163 @@ class TestRactor < Test::Unit::TestCase Warning[:experimental] = false main_ractor_id = Thread.current.group.object_id - ractor_id = Ractor.new { Thread.current.group.object_id }.take + ractor_id = Ractor.new { Thread.current.group.object_id }.value refute_equal main_ractor_id, ractor_id end; end + def test_class_instance_variables + assert_ractor(<<~'RUBY') + # Once we're in multi-ractor mode, the codepaths + # for class instance variables are a bit different. + Ractor.new {}.value + + class TestClass + @a = 1 + @b = 2 + @c = 3 + @d = 4 + end + + assert_equal 4, TestClass.remove_instance_variable(:@d) + assert_nil TestClass.instance_variable_get(:@d) + assert_equal 4, TestClass.instance_variable_set(:@d, 4) + assert_equal 4, TestClass.instance_variable_get(:@d) + RUBY + end + + def test_struct_instance_variables + assert_ractor(<<~'RUBY') + StructIvar = Struct.new(:member) do + def initialize(*) + super + @ivar = "ivar" + end + attr_reader :ivar + end + obj = StructIvar.new("member") + obj_copy = Ractor.new { Ractor.receive }.send(obj).value + assert_equal obj.ivar, obj_copy.ivar + refute_same obj.ivar, obj_copy.ivar + assert_equal obj.member, obj_copy.member + refute_same obj.member, obj_copy.member + RUBY + end + + def test_move_nested_hash_during_gc_with_yjit + assert_ractor(<<~'RUBY', args: [{ "RUBY_YJIT_ENABLE" => "1" }]) + GC.stress = true + hash = { foo: { bar: "hello" }, baz: { qux: "there" } } + result = Ractor.new { Ractor.receive }.send(hash, move: true).value + assert_equal "hello", result[:foo][:bar] + assert_equal "there", result[:baz][:qux] + RUBY + end + + def test_fork_raise_isolation_error + assert_ractor(<<~'RUBY') + ractor = Ractor.new do + Process.fork + rescue Ractor::IsolationError => e + e + end + assert_equal Ractor::IsolationError, ractor.value.class + RUBY + end if Process.respond_to?(:fork) + + def test_require_raises_and_no_ractor_belonging_issue + assert_ractor(<<~'RUBY') + require "tempfile" + f = Tempfile.new(["file_to_require_from_ractor", ".rb"]) + f.write("raise 'uh oh'") + f.flush + err_msg = Ractor.new(f.path) do |path| + begin + require path + rescue RuntimeError => e + e.message # had confirm belonging issue here + else + nil + end + end.value + assert_equal "uh oh", err_msg + RUBY + end + + def test_require_non_string + assert_ractor(<<~'RUBY') + require "tempfile" + require "pathname" + f = Tempfile.new(["file_to_require_from_ractor", ".rb"]) + f.write("") + f.flush + result = Ractor.new(f.path) do |path| + require Pathname.new(path) + "success" + end.value + assert_equal "success", result + RUBY + end + + # [Bug #21398] + def test_port_receive_dnt_with_port_send + omit 'unstable on windows and macos-14' if RUBY_PLATFORM =~ /mswin|mingw|darwin/ + assert_ractor(<<~'RUBY', timeout: 90) + THREADS = 10 + JOBS_PER_THREAD = 50 + ARRAY_SIZE = 20_000 + def ractor_job(job_count, array_size) + port = Ractor::Port.new + workers = (1..4).map do |i| + Ractor.new(port) do |job_port| + while job = Ractor.receive + result = job.map { |x| x * 2 }.sum + job_port.send result + end + end + end + jobs = Array.new(job_count) { Array.new(array_size) { rand(1000) } } + jobs.each_with_index do |job, i| + w_idx = i % 4 + workers[w_idx].send(job) + end + results = [] + jobs.size.times do + result = port.receive # dnt receive + results << result + end + results + end + threads = [] + # creates 40 ractors (THREADSx4) + THREADS.times do + threads << Thread.new do + ractor_job(JOBS_PER_THREAD, ARRAY_SIZE) + end + end + threads.each(&:join) + RUBY + end + + # [Bug #20146] + def test_max_cpu_1 + assert_ractor(<<~'RUBY', args: [{ "RUBY_MAX_CPU" => "1" }]) + assert_equal :ok, Ractor.new { :ok }.value + RUBY + end + + def test_symbol_proc_is_shareable + pr = :symbol.to_proc + assert_make_shareable(pr) + end + + # [Bug #21775] + def test_ifunc_proc_not_shareable + h = Hash.new { self } + pr = h.to_proc + assert_unshareable(pr, /not supported yet/, exception: RuntimeError) + end + def assert_make_shareable(obj) refute Ractor.shareable?(obj), "object was already shareable" Ractor.make_shareable(obj) |
