diff options
Diffstat (limited to 'test/ruby/test_fiber.rb')
| -rw-r--r-- | test/ruby/test_fiber.rb | 350 |
1 files changed, 257 insertions, 93 deletions
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index 38050386c4..b7d2b71c19 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: false require 'test/unit' require 'fiber' -require 'continuation' -require_relative './envutil' +EnvUtil.suppress_warning {require 'continuation'} +require 'tmpdir' class TestFiber < Test::Unit::TestCase def test_normal @@ -33,20 +34,22 @@ class TestFiber < Test::Unit::TestCase end def test_many_fibers - max = 10000 + max = 1000 assert_equal(max, max.times{ Fiber.new{} }) + GC.start # force collect created fibers assert_equal(max, max.times{|i| Fiber.new{ }.resume } ) + GC.start # force collect created fibers end def test_many_fibers_with_threads - assert_normal_exit %q{ + assert_normal_exit <<-SRC, timeout: 60 max = 1000 @cnt = 0 (1..100).map{|ti| @@ -60,28 +63,32 @@ class TestFiber < Test::Unit::TestCase }.each{|t| t.join } - } + SRC end def test_error assert_raise(ArgumentError){ Fiber.new # Fiber without block } - assert_raise(FiberError){ - f = Fiber.new{} - Thread.new{f.resume}.join # Fiber yielding across thread - } + f = Fiber.new{} + Thread.new{ + assert_raise(FiberError){ # Fiber yielding across thread + f.resume + } + }.join assert_raise(FiberError){ f = Fiber.new{} f.resume f.resume } - assert_raise(RuntimeError){ - Fiber.new{ - @c = callcc{|c| @c = c} - }.resume - @c.call # cross fiber callcc - } + if respond_to?(:callcc) + assert_raise(RuntimeError){ + Fiber.new{ + @c = callcc{|c| @c = c} + }.resume + @c.call # cross fiber callcc + } + end assert_raise(RuntimeError){ Fiber.new{ raise @@ -104,6 +111,15 @@ class TestFiber < Test::Unit::TestCase } fib.resume } + assert_raise(FiberError){ + fib = Fiber.new{} + fib.raise "raise in unborn fiber" + } + assert_raise(FiberError){ + fib = Fiber.new{} + fib.resume + fib.raise "raise in dead fiber" + } end def test_return @@ -115,13 +131,55 @@ class TestFiber < Test::Unit::TestCase end def test_throw - assert_raise(ArgumentError){ + assert_raise(UncaughtThrowError){ Fiber.new do throw :a end.resume } end + def test_raise + assert_raise(ZeroDivisionError){ + Fiber.new do + 1/0 + end.resume + } + assert_raise(RuntimeError){ + fib = Fiber.new{ Fiber.yield } + fib.resume + fib.raise "raise and propagate" + } + assert_nothing_raised{ + fib = Fiber.new do + begin + Fiber.yield + rescue + end + end + fib.resume + fib.raise "rescue in fiber" + } + fib = Fiber.new do + begin + Fiber.yield + rescue + Fiber.yield :ok + end + end + fib.resume + assert_equal(:ok, fib.raise) + end + + def test_raise_transferring_fiber + root = Fiber.current + fib = Fiber.new { root.transfer } + fib.transfer + assert_raise(RuntimeError){ + fib.raise "can raise with transfer: true" + } + assert_not_predicate(fib, :alive?) + end + def test_transfer ary = [] f2 = nil @@ -137,6 +195,33 @@ class TestFiber < Test::Unit::TestCase assert_equal([:baz], ary) end + def test_terminate_transferred_fiber + log = [] + fa1 = fa2 = fb1 = r1 = nil + + fa1 = Fiber.new{ + fa2 = Fiber.new{ + log << :fa2_terminate + } + fa2.resume + log << :fa1_terminate + } + fb1 = Fiber.new{ + fa1.transfer + log << :fb1_terminate + } + + r1 = Fiber.new{ + fb1.transfer + log << :r1_terminate + } + + r1.resume + log << :root_terminate + + assert_equal [:fa2_terminate, :fa1_terminate, :r1_terminate, :root_terminate], log + end + def test_tls # def tvar(var, val) @@ -166,6 +251,18 @@ class TestFiber < Test::Unit::TestCase assert_equal(nil, Thread.current[:v]); end + def test_fiber_variables + assert_equal "bar", Fiber.new {Fiber[:foo] = "bar"; Fiber[:foo]}.resume + + key = :"#{self.class.name}#.#{self.object_id}" + Fiber[key] = 42 + assert_equal 42, Fiber[key] + + key = Object.new + def key.to_str; "foo"; end + assert_equal "Bar", Fiber.new {Fiber[key] = "Bar"; Fiber[key]}.resume + end + def test_alive fib = Fiber.new{Fiber.yield} assert_equal(true, fib.alive?) @@ -196,11 +293,11 @@ class TestFiber < Test::Unit::TestCase end def test_resume_root_fiber - assert_raise(FiberError) do - Thread.new do + Thread.new do + assert_raise(FiberError) do Fiber.current.resume - end.join - end + end + end.join end def test_gc_root_fiber @@ -214,52 +311,120 @@ class TestFiber < Test::Unit::TestCase }, bug4612 end + def test_mark_fiber + bug13875 = '[ruby-core:82681]' + + assert_normal_exit %q{ + GC.stress = true + up = 1.upto(10) + down = 10.downto(1) + up.zip(down) {|a, b| a + b == 11 or fail 'oops'} + }, bug13875 + end + def test_no_valid_cfp bug5083 = '[ruby-dev:44208]' - assert_equal([], Fiber.new(&Module.method(:nesting)).resume) - error = assert_raise(RuntimeError) do - Fiber.new(&Module.method(:undef_method)).resume(:to_s) - end - assert_equal("Can't call on top of Fiber or Thread", error.message, bug5083) + assert_equal([], Fiber.new(&Module.method(:nesting)).resume, bug5083) + assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083) end - def test_prohibit_resume_transfered_fiber + def test_prohibit_transfer_to_resuming_fiber + root_fiber = Fiber.current + assert_raise(FiberError){ - root_fiber = Fiber.current - f = Fiber.new{ - root_fiber.transfer - } - f.transfer - f.resume + fiber = Fiber.new{ root_fiber.transfer } + fiber.resume + } + + fa1 = Fiber.new{ + _fa2 = Fiber.new{ root_fiber.transfer } + } + fb1 = Fiber.new{ + _fb2 = Fiber.new{ root_fiber.transfer } + } + fa1.transfer + fb1.transfer + + assert_raise(FiberError){ + fa1.transfer } assert_raise(FiberError){ - g=nil - f=Fiber.new{ - g.resume - g.resume - } - g=Fiber.new{ - f.resume - f.resume + fb1.transfer + } + end + + def test_prohibit_transfer_to_yielding_fiber + f1 = f2 = f3 = nil + + f1 = Fiber.new{ + f2 = Fiber.new{ + f3 = Fiber.new{ + p f3: Fiber.yield + } + f3.resume } - f.transfer + f2.resume } + f1.resume + + assert_raise(FiberError){ f3.transfer 10 } end - def test_fork_from_fiber - begin - Process.fork{} - rescue NotImplementedError - return + def test_prohibit_resume_to_transferring_fiber + root_fiber = Fiber.current + + assert_raise(FiberError){ + Fiber.new{ + root_fiber.resume + }.transfer + } + + f1 = f2 = nil + f1 = Fiber.new do + f2.transfer end - bug5700 = '[ruby-core:41456]' + f2 = Fiber.new do + f1.resume # attempt to resume transferring fiber + end + + assert_raise(FiberError){ + f1.transfer + } + end + + + def test_fork_from_fiber + omit 'fork not supported' unless Process.respond_to?(:fork) pid = nil + bug5700 = '[ruby-core:41456]' assert_nothing_raised(bug5700) do - Fiber.new{ pid = fork {} }.resume + Fiber.new do + pid = fork do + xpid = nil + Fiber.new { + xpid = fork do + # enough to trigger GC on old root fiber + count = 1000 + count.times do + Fiber.new {}.transfer + Fiber.new { Fiber.yield } + end + exit!(true) + end + }.transfer + _, status = Process.waitpid2(xpid) + exit!(status.success?) + end + end.resume end pid, status = Process.waitpid2(pid) - assert_equal(0, status.exitstatus, bug5700) - assert_equal(false, status.signaled?, bug5700) + assert_not_predicate(status, :signaled?, bug5700) + assert_predicate(status, :success?, bug5700) + + pid = Fiber.new {fork}.resume + pid, status = Process.waitpid2(pid) + assert_not_predicate(status, :signaled?) + assert_predicate(status, :success?) end def test_exit_in_fiber @@ -270,54 +435,14 @@ class TestFiber < Test::Unit::TestCase end def test_fatal_in_fiber - assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/) + assert_in_out_err(["-r-test-/fatal", "-e", <<-EOS], "", [], /ok/) Fiber.new{ - rb_fatal "ok" + Bug.rb_fatal "ok" }.resume puts :ng # unreachable. EOS end - def invoke_rec script, vm_stack_size, machine_stack_size, use_length = true - env = {} - env['RUBY_FIBER_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size - env['RUBY_FIBER_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size - out, err = EnvUtil.invoke_ruby([env, '-e', script], '', true, true) - use_length ? out.length : out - end - - def test_stack_size - h_default = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', nil, nil, false)) - h_0 = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 0, 0, false)) - h_large = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 1024 * 1024 * 10, 1024 * 1024 * 10, false)) - - assert(h_default[:fiber_vm_stack_size] > h_0[:fiber_vm_stack_size]) - assert(h_default[:fiber_vm_stack_size] < h_large[:fiber_vm_stack_size]) - assert(h_default[:fiber_machine_stack_size] >= h_0[:fiber_machine_stack_size]) - assert(h_default[:fiber_machine_stack_size] <= h_large[:fiber_machine_stack_size]) - - # check VM machine stack size - script = '$stdout.sync=true; def rec; print "."; rec; end; Fiber.new{rec}.resume' - size_default = invoke_rec script, nil, nil - assert_operator(size_default, :>, 0) - size_0 = invoke_rec script, 0, nil - assert_operator(size_default, :>, size_0) - size_large = invoke_rec script, 1024 * 1024 * 10, nil - assert_operator(size_default, :<, size_large) - - return if /mswin|mingw/ =~ RUBY_PLATFORM - - # check machine stack size - # Note that machine stack size may not change size (depend on OSs) - script = '$stdout.sync=true; def rec; print "."; 1.times{1.times{1.times{rec}}}; end; Fiber.new{rec}.resume' - vm_stack_size = 1024 * 1024 - size_default = invoke_rec script, vm_stack_size, nil - size_0 = invoke_rec script, vm_stack_size, 0 - assert_operator(size_default, :>=, size_0) - size_large = invoke_rec script, vm_stack_size, 1024 * 1024 * 10 - assert_operator(size_default, :<=, size_large) - end - def test_separate_lastmatch bug7678 = '[ruby-core:51331]' /a/ =~ "a" @@ -341,5 +466,44 @@ class TestFiber < Test::Unit::TestCase assert_equal("inner", s2) assert_equal(s1, $_, bug7678) end -end + def test_new_symbol_proc + bug = '[ruby-core:80147] [Bug #13313]' + assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug) + begin; + exit("1" == Fiber.new(&:to_s).resume(1)) + end; + end + + def test_to_s + f = Fiber.new do + assert_match(/resumed/, f.to_s) + Fiber.yield + end + assert_match(/created/, f.to_s) + f.resume + assert_match(/suspended/, f.to_s) + f.resume + assert_match(/terminated/, f.to_s) + assert_match(/resumed/, Fiber.current.to_s) + end + + def test_create_fiber_in_new_thread + ret = Thread.new{ + Thread.new{ + Fiber.new{Fiber.yield :ok}.resume + }.value + }.value + assert_equal :ok, ret, '[Bug #14642]' + end + + def test_machine_stack_gc + assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 60 + enum = Enumerator.new { |y| y << 1 } + thread = Thread.new { enum.peek } + thread.join + sleep 5 # pause until thread cache wait time runs out. Native thread exits. + GC.start + RUBY + end +end |
