diff options
Diffstat (limited to 'spec/ruby/core/mutex/lock_spec.rb')
| -rw-r--r-- | spec/ruby/core/mutex/lock_spec.rb | 91 |
1 files changed, 71 insertions, 20 deletions
diff --git a/spec/ruby/core/mutex/lock_spec.rb b/spec/ruby/core/mutex/lock_spec.rb index b5c2b168e8..4fee29091a 100644 --- a/spec/ruby/core/mutex/lock_spec.rb +++ b/spec/ruby/core/mutex/lock_spec.rb @@ -1,41 +1,92 @@ -require File.expand_path('../../../spec_helper', __FILE__) +require_relative '../../spec_helper' describe "Mutex#lock" do - before :each do - ScratchPad.clear - end - it "returns self" do m = Mutex.new m.lock.should == m m.unlock end - it "waits if the lock is not available" do + it "blocks the caller if already locked" do m = Mutex.new - m.lock + -> { m.lock }.should block_caller + end - th = Thread.new do - m.lock - ScratchPad.record :after_lock - end - - Thread.pass while th.status and th.status != "sleep" - - ScratchPad.recorded.should be_nil - m.unlock - th.join - ScratchPad.recorded.should == :after_lock + it "does not block the caller if not locked" do + m = Mutex.new + -> { m.lock }.should_not block_caller end # Unable to find a specific ticket but behavior change may be # related to this ML thread. - it "raises a ThreadError when used recursively" do + it "raises a deadlock ThreadError when used recursively" do m = Mutex.new m.lock -> { m.lock - }.should raise_error(ThreadError) + }.should.raise(ThreadError, /deadlock/) + end + + it "raises a deadlock ThreadError when multiple fibers from the same thread try to lock" do + m = Mutex.new + + m.lock + f0 = Fiber.new do + m.lock + end + -> { f0.resume }.should.raise(ThreadError, /deadlock/) + + m.unlock + f1 = Fiber.new do + m.lock + Fiber.yield + end + f2 = Fiber.new do + m.lock + end + f1.resume + -> { f2.resume }.should.raise(ThreadError, /deadlock/) + end + + it "does not raise deadlock if a fiber's attempt to lock was interrupted" do + lock = Mutex.new + main = Thread.current + + t2 = nil + t1 = Thread.new do + loop do + # interrupt fiber below looping on synchronize + sleep 0.01 + t2.raise if t2 + end + end + + # loop ten times to try to handle the interrupt during synchronize + t2 = Thread.new do + 10.times do + Fiber.new do + begin + loop { lock.synchronize {} } + rescue RuntimeError + end + end.resume + + Fiber.new do + -> do + lock.synchronize {} + end.should_not.raise(ThreadError) + end.resume + rescue RuntimeError + retry + end + end + t2.join + ensure + t1.kill rescue nil + t2.kill rescue nil + + t1.join + t2.join end end |
