upgrade_mutex
upgrade_mutex copied to clipboard
Fix bugs and improve upgrade_mutex
I recently took another look at this upgrade_mutex implementation. This is a good implementation and it seems a shame that it isn't in the C++ standard library and probably won't be added anytime soon. However, in case it is reconsidered for addition at some point in the future, I believe the following changes make this upgrade_mutex implementation simpler, more robust, and more likely to be accepted by a C++ standards committee. I welcome your feedback.
-
Remove dangerous functions:
-
Remove
try_unlock_shared_and_lock_until/for()
fromupgrade_mutex
. These functions make it easy to create code that deadlocks and so should not be provided or implemented. -
Remove
try_unlock_shared_and_lock_upgrade_until/for()
fromupgrade_mutex
. These functions make it easy to create code that deadlocks and so should not be provided or implemented. -
Remove the associated timed
upgrade_lock
constructors that create an upgrade lock from a shared lock since they rely on the above functions. -
Eliminate the following bug due to removing
try_unlock_shared_and_lock_until/for()
:- Two shared locks are being held.
- One of the threads calls
try_unlock_shared_and_lock_until()
and blocks waiting ongate2_
. - The other thread releases via
unlock_shared()
. - Because
write_entered_
is false andnum_readers
is 1,unlock_shared()
does not callgate2_.notify_one()
and so the blocked thread does not proceed even though it now can.
-
Eliminate the following bug due to removing
try_unlock_shared_and_lock_upgrade_until/for()
:- Two shared locks are being held.
- One of the two shared locks is upgradable.
- The non-upgradable thread calls
try_unlock_shared_and_lock_upgrade_until()
and blocks waiting ongate2_
. - The upgradable lock releases via
unlock_upgrade()
. - Because
unlock_upgrade()
callsgate1_.notify_all()
buttry_unlock_shared_and_lock_upgrade_until()
is blocked ongate2_
, the blocked thread does not proceed even though it now can.
-
-
Change behavior of
try_unlock_upgrade_and_lock_until/for()
:-
Change
try_unlock_upgrade_and_lock_until()
to takewrite_entered_
whenever it is called so that it prevents new shared locks from being taken. This matches the behavior ofunlock_upgrade_and_lock()
, and it ensures that the function blocks no longer than necessary. -
Fix the following bug:
- Two shared locks are being held.
- One of the two shared locks is upgradable.
- The upgradable thread calls
try_unlock_upgrade_and_lock_until()
and blocks waiting ongate2_
. - The other thread releases via
unlock_shared()
. - Because
write_entered_
is false andnum_readers
is 1,unlock_shared()
does not callgate2_.notify_one()
and so the blocked thread does not proceed even though it now can.
-
-
Fix bug in
unlock_shared()
:- The maximum number of shared locks are being held.
- One of the shared locks is upgradable.
- A thread has called
lock()
and blocks waiting ongate1_
for the shared upgradable lock to release. - A thread has called
lock_shared()
and blocks waiting ongate1_
for a a shared lock to release. - One of the threads releases via
unlock_shared()
which callsgate1_.notify_one()
. - One of two things happens:
- The thread calling
lock_shared()
receives the signal and takes a shared lock. - The thread calling
lock()
receives the signal and does nothing because there is still an upgradable lock. The thread callinglock_shared()
does nothing as well. This is a lost wake-up.
- The thread calling
- This behavior is inconsistent. The thread calling
lock_shared()
should always take a shared lock in this (admittedly rare) situation.
-
Fix bug in
try_lock_until()
:- One shared lock is being held.
- A thread calls
try_lock_until()
and blocks waiting for the shared lock to release. - A second thread calls
lock_shared()
and blocks waiting ongate1_
fortry_lock_until()
to either time out or complete and then release. -
try_lock_until()
times out and exits without taking the lock, butlock_shared()
does not proceed becausegate1_
was never notified.
-
Clean up code:
-
Add brackets around single-line if/else/while blocks.
-
Change
<
to!=
where functionally equivalent for consistency. -
Change
== 0
to!
where functionally equivalent for consistency. -
Add a few explanatory comments.
-
Remove trailing spaces.
-