Reputation: 23
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?
Upvotes: 2
Views: 466
Reputation: 1476
This appears to be valid, according to the current C++ standard draft, and the same text applies in C++11 with only one paragraph-numbering change.
The thing passed as the template argument to std::lock_guard
must be a BasicLockable
.
BasicLockable
specifies two things:
lock()
, which has no requirements or limitations beyond either acquiring the lock or throwing an exception.unlock()
, which can only be called if the lock is held, cannot fail, and cannot throw an exception.unique_lock::lock
appears to satisfy the first clause, but see below the separator line.
unique_lock::unlock
satisfies the requirements of the second clause, on the basis that the only circumstance under which unique_lock::unlock
may throw is if the lock is not currently held, and BasicLockable
requires that the lock is held when unlock
is called.
This matches the only response I received when I posited this reading to the isocpp.org std-discussion mailing list.
If the unique_lock
is already held, it appears that the requirement for BasicLockable
is violated, as the current execution agent holds the lock, and an exception is thrown. Since the exact wording is
If an exception is thrown then a lock shall not have been acquired for the current execution agent.
one could argue that the lock wasn't acquired, as it was already held.
Upvotes: 0
Reputation: 10155
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.
Upvotes: 1