diff options
Diffstat (limited to 'spec/ruby/core/thread/shared/exit.rb')
-rw-r--r-- | spec/ruby/core/thread/shared/exit.rb | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/spec/ruby/core/thread/shared/exit.rb b/spec/ruby/core/thread/shared/exit.rb new file mode 100644 index 0000000000..f15da360fd --- /dev/null +++ b/spec/ruby/core/thread/shared/exit.rb @@ -0,0 +1,176 @@ +describe :thread_exit, shared: true do + before :each do + ScratchPad.clear + end + + it "kills sleeping thread" do + sleeping_thread = Thread.new do + sleep + ScratchPad.record :after_sleep + end + Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" + sleeping_thread.send(@method) + sleeping_thread.join + ScratchPad.recorded.should == nil + end + + it "kills current thread" do + thread = Thread.new do + Thread.current.send(@method) + ScratchPad.record :after_sleep + end + thread.join + ScratchPad.recorded.should == nil + end + + it "runs ensure clause" do + thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause } + thread.join + ScratchPad.recorded.should == :in_ensure_clause + end + + it "runs nested ensure clauses" do + ScratchPad.record [] + @outer = Thread.new do + begin + @inner = Thread.new do + begin + sleep + ensure + ScratchPad << :inner_ensure_clause + end + end + sleep + ensure + ScratchPad << :outer_ensure_clause + @inner.send(@method) + @inner.join + end + end + Thread.pass while @outer.status and @outer.status != "sleep" + Thread.pass until @inner + Thread.pass while @inner.status and @inner.status != "sleep" + @outer.send(@method) + @outer.join + ScratchPad.recorded.should include(:inner_ensure_clause) + ScratchPad.recorded.should include(:outer_ensure_clause) + end + + it "does not set $!" do + thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! } + thread.join + ScratchPad.recorded.should == nil + end + + it "cannot be rescued" do + thread = Thread.new do + begin + Thread.current.send(@method) + rescue Exception + ScratchPad.record :in_rescue + end + ScratchPad.record :end_of_thread_block + end + + thread.join + ScratchPad.recorded.should == nil + end + + with_feature :fiber do + it "kills the entire thread when a fiber is active" do + t = Thread.new do + Fiber.new do + sleep + end.resume + ScratchPad.record :fiber_resumed + end + Thread.pass while t.status and t.status != "sleep" + t.send(@method) + t.join + ScratchPad.recorded.should == nil + end + end + + # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed + quarantine! do + it "killing dying running does nothing" do + in_ensure_clause = false + exit_loop = true + t = ThreadSpecs.dying_thread_ensures do + in_ensure_clause = true + loop { if exit_loop then break end } + ScratchPad.record :after_stop + end + + Thread.pass until in_ensure_clause == true + 10.times { t.send(@method); Thread.pass } + exit_loop = true + t.join + ScratchPad.recorded.should == :after_stop + end + end + + quarantine! do + + it "propogates inner exception to Thread.join if there is an outer ensure clause" do + thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { } + lambda { thread.join }.should raise_error(RuntimeError, "In dying thread") + end + + it "runs all outer ensure clauses even if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause } + ScratchPad.recorded.should == :in_outer_ensure_clause + end + + it "sets $! in outer ensure clause if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! } + ScratchPad.recorded.to_s.should == "In dying thread" + end + end + + it "can be rescued by outer rescue clause when inner ensure clause raises exception" do + thread = Thread.new do + begin + begin + Thread.current.send(@method) + ensure + raise "In dying thread" + end + rescue Exception + ScratchPad.record $! + end + :end_of_thread_block + end + + thread.value.should == :end_of_thread_block + ScratchPad.recorded.to_s.should == "In dying thread" + end + + it "is deferred if ensure clause does Thread.stop" do + ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + + # Hangs on 1.8.6.114 OS X, possibly also on Linux + quarantine! do + it "is deferred if ensure clause sleeps" do + ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + end + + # This case occurred in JRuby where native threads are used to provide + # the same behavior as MRI green threads. Key to this issue was the fact + # that the thread which called #exit in its block was also being explicitly + # sent #join from outside the thread. The 100.times provides a certain + # probability that the deadlock will occur. It was sufficient to reliably + # reproduce the deadlock in JRuby. + it "does not deadlock when called from within the thread while being joined from without" do + 100.times do + t = Thread.new { Thread.stop; Thread.current.send(@method) } + Thread.pass while t.status and t.status != "sleep" + t.wakeup.should == t + t.join.should == t + end + end +end |