I was wondering what happens when you move a unique_lock
that holds a recursive_mutex
.
Specifically, I was looking at this code:
recursive_mutex g_mutex;
#define TRACE(msg) trace(__FUNCTION__, msg)
void trace(const char* function, const char* message)
{
cout << std::this_thread::get_id() << "\t" << function << "\t" << message << endl;
}
future<void> foo()
{
unique_lock<recursive_mutex> lock(g_mutex);
TRACE("Owns lock");
auto f = std::async(launch::async, [lock = move(lock)]{
TRACE("Entry");
TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock!
this_thread::sleep_for(chrono::seconds(3));
});
TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Doesn't own lock!
return f;
}
int main()
{
unique_lock<recursive_mutex> lock(g_mutex);
TRACE("Owns lock");
auto f = foo();
TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock!
f.wait();
TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock!
}
The output of this sample code surprised me a lot. How does the unique_lock
in main() know that the thread released the mutex? Is it real?
You appear to ascribe some magic properties to unique_lock
. It doesn't have any, it's a very simple class. It has two data members, Mutex* pm
and bool owns
(member names shown for exposition only). lock()
is simply pm->lock(); owns = true;
, and unlock
does pm->unlock(); owns = false;
. The destructor is if (owns) unlock();
. Move constructor copies over the two members, and sets them in the original to nullptr
and false
, correspondingly. owns_lock()
returns the value of owns
member.
All the thread-synchronizing magic is in the mutex itself, and its lock()
and unlock()
methods. unique_lock
is merely a thin wrapper around it.
Now, the thread that calls mutex.unlock()
must, as a prerequisite, hold the mutex (meaning, that thread has previously called lock()
on it), or else the program exhibits undefined behavior. This is true whether you call unlock
explicitly, or trick some helper like unique_lock
into calling it for you.
In light of all this, moving a unique_lock
instance over to another thread is merely a recipe for triggering undefined behavior shortly thereafter; there is no upside.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With