The new std::shared_timed_mutex allows for two types of locks: shared and exclusive.
If one holds a shared lock, is there any way to atomically exchange it ("upgrade it") to an exclusive lock? In other words, given the following code, how can I avoid the non-atomic drop and re-lock?
std::shared_timed_mutex m; //Guards a std::vector. m.lock_shared(); //Read from vector. (Shared lock is sufficient.) // ... //Now we want to write to the vector. We need an exclusive lock. m.unlock_shared(); // <---- Problem here: non-atomic! m.lock(); //Write to vector. // ... m.unlock();
Ideally, m.unlock_shared(); m.lock();
could be replaced with something like m.upgrade_to_exclusive();
(or something like the boost::.upgrade_to_unique_lock()
).
In a similar question but for Boost's shared_mutex Dave S mentions
It is impossible to convert from a shared lock to a unique lock, or a shared lock to an upgradeable lock without releasing the shared lock first.
I'm not certain whether this applies to std::shared_mutex, though I suspect it does.
I would be happy with a reasonable work-around based on std::atomic/condition_variable or GCC's transactional memory.
Edit: Howard's answer addresses my question. His proposal N3427 contains nice descriptions of a mechanism to achieve mutex upgrading. I still welcome work-arounds based on std::atomic/condition_variable or GCC's transactional memory.
class shared_lock; (since C++14) The class shared_lock is a general-purpose shared mutex ownership wrapper allowing deferred locking, timed locking and transfer of lock ownership. Locking a shared_lock locks the associated shared mutex in shared mode (to lock it in exclusive mode, std::unique_lock can be used)
The upgrade_lock is useful when some of threads can be readers only and will not try to promote itself to writers. Otherwise (all readers may try to become writers at some point) upgrade_lock will operate as unique_lock .
std::unique_lock The class unique_lock is a general-purpose mutex ownership wrapper allowing deferred locking, time-constrained attempts at locking, recursive locking, transfer of lock ownership, and use with condition variables.
No, it can not. That functionality was proposed to the committee under the name upgrade_mutex
and upgrade_lock
, but the committee chose to reject that portion of the proposal. There is currently no work under way to re-prepose that functionality.
Edit
In response to the "where to go from here" edit in user3761401's question, I've created a partially crippled implementation of upgrade_mutex/upgrade_lock
here:
https://github.com/HowardHinnant/upgrade_mutex
Feel free to use this. It is in the public domain. It is only lightly tested, and it does not have the full functionality described in N3427. Specifically the following functionality is missing:
unique_lock
to a shared_timed_lock
.shared_timed_lock
to a unique_lock
.upgrade_lock
to a unique_lock
.That being said, I've included this functionality in upgrade_mutex
and it can be accessed at this low level in a very ugly manner (such examples are in main.cpp).
The other lock conversions mentioned in N3427 are available.
shared_timed_lock
to upgrade_lock
.upgrade_lock
to shared_timed_lock
.upgrade_lock
to unique_lock
.unique_lock
to upgrade_lock
.It has all been put in namespace acme
. Put it in whatever namespace you want.
Requirements
The compiler needs to support "rvalue-this" qualifiers, and explicit conversion operators.
Disclaimers
The code has been only lightly tested. If you find bugs I would appreciate a pull request.
It is possible to optimize the upgrade_mutex
through the use of std::atomic
. No effort has been done on that front (it is a difficult and error prone task, taking more time than I have at the moment).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With