There is a shared_mutex
class planned for C++17. And shared_timed_mutex
already in C++14. (Who knows why they came in that order, but whatever.) Then there is a recursive_mutex
and a recursive_timed_mutex
since C++11. What I need is a shared_recursive_mutex
. Did I miss something in the standard or do I have to wait another three years for a standardized version of that?
If there is currently no such facility, what would be a simple (first priority) and efficient (2nd priority) implementation of such a feature using standard C++ only?
Recursive property of the mutex operates with term owner, which in case of shared_mutex is not well-defined: several threads may have .lock_shared()
called at the same time.
Assuming owner as a thread which calls .lock()
(not .lock_shared()
!), implementation of recursive shared mutex can be simply derived from shared_mutex
:
class shared_recursive_mutex: public shared_mutex
{
public:
void lock(void) {
std::thread::id this_id = std::this_thread::get_id();
if(owner == this_id) {
// recursive locking
count++;
}
else {
// normal locking
shared_mutex::lock();
owner = this_id;
count = 1;
}
}
void unlock(void) {
if(count > 1) {
// recursive unlocking
count--;
}
else {
// normal unlocking
owner = std::thread::id();
count = 0;
shared_mutex::unlock();
}
}
private:
std::atomic<std::thread::id> owner;
int count;
};
Field .owner
need to be declared as atomic, because in .lock()
method it is checked without protection from concurrent access.
If you want to recursively call .lock_shared()
method, you need to maintain map of owners, and accesses to that map should be protected with some additional mutex.
Allowing thread with active .lock()
to call .lock_shared()
make implementation more complex.
Finally, allowing thread to advance locking from .lock_shared()
to .lock()
is no-no, as it leads to possible deadlock when two threads attempt to perform that advancing.
Again, semantic of recursive shared mutex would be very fragile, so it is better to not use it at all.
If you are on Linux / POSIX platform, you are in luck because C++ mutexes are modelled after POSIX ones. The POSIX ones provide more features, including being recursive, process shared and more. And wrapping POSIX primitives into C++ classes is straight-forward.
Good entry point into POSIX threads documentation.
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