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 the term "owner", which in case of a shared_mutex is not well-defined: several threads may have .lock_shared()
called at the same time.
Assuming "owner" to be a thread which calls .lock()
(not .lock_shared()
!), an implementation of the 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;
};
The field .owner
needs to be declared as atomic, because in the .lock()
method this field is checked without a protection from the concurrent access.
If you want to recursively call .lock_shared()
method, you need to maintain a map of owners, and accesses to that map should be protected with some additional mutex.
Allowing a thread with active .lock()
to call .lock_shared()
makes implementation to be more complex.
Finally, allowing a 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.