diff options
-rw-r--r-- | activesupport/lib/active_support/concurrency/share_lock.rb | 2 | ||||
-rw-r--r-- | activesupport/test/share_lock_test.rb | 29 |
2 files changed, 30 insertions, 1 deletions
diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb index 1537f2898f..fa5d9bfdd7 100644 --- a/activesupport/lib/active_support/concurrency/share_lock.rb +++ b/activesupport/lib/active_support/concurrency/share_lock.rb @@ -85,7 +85,7 @@ module ActiveSupport def start_sharing(purpose: :share) synchronize do - if busy_for_sharing?(purpose) + if @sharing[Thread.current] == 0 && @exclusive_thread != Thread.current && busy_for_sharing?(purpose) @cv.wait_while { busy_for_sharing?(purpose) } end @sharing[Thread.current] += 1 diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb index 0a5b074bee..3475ee94cd 100644 --- a/activesupport/test/share_lock_test.rb +++ b/activesupport/test/share_lock_test.rb @@ -241,6 +241,35 @@ class ShareLockTest < ActiveSupport::TestCase end end + def test_share_remains_reentrant_ignoring_a_waiting_exclusive + with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch| + ready = Concurrent::CyclicBarrier.new(2) + attempt_reentrancy = Concurrent::CountDownLatch.new + + sharer = Thread.new do + @lock.sharing do + ready.wait + attempt_reentrancy.wait + @lock.sharing {} + end + end + + exclusive = Thread.new do + @lock.sharing do + ready.wait + @lock.exclusive {} + end + end + + assert_threads_stuck exclusive + + attempt_reentrancy.count_down + + assert_threads_not_stuck sharer + assert_threads_stuck exclusive + end + end + def test_in_shared_section_incompatible_non_upgrading_threads_cannot_preempt_upgrading_threads scratch_pad = [] scratch_pad_mutex = Mutex.new |