Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A shared recursive mutex in standard C++

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?

like image 987
Ralph Tandetzky Avatar asked Apr 14 '16 10:04

Ralph Tandetzky


2 Answers

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.

like image 162
Tsyvarev Avatar answered Oct 17 '22 12:10

Tsyvarev


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.

like image 8
Maxim Egorushkin Avatar answered Oct 17 '22 11:10

Maxim Egorushkin