diff options
author | Benoit Daloze <[email protected]> | 2021-01-28 17:08:57 +0100 |
---|---|---|
committer | Benoit Daloze <[email protected]> | 2021-01-28 17:08:57 +0100 |
commit | 2e32b919b4f2f5b7f2e1509d6fa985526ef1f61c (patch) | |
tree | aedadac3c99ca0097c2bbeaa95830332d6fb9971 /spec/ruby/core/thread | |
parent | 1b377b32c8616f85c0a97e68758c5c2db83f2169 (diff) |
Update to ruby/spec@8cafaa5
Diffstat (limited to 'spec/ruby/core/thread')
-rw-r--r-- | spec/ruby/core/thread/backtrace_locations_spec.rb | 8 | ||||
-rw-r--r-- | spec/ruby/core/thread/handle_interrupt_spec.rb | 125 | ||||
-rw-r--r-- | spec/ruby/core/thread/pending_interrupt_spec.rb | 32 |
3 files changed, 165 insertions, 0 deletions
diff --git a/spec/ruby/core/thread/backtrace_locations_spec.rb b/spec/ruby/core/thread/backtrace_locations_spec.rb index ead4be2d8c..1f77e13378 100644 --- a/spec/ruby/core/thread/backtrace_locations_spec.rb +++ b/spec/ruby/core/thread/backtrace_locations_spec.rb @@ -51,6 +51,14 @@ describe "Thread#backtrace_locations" do end end + ruby_version_is "2.7" do + it "can be called with an beginless range" do + locations1 = Thread.current.backtrace_locations(0) + locations2 = Thread.current.backtrace_locations(eval("(..5)")) + locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(..5)")].map(&:to_s)[eval("(2..)")] + end + end + it "returns nil if omitting more locations than available" do Thread.current.backtrace_locations(100).should == nil Thread.current.backtrace_locations(100..-1).should == nil diff --git a/spec/ruby/core/thread/handle_interrupt_spec.rb b/spec/ruby/core/thread/handle_interrupt_spec.rb new file mode 100644 index 0000000000..ea7e81cb98 --- /dev/null +++ b/spec/ruby/core/thread/handle_interrupt_spec.rb @@ -0,0 +1,125 @@ +require_relative '../../spec_helper' + +describe "Thread.handle_interrupt" do + def make_handle_interrupt_thread(interrupt_config, blocking = true) + interrupt_class = Class.new(RuntimeError) + + ScratchPad.record [] + + in_handle_interrupt = Queue.new + can_continue = Queue.new + + thread = Thread.new do + begin + Thread.handle_interrupt(interrupt_config) do + begin + in_handle_interrupt << true + if blocking + Thread.pass # Make it clearer the other thread needs to wait for this one to be in #pop + can_continue.pop + else + begin + can_continue.pop(true) + rescue ThreadError + Thread.pass + retry + end + end + rescue interrupt_class + ScratchPad << :interrupted + end + end + rescue interrupt_class + ScratchPad << :deferred + end + end + + in_handle_interrupt.pop + if blocking + # Ensure the thread is inside Thread#pop, as if thread.raise is done before it would be deferred + Thread.pass until thread.stop? + end + thread.raise interrupt_class, "interrupt" + can_continue << true + thread.join + + ScratchPad.recorded + end + + before :each do + Thread.pending_interrupt?.should == false # sanity check + end + + it "with :never defers interrupts until exiting the handle_interrupt block" do + make_handle_interrupt_thread(RuntimeError => :never).should == [:deferred] + end + + it "with :on_blocking defers interrupts until the next blocking call" do + make_handle_interrupt_thread(RuntimeError => :on_blocking).should == [:interrupted] + make_handle_interrupt_thread({ RuntimeError => :on_blocking }, false).should == [:deferred] + end + + it "with :immediate handles interrupts immediately" do + make_handle_interrupt_thread(RuntimeError => :immediate).should == [:interrupted] + end + + it "with :immediate immediately runs pending interrupts, before the block" do + Thread.handle_interrupt(RuntimeError => :never) do + current = Thread.current + Thread.new { + current.raise "interrupt immediate" + }.join + + Thread.pending_interrupt?.should == true + -> { + Thread.handle_interrupt(RuntimeError => :immediate) { + flunk "not reached" + } + }.should raise_error(RuntimeError, "interrupt immediate") + Thread.pending_interrupt?.should == false + end + end + + it "also works with suspended Fibers and does not duplicate interrupts" do + fiber = Fiber.new { Fiber.yield } + fiber.resume + + Thread.handle_interrupt(RuntimeError => :never) do + current = Thread.current + Thread.new { + current.raise "interrupt with fibers" + }.join + + Thread.pending_interrupt?.should == true + -> { + Thread.handle_interrupt(RuntimeError => :immediate) { + flunk "not reached" + } + }.should raise_error(RuntimeError, "interrupt with fibers") + Thread.pending_interrupt?.should == false + end + + fiber.resume + end + + it "runs pending interrupts at the end of the block, even if there was an exception raised in the block" do + executed = false + -> { + Thread.handle_interrupt(RuntimeError => :never) do + current = Thread.current + Thread.new { + current.raise "interrupt exception" + }.join + + Thread.pending_interrupt?.should == true + executed = true + raise "regular exception" + end + }.should raise_error(RuntimeError, "interrupt exception") + executed.should == true + end + + it "supports multiple pairs in the Hash" do + make_handle_interrupt_thread(ArgumentError => :never, RuntimeError => :never).should == [:deferred] + end +end diff --git a/spec/ruby/core/thread/pending_interrupt_spec.rb b/spec/ruby/core/thread/pending_interrupt_spec.rb new file mode 100644 index 0000000000..cd565d92a4 --- /dev/null +++ b/spec/ruby/core/thread/pending_interrupt_spec.rb @@ -0,0 +1,32 @@ +require_relative '../../spec_helper' + +describe "Thread.pending_interrupt?" do + it "returns false if there are no pending interrupts, e.g., outside any Thread.handle_interrupt block" do + Thread.pending_interrupt?.should == false + end + + it "returns true if there are pending interrupts, e.g., Thread#raise inside Thread.handle_interrupt" do + executed = false + -> { + Thread.handle_interrupt(RuntimeError => :never) do + Thread.pending_interrupt?.should == false + + current = Thread.current + Thread.new { + current.raise "interrupt" + }.join + + Thread.pending_interrupt?.should == true + executed = true + end + }.should raise_error(RuntimeError, "interrupt") + executed.should == true + Thread.pending_interrupt?.should == false + end +end + +describe "Thread#pending_interrupt?" do + it "returns whether the given threads has pending interrupts" do + Thread.current.pending_interrupt?.should == false + end +end |