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?
shared_mutex
appears to be the stated purpose of shared_lock. "For example the standard defined generic locking algorithm which locks multiple locks without deadlock can just as easily work on ashared_lock<shared_mutex>
as aunique_lock<mutex>
." – Greenwichstd::scoped_lock
was meant to work with any Lockable objects, and different Lockables would do; be itmutex
,recursive_mutex
,unique_lock<>
,shared_lock<>
, or any user defined ones (as long as they operate properly). – Centriolestd::scoped_lock
is applied directly on astd::shared_lock
, the later will be locked in exclusive (write) mode. But I want one of two shared locks to be locked in shared (read) mode. – Piecedyedlock()
,unlock()
andtry_lock()
on the lock as it should, and instead it takes the mutex itself and applies them to the mutex instead of the lock? This is an error if it does so. – Centriolestd::scoped_lock lockall(std::defer_lock, slock, dst.mutex);
std::lock(slock,dst.mutex)
Would also have been a solution – Unrealizablescoped_lock
does accept objects that satisfy Lockable, so your code is fine. See the comments to the answer for some details. The part of the answer that says thatscoped_lock
wants to work with the underlying mutex is incorrect as far as I can tell. – Faggoting