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.