mirror of
https://github.com/ruby/ruby.git
synced 2026-08-24 09:54:21 +08:00
218 lines
6.5 KiB
Ruby
218 lines
6.5 KiB
Ruby
# frozen_string_literal: false
|
|
require 'test/unit'
|
|
|
|
class TestRactor < Test::Unit::TestCase
|
|
def test_shareability_of_iseq_proc
|
|
assert_raise Ractor::IsolationError do
|
|
foo = []
|
|
Ractor.shareable_proc{ foo }
|
|
end
|
|
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/)
|
|
|
|
x = str.instance_exec { method(:to_s) }
|
|
assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error)
|
|
|
|
x = str.instance_exec { method(:to_s).to_proc }
|
|
assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error)
|
|
|
|
x = str.instance_exec { method(:itself).to_proc }
|
|
assert_unshareable(x, "can not make shareable object for #<Method: String(Kernel)#itself()>", exception: Ractor::Error)
|
|
|
|
str.freeze
|
|
|
|
x = str.instance_exec { proc { to_s } }
|
|
assert_make_shareable(x)
|
|
|
|
x = str.instance_exec { method(:to_s) }
|
|
assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error)
|
|
|
|
x = str.instance_exec { method(:to_s).to_proc }
|
|
assert_unshareable(x, "can not make shareable object for #<Method: String#to_s()>", exception: Ractor::Error)
|
|
|
|
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
|
|
x = (+"").instance_exec { method(:to_s) }
|
|
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)
|
|
end
|
|
|
|
def test_default_thread_group
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
Warning[:experimental] = false
|
|
|
|
main_ractor_id = Thread.current.group.object_id
|
|
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 assert_make_shareable(obj)
|
|
refute Ractor.shareable?(obj), "object was already shareable"
|
|
Ractor.make_shareable(obj)
|
|
assert Ractor.shareable?(obj), "object didn't become shareable"
|
|
end
|
|
|
|
def assert_unshareable(obj, msg=nil, exception: Ractor::IsolationError)
|
|
refute Ractor.shareable?(obj), "object is already shareable"
|
|
assert_raise_with_message(exception, msg) do
|
|
Ractor.make_shareable(obj)
|
|
end
|
|
refute Ractor.shareable?(obj), "despite raising, object became shareable"
|
|
end
|
|
end
|