Skip to content

Commit a9ae6de

Browse files
authored
Merge pull request #919 from turnon/can-get-write-lock-after-read-locked-more-than-once-only-by-myself
can get write lock after read locked more than once only by myself
2 parents 196e92a + 7957729 commit a9ae6de

File tree

2 files changed

+21
-6
lines changed

2 files changed

+21
-6
lines changed

lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,10 @@ def acquire_write_lock
267267
# running right now, AND no writers who came before us still waiting to
268268
# acquire the lock
269269
# Additionally, if any read locks have been taken, we must hold all of them
270-
if c == held
271-
# If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
272-
if @Counter.compare_and_set(c, c+RUNNING_WRITER)
273-
@HeldCount.value = held + WRITE_LOCK_HELD
274-
return true
275-
end
270+
if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
271+
# If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
272+
@HeldCount.value = held + WRITE_LOCK_HELD
273+
return true
276274
elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
277275
while true
278276
# Now we have successfully incremented, so no more readers will be able to increment

spec/concurrent/atomic/reentrant_read_write_lock_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,23 @@ def wait_up_to(secs, &condition)
149149
end
150150
end
151151

152+
it "can be upgraded to a write lock when read lock acquired more than once" do
153+
Timeout.timeout(3) do
154+
expect(lock.acquire_read_lock).to be true
155+
expect(lock.acquire_read_lock).to be true
156+
expect(Thread.current).to hold(lock).for_read
157+
158+
# now we want to upgrade...
159+
expect(lock.acquire_write_lock).to be true
160+
expect(lock.release_read_lock).to be true
161+
expect(lock.release_read_lock).to be true
162+
expect(Thread.current).to hold(lock).for_write
163+
164+
expect(lock.release_write_lock).to be true
165+
expect(lock).to be_free
166+
end
167+
end
168+
152169
it "cannot be released when not held" do
153170
expect { lock.release_read_lock }.to raise_error(IllegalOperationError)
154171
end

0 commit comments

Comments
 (0)