1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/core/thread/shared/exit.rb
2019-07-27 12:40:09 +02:00

180 lines
5.4 KiB
Ruby

describe :thread_exit, shared: true do
before :each do
ScratchPad.clear
end
# This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19390874/job/wv1bsm8skd4e1pxl
# TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard.
platform_is_not :mingw do
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
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
# 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 "propagates inner exception to Thread.join if there is an outer ensure clause" do
thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { }
-> { 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 # platform_is_not :mingw
end