summaryrefslogtreecommitdiff
path: root/test/ruby/test_fiber.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_fiber.rb')
-rw-r--r--test/ruby/test_fiber.rb350
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