diff --git a/spec/process_shared/lock_behavior.rb b/spec/process_shared/lock_behavior.rb new file mode 100644 index 0000000..77f97bc --- /dev/null +++ b/spec/process_shared/lock_behavior.rb @@ -0,0 +1,60 @@ +module ProcessShared + # Classes that include this module should assign a lock object to + # @lock before each test. + module LockBehavior + + # Fork +n+ processes. In each, yield the block (passing the process + # number), then call Kernel.exit! Waits for all processes to + # complete before returning. + def fork_many(n) + pids = [] + n.times do |i| + pids << fork do + yield i + Kernel.exit! + end + end + + pids.each { |pid| ::Process.wait(pid) } + end + + def test_protects_access_to_a_shared_variable + mem = SharedMemory.new(:char) + mem.put_char(0, 0) + + fork_many(10) do |i| + inc = (-1) ** i # half the procs increment; half decrement + 10.times do + @lock.lock + begin + mem.put_char(0, mem.get_char(0) + inc) + sleep 0.001 + ensure + @lock.unlock + end + end + end + + mem.get_char(0).must_equal(0) + end + + def test_protects_access_to_a_shared_variable_with_synchronize + mem = SharedMemory.new(:char) + mem.put_char(0, 0) + + fork_many(10) do |i| + inc = (-1) ** i # half the procs increment; half decrement + 10.times do + @lock.synchronize do + mem.put_char(0, mem.get_char(0) + inc) + sleep 0.001 + end + end + end + + mem.get_char(0).must_equal(0) + end + + + end +end diff --git a/spec/process_shared/mutex_spec.rb b/spec/process_shared/mutex_spec.rb index 84465a0..07744c7 100644 --- a/spec/process_shared/mutex_spec.rb +++ b/spec/process_shared/mutex_spec.rb @@ -1,81 +1,35 @@ require 'spec_helper' require 'process_shared' +require 'process_shared/lock_behavior' + module ProcessShared describe Mutex do - it 'protects access to a shared variable' do - mutex = Mutex.new - mem = SharedMemory.new(:char) - mem.put_char(0, 0) - pids = [] - 10.times do |i| - inc = (-1) ** i # half the procs increment; half decrement - pids << fork do - 10.times do - mutex.lock - begin - mem.put_char(0, mem.get_char(0) + inc) - sleep 0.001 - ensure - mutex.unlock - end - end - Kernel.exit! - end - end + include LockBehavior - pids.each { |pid| ::Process.wait(pid) } - - mem.get_char(0).must_equal(0) + before :each do + @lock = Mutex.new end - - it 'protects access to a shared variable with synchronize' do - mutex = Mutex.new - mem = SharedMemory.new(:char) - mem.put_char(0, 0) - - pids = [] - 10.times do |i| - inc = (-1) ** i # half the procs increment; half decrement - pids << fork do - 10.times do - mutex.synchronize do - mem.put_char(0, mem.get_char(0) + inc) - sleep 0.001 - end - end - Kernel.exit! - end - end - - pids.each { |pid| ::Process.wait(pid) } - - mem.get_char(0).must_equal(0) - end - + it 'raises exception when unlocked by other process' do - mutex = Mutex.new - pid = Kernel.fork do - mutex.lock + @lock.lock sleep 0.2 - mutex.unlock + @lock.unlock Kernel.exit! end sleep 0.1 - proc { mutex.unlock }.must_raise(ProcessError) + proc { @lock.unlock }.must_raise(ProcessError) ::Process.wait(pid) end it 'raises exception when locked twice by same process' do - mutex = Mutex.new - - mutex.lock - proc { mutex.lock }.must_raise(ProcessError) - mutex.unlock + @lock.lock + proc { @lock.lock }.must_raise(ProcessError) + @lock.unlock end end end