asterisc asterisc - 9 days ago 5
C++ Question

Is std::lock_guard<std::unique_lock<std::mutex>> valid or recommended?

I need to assert on unique_lock::owns_lock() in a working method, but I dont' want to pass the unique_lock as an argument but use it as a static variable. But that means I cannot use the unique_lock for its RAII behavior.
Would it be fair to have code like:

namespace
{
std::mutex gMtx;
std::unique_lock<std::mutex> gLock(gMtx, std::defer_lock);
}

void foo()
{
assert(gLock.owns_lock());
// ...
}

void bar()
{
std::lock_guard<std::unique_lock<std::mutex>> lock(gLock);
//...
foo();
//...
}


Thoughts?

Answer

The standard is unclear about the validity of this construct.

The template parameter of std::lock_guard must meet BasicLockable requirements, i.e. it must have functions lock() and unlock().

std::unique_lock has these functions, which means the code compiles. However, they do not work as BasicLockable requires. unlock() should throw no exceptions, but std::unique_lock::unlock() can throw exceptions. According to this, using std::lock_guard<std::unique_lock<std::mutex>> should be undefined behavior.

However, as Holt points out in comments, the standard also says that unique_lock meets the BasicLockable requirements. So it is unclear whether the behavior is defined. If you can guarantee that unlock() does not throw (the mutex is not unlocked anywhere before lock_guard is destroyed), it will probably work in practice.