int main(){
std::mutex mut;
mut.lock();
cout<<"1111\n";
mut.lock();
cout<<"2222\n";
return 0;
}
Why does this code work and output 2222
? Shouldn't it block at the second lock()? In the mutex source code, the operation lock throws an exception. Shouldn't it block and wait? I use try{...}catch(exception& e){}
to catch this exception but it doesn't work.
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
It cannot. Your code has undefined behavior.
From cppreference:
If lock is called by a thread that already owns the mutex, the behavior is undefined: for example, the program may deadlock. An implementation that can detect the invalid usage is encouraged to throw a std::system_error with error condition resource_deadlock_would_occur instead of deadlocking.
Reading on, one might get the impression that calling lock
on the same thread would trigger an exception always:
Exceptions
Throws std::system_error when errors occur, including errors from the underlying operating system that would prevent lock from meeting its specifications. The mutex is not locked in the case of any exception being thrown.
However, there is not necessarily an exception when you call lock
in the same thread. Only if the implementation can detect such invalid use and only if the implementation is kind enough to actually throw an exception.
Looking into the standard we find:
1 The class mutex provides a non-recursive mutex with exclusive ownership semantics. If one thread owns a mutex object, attempts by another thread to acquire ownership of that object will fail (for try_lock()) or block (for lock()) until the owning thread has released ownership with a call to unlock().
2 [Note: After a thread A has called unlock(), releasing a mutex, it is possible for another thread B to lock the same mutex, observe that it is no longer in use, unlock it, and destroy it, before thread A appears to have returned from its unlock call. Implementations are required to handle such scenarios correctly, as long as thread A doesn't access the mutex after the unlock call returns. These cases typically occur when a reference-counted object contains a mutex that is used to protect the reference count. — end note ]
3 The class mutex meets all of the mutex requirements ([thread.mutex.requirements]). It is a standard-layout class ([class.prop]).
4 [Note: A program can deadlock if the thread that owns a mutex object calls lock() on that object. If the implementation can detect the deadlock, a resource_deadlock_would_occur error condition might be observed. — end note ]
5 The behavior of a program is undefined if it destroys a mutex object owned by any thread or a thread terminates while owning a mutex object.
It only says "can deadlock" and "might be observed" but otherwise it does not define what happens when you call lock
in the same thread.
PS Requiring to throw an excpetion in this case would make calling lock
more expensive without any real benefit. Calling lock
twice in the same thread is something that you should just not do. Actually you shouldn't call std::mutex::lock
directly at all, because manually releasing the lock is not exception safe:
// shared block - bad
{
mut.lock();
// ... code ...
mut.unlock();
}
If ...code..
throws an exception then mut
is never unlocked. The way to solve that is RAII and fortunately the standard library offers lots of RAII-helpers for locks, eg std::lock_guard
:
// shared block - good
{
std::lock_guard<std::mutex> lock(mut);
// ... code ...
}
TL;DR Don't do it.
If your code relies on that exception then you have more severe problems than not getting that exception.
You are working with a single thread. You should read on what a mutex really does and when/how it is used.
As of why it outputs 2222, it might as well output anything else or even make your neighborhood explode, since:
If lock is called by a thread that already owns the mutex, the behavior is undefined: for example, the program may deadlock.
https://en.cppreference.com/w/cpp/thread/mutex/lock
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