Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can scoped_lock lock a shared_mutex in read mode?

C++17 introduced both std::shared_mutex and std::scoped_lock. My problem is now, that it seems, that scoped_lock will lock a shared mutex always in exclusive (writer) mode, when it is passed as an argument, and not in shared (reader) mode. In my app, I need to update an object dst with data from an object src. I want to lock src shared and dst exclusive. Unfortunately, this has the potential for deadlock, if a call to another update method with src and dst switched occurs at the same time. So I would like to use the fancy deadlock avoidance mechanisms of std::scoped_lock.

I could use scoped_lock to lock both src and dst in exclusive mode, but that unnecessarily strict lock has performance backdraws elsewhere. However, it seems, that it is possible to wrap src's shared_mutex into a std::shared_lock and use that with the scoped_lock: When the scoped_lock during its locking action calls try_lock() on the shared_lock, the later will actually call try_shared_lock() on src's shared_mutex, and that's what I need.

So my code looks as simple as this:

struct data {
    mutable std::shared_mutex mutex;
    // actual data follows
};

void update(const data& src, data& dst)
{
    std::shared_lock slock(src.mutex, std::defer_lock);
    std::scoped_lock lockall(slock, dst.mutex);
    // now can safely update dst with src???
}

Is it safe to use a (shared) lock guard like this inside another (deadlock avoidance) lock guard?

like image 421
Kai Petzke Avatar asked Feb 12 '19 00:02

Kai Petzke


People also ask

What is scoped lock?

The Scoped Locking C++ idiom ensures that a lock is acquired when control enters a scope and the lock is released automatically when control leaves the scope. Also Known As. Synchronized Block, Object-Construction-is-Resource-Acquisition.

What is shared lock in C++?

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)

What is a Shared_mutex?

The shared_mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.


Video Answer


1 Answers

As pointed out by various commentators, who have read the implementation code of the C++ standard library: Yes, the use of a std::shared_mutex wrapped inside a std::shared_lock() as one of the arguments to std::scoped_lock() is safe.

Basically, a std::shared_lock forwards all calls to lock() to lock_shared() on the mutex.

std::shared_lock::lock -----------> mutex()->lock_shared(). // same for try_lock etc..

Another possible solution

std::shared_lock lk1(src.mutex, std::defer_lock);
std::unique_lock lk2(dst.mutex, std::defer_lock);
std::lock(lk1, lk2);

std::lock is a function that accepts any number of Lockable objects and locks all of them (or aborts with an exception, in which case they will all be unlocked).

std::scoped_lock according to cppreference is a wrapper for std::lock, with the added functionaliy of calling unlock() on each Lockable object in its destructor. That added functionality is not required here, as std::shared_lock lk1 and std::unique_lock lk2 also work as lock guards, that unlock their mutexes, when they go out of scope.

Edit: various clarifications

like image 154
Mellester Avatar answered Oct 23 '22 11:10

Mellester