diff options
Diffstat (limited to 'test/ruby/test_thread.rb')
| -rw-r--r-- | test/ruby/test_thread.rb | 178 |
1 files changed, 171 insertions, 7 deletions
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index ddb4516b3c..2a61fc3450 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -29,13 +29,19 @@ class TestThread < Test::Unit::TestCase end def test_inspect + m = Thread::Mutex.new + m.lock line = __LINE__+1 - th = Module.new {break module_eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")}.start{} + th = Module.new {break module_eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")}.start do + m.synchronize {} + end + Thread.pass until th.stop? s = th.inspect assert_include(s, "::C\u{30b9 30ec 30c3 30c9}:") assert_include(s, " #{__FILE__}:#{line} ") assert_equal(s, th.to_s) ensure + m.unlock th.join end @@ -237,6 +243,10 @@ class TestThread < Test::Unit::TestCase def test_join_argument_conversion t = Thread.new {} + + # Make sure that the thread terminates + Thread.pass while t.status + assert_raise(TypeError) {t.join(:foo)} limit = Struct.new(:to_f, :count).new(0.05) @@ -317,7 +327,6 @@ class TestThread < Test::Unit::TestCase s += 1 end Thread.pass until t.stop? - sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait assert_equal(1, s) t.wakeup Thread.pass while t.alive? @@ -389,7 +398,7 @@ class TestThread < Test::Unit::TestCase end INPUT - assert_in_out_err(%w(--disable-gems -d), <<-INPUT, %w(false 2), %r".+") + assert_in_out_err(%w(-d), <<-INPUT, %w(false 2), %r".+") p Thread.abort_on_exception begin t = Thread.new { raise } @@ -966,6 +975,8 @@ _eom end def test_thread_timer_and_interrupt + omit "[Bug #18613]" if /freebsd/ =~ RUBY_PLATFORM + bug5757 = '[ruby-dev:44985]' pid = nil cmd = 'Signal.trap(:INT, "DEFAULT"); pipe=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; pipe[0].read' @@ -1244,6 +1255,21 @@ q.pop assert_predicate(status, :success?, bug9751) end if Process.respond_to?(:fork) + def test_fork_value + bug18902 = "[Bug #18902]" + th = Thread.start { sleep 2 } + begin + pid = fork do + th.value + end + _, status = Process.wait2(pid) + assert_predicate(status, :success?, bug18902) + ensure + th.kill + th.join + end + end if Process.respond_to?(:fork) + def test_fork_while_locked m = Thread::Mutex.new thrs = [] @@ -1411,7 +1437,8 @@ q.pop Thread.pass until th1.stop? # After a thread starts (and execute `sleep`), it returns native_thread_id - assert_instance_of Integer, th1.native_thread_id + native_tid = th1.native_thread_id + assert_instance_of Integer, native_tid if native_tid # it can be nil th1.wakeup Thread.pass while th1.alive? @@ -1420,11 +1447,42 @@ q.pop assert_nil th1.native_thread_id end + def test_thread_native_thread_id_across_fork_on_linux + begin + require '-test-/thread/id' + rescue LoadError + omit "this test is only for Linux" + else + extend Bug::ThreadID + end + + parent_thread_id = Thread.main.native_thread_id + real_parent_thread_id = gettid + + assert_equal real_parent_thread_id, parent_thread_id + + child_lines = nil + IO.popen('-') do |pipe| + if pipe + # parent + child_lines = pipe.read.lines + else + # child + puts Thread.main.native_thread_id + puts gettid + end + end + child_thread_id = child_lines[0].chomp.to_i + real_child_thread_id = child_lines[1].chomp.to_i + + assert_equal real_child_thread_id, child_thread_id + refute_equal parent_thread_id, child_thread_id + end + def test_thread_interrupt_for_killed_thread - opts = { timeout: 5, timeout_error: nil } + pend "hang-up" if /mswin|mingw/ =~ RUBY_PLATFORM - # prevent SIGABRT from slow shutdown with MJIT - opts[:reprieve] = 3 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? + opts = { timeout: 5, timeout_error: nil } assert_normal_exit(<<-_end, '[Bug #8996]', **opts) Thread.report_on_exception = false @@ -1442,6 +1500,11 @@ q.pop omit "can't trap a signal from another process on Windows" # opt = {new_pgroup: true} end + + if /freebsd/ =~ RUBY_PLATFORM + omit "[Bug #18613]" + end + assert_separately([], "#{<<~"{#"}\n#{<<~'};'}", timeout: 120) {# n = 1000 @@ -1488,4 +1551,105 @@ q.pop end }; end + + def test_pending_interrupt? + t = Thread.handle_interrupt(Exception => :never) { Thread.new { Thread.stop } } + t.raise(StandardError) + assert_equal(true, t.pending_interrupt?) + assert_equal(true, t.pending_interrupt?(Exception)) + assert_equal(false, t.pending_interrupt?(ArgumentError)) + end + + def test_deadlock_backtrace + bug21127 = '[ruby-core:120930] [Bug #21127]' + + expected_stderr = [ + /-:12:in 'Thread#join': No live threads left. Deadlock\? \(fatal\)\n/, + /2 threads, 2 sleeps current:\w+ main thread:\w+\n/, + /\* #<Thread:\w+ sleep_forever>\n/, + :*, + /^\s*-:6:in 'Object#frame_for_deadlock_test_2'/, + :*, + /\* #<Thread:\w+ -:10 sleep_forever>\n/, + :*, + /^\s*-:2:in 'Object#frame_for_deadlock_test_1'/, + :*, + ] + + assert_in_out_err([], <<-INPUT, [], expected_stderr, bug21127) + def frame_for_deadlock_test_1 + yield + end + + def frame_for_deadlock_test_2 + yield + end + + q = Thread::Queue.new + t = Thread.new { frame_for_deadlock_test_1 { q.pop } } + + frame_for_deadlock_test_2 { t.join } + INPUT + end + + # [Bug #21342] + def test_unlock_locked_mutex_with_collected_fiber + bug21127 = '[ruby-core:120930] [Bug #21127]' + assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + 5.times do + m = Mutex.new + Thread.new do + m.synchronize do + end + end.join + Fiber.new do + GC.start + m.lock + end.resume + end + end; + end + + def test_unlock_locked_mutex_with_collected_fiber2 + assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + MUTEXES = [] + 5.times do + m = Mutex.new + Fiber.new do + GC.start + m.lock + end.resume + MUTEXES << m + end + 10.times do + MUTEXES.clear + GC.start + end + end; + end + + def test_mutexes_locked_in_fiber_dont_have_aba_issue_with_new_fibers + assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + mutexes = 1000.times.map do + Mutex.new + end + + mutexes.map do |m| + Fiber.new do + m.lock + end.resume + end + + GC.start + + 1000.times.map do + Fiber.new do + raise "FAILED!" if mutexes.any?(&:owned?) + end.resume + end + end; + end end |
