From d1644104c8660281418ab0c98f460f9a3a1b1c64 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 12:27:36 -0500 Subject: [PATCH 1/6] On mach, allow other ruby green threads to continue during semaphore wait --- lib/mach/functions.rb | 7 ++++--- spec/process_shared/lock_behavior.rb | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/mach/functions.rb b/lib/mach/functions.rb index d5cd8e7..057ec38 100644 --- a/lib/mach/functions.rb +++ b/lib/mach/functions.rb @@ -72,8 +72,8 @@ module Mach # Attach a function as with +attach_function+, but check the # return value and raise an exception on errors. - def self.attach_mach_function(sym, argtypes, rettype) - attach_function(sym, argtypes, rettype) + def self.attach_mach_function(sym, argtypes, rettype, options = nil) + attach_function(sym, argtypes, rettype, options) error_check(sym) end @@ -208,7 +208,8 @@ module Mach :kern_return_t) attach_mach_function(:semaphore_wait, [:semaphore_t], - :kern_return_t) + :kern_return_t, + :blocking => true) attach_mach_function(:semaphore_timedwait, [:semaphore_t, TimeSpec.val], :kern_return_t) diff --git a/spec/process_shared/lock_behavior.rb b/spec/process_shared/lock_behavior.rb index 77f97bc..d45d742 100644 --- a/spec/process_shared/lock_behavior.rb +++ b/spec/process_shared/lock_behavior.rb @@ -55,6 +55,26 @@ module ProcessShared mem.get_char(0).must_equal(0) end + def test_allows_other_threads_within_a_process_to_continue_while_locked + was_set = false + + @lock.synchronize do + t1 = Thread.new do + # give t2 a chance to wait on the lock, then set the flag + sleep 0.01 + was_set = true + end + + t2 = Thread.new do + @lock.synchronize { } + end + + # t1 should set the flag and die while t2 is still waiting on the lock + t1.join + end + + was_set.must_equal(true) + end end end From 50c096d84c0c78b5e0e4f10360e7d924ea23bcb8 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 12:45:04 -0500 Subject: [PATCH 2/6] On mach, allow other ruby threads to continue during semaphore try_wait --- lib/mach/functions.rb | 3 ++- spec/process_shared/semaphore_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/mach/functions.rb b/lib/mach/functions.rb index 057ec38..eda7628 100644 --- a/lib/mach/functions.rb +++ b/lib/mach/functions.rb @@ -212,7 +212,8 @@ module Mach :blocking => true) attach_mach_function(:semaphore_timedwait, [:semaphore_t, TimeSpec.val], - :kern_return_t) + :kern_return_t, + :blocking => true) end end diff --git a/spec/process_shared/semaphore_spec.rb b/spec/process_shared/semaphore_spec.rb index c6afad1..a39f959 100644 --- a/spec/process_shared/semaphore_spec.rb +++ b/spec/process_shared/semaphore_spec.rb @@ -131,6 +131,31 @@ module ProcessShared ::Process.wait(pid) end end + + it 'allows other threads in a process to continue while waiting' do + # NOTE: A similar test in LockBehavior tests Semaphore#wait, + # Mutex#lock, etc. Necessary only to test #try_wait here. + + start = Time.now.to_f + was_set = false + + Semaphore.open(0) do |sem| + t1 = Thread.new do + # give t2 a chance to wait on the lock, then set the flag + sleep 0.01 + was_set = true + end + + t2 = Thread.new do + sem.try_wait(10.0) + end + + t1.join + end + + was_set.must_equal true + (Time.now.to_f - start).must be_lt(0.1) + end end end end From e2e70591a1c97210a30b145f1b44babade4b5082 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 12:47:09 -0500 Subject: [PATCH 3/6] On posix, allow ruby threads to continue during wait / try_wait --- lib/process_shared/posix/semaphore.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/process_shared/posix/semaphore.rb b/lib/process_shared/posix/semaphore.rb index e75028f..fab29f8 100644 --- a/lib/process_shared/posix/semaphore.rb +++ b/lib/process_shared/posix/semaphore.rb @@ -26,9 +26,9 @@ module ProcessShared attach_function :sem_getvalue, [:sem_p, :pointer], :int attach_function :sem_post, [:sem_p], :int - attach_function :sem_wait, [:sem_p], :int - attach_function :sem_trywait, [:sem_p], :int - attach_function :sem_timedwait, [:sem_p, TimeSpec], :int + attach_function :sem_wait, [:sem_p], :int, :blocking => true + attach_function :sem_trywait, [:sem_p], :int, :blocking => true + attach_function :sem_timedwait, [:sem_p, TimeSpec], :int, :blocking => true error_check(:sem_close, :sem_unlink, :sem_init, :sem_destroy, :sem_getvalue, :sem_post, :sem_wait, :sem_trywait, From d3d51fef91b364bf886478c5ee96f5e42e6da72d Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 13:28:40 -0500 Subject: [PATCH 4/6] Moved tests to just semaphore, and fixed thread joining --- spec/process_shared/lock_behavior.rb | 21 -------------- spec/process_shared/semaphore_spec.rb | 42 +++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/spec/process_shared/lock_behavior.rb b/spec/process_shared/lock_behavior.rb index d45d742..7e49869 100644 --- a/spec/process_shared/lock_behavior.rb +++ b/spec/process_shared/lock_behavior.rb @@ -55,26 +55,5 @@ module ProcessShared mem.get_char(0).must_equal(0) end - def test_allows_other_threads_within_a_process_to_continue_while_locked - was_set = false - - @lock.synchronize do - t1 = Thread.new do - # give t2 a chance to wait on the lock, then set the flag - sleep 0.01 - was_set = true - end - - t2 = Thread.new do - @lock.synchronize { } - end - - # t1 should set the flag and die while t2 is still waiting on the lock - t1.join - end - - was_set.must_equal(true) - end - end end diff --git a/spec/process_shared/semaphore_spec.rb b/spec/process_shared/semaphore_spec.rb index a39f959..8f12f01 100644 --- a/spec/process_shared/semaphore_spec.rb +++ b/spec/process_shared/semaphore_spec.rb @@ -91,6 +91,32 @@ module ProcessShared end end end + + it 'allows other threads in a process to continue while waiting' do + sem = Semaphore.new + was_set = false + t2 = nil + + sem.synchronize do + t1 = Thread.new do + # give t2 a chance to wait on the lock, then set the flag + sleep 0.01 + was_set = true + end + + t2 = Thread.new do + sem.synchronize { } + end + + # t1 should set the flag and die while t2 is still waiting on the lock + t1.join + end + + was_set.must_equal true + + t2.join + end + end describe '#try_wait' do @@ -133,13 +159,12 @@ module ProcessShared end it 'allows other threads in a process to continue while waiting' do - # NOTE: A similar test in LockBehavior tests Semaphore#wait, - # Mutex#lock, etc. Necessary only to test #try_wait here. - start = Time.now.to_f + sem = Semaphore.new was_set = false + t2 = nil - Semaphore.open(0) do |sem| + sem.synchronize do t1 = Thread.new do # give t2 a chance to wait on the lock, then set the flag sleep 0.01 @@ -147,14 +172,21 @@ module ProcessShared end t2 = Thread.new do - sem.try_wait(10.0) + begin + sem.try_wait(10.0) + rescue Errno::ETIMEDOUT + # success + end end + # t1 should set the flag and die while t2 is still waiting on the lock t1.join end was_set.must_equal true (Time.now.to_f - start).must be_lt(0.1) + + t2.join end end end From d2f8af03eb6bc8043cb47b979092f0ff1af109d2 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 13:48:29 -0500 Subject: [PATCH 5/6] Workaround apparent bug in 1.8.7/REE: block all during sem_timedwait --- lib/process_shared/posix/semaphore.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/process_shared/posix/semaphore.rb b/lib/process_shared/posix/semaphore.rb index fab29f8..d1fe59e 100644 --- a/lib/process_shared/posix/semaphore.rb +++ b/lib/process_shared/posix/semaphore.rb @@ -28,7 +28,10 @@ module ProcessShared attach_function :sem_post, [:sem_p], :int attach_function :sem_wait, [:sem_p], :int, :blocking => true attach_function :sem_trywait, [:sem_p], :int, :blocking => true - attach_function :sem_timedwait, [:sem_p, TimeSpec], :int, :blocking => true + + # Workaround bug which only appears to affect Ruby 1.8.7 and REE + BLOCKING_SEM_TIMEDWAIT = (RUBY_VERSION != '1.8.7') + attach_function :sem_timedwait, [:sem_p, TimeSpec], :int, :blocking => BLOCKING_SEM_TIMEDWAIT error_check(:sem_close, :sem_unlink, :sem_init, :sem_destroy, :sem_getvalue, :sem_post, :sem_wait, :sem_trywait, From 90a9f37da4bfae48175f19d6ccef979697a2acf1 Mon Sep 17 00:00:00 2001 From: Marc Siegel Date: Thu, 26 Dec 2013 13:51:03 -0500 Subject: [PATCH 6/6] Disable test on 1.8.7, since workaround is to allow blocking on that version --- spec/process_shared/semaphore_spec.rb | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/spec/process_shared/semaphore_spec.rb b/spec/process_shared/semaphore_spec.rb index 8f12f01..a0a844c 100644 --- a/spec/process_shared/semaphore_spec.rb +++ b/spec/process_shared/semaphore_spec.rb @@ -158,35 +158,37 @@ module ProcessShared end end - it 'allows other threads in a process to continue while waiting' do - start = Time.now.to_f - sem = Semaphore.new - was_set = false - t2 = nil + unless RUBY_VERSION == '1.8.7' + it 'allows other threads in a process to continue while waiting' do + start = Time.now.to_f + sem = Semaphore.new + was_set = false + t2 = nil - sem.synchronize do - t1 = Thread.new do - # give t2 a chance to wait on the lock, then set the flag - sleep 0.01 - was_set = true - end - - t2 = Thread.new do - begin - sem.try_wait(10.0) - rescue Errno::ETIMEDOUT - # success + sem.synchronize do + t1 = Thread.new do + # give t2 a chance to wait on the lock, then set the flag + sleep 0.01 + was_set = true end + + t2 = Thread.new do + begin + sem.try_wait(10.0) + rescue Errno::ETIMEDOUT + # success + end + end + + # t1 should set the flag and die while t2 is still waiting on the lock + t1.join end - # t1 should set the flag and die while t2 is still waiting on the lock - t1.join + was_set.must_equal true + (Time.now.to_f - start).must be_lt(0.1) + + t2.join end - - was_set.must_equal true - (Time.now.to_f - start).must be_lt(0.1) - - t2.join end end end