I wrote the following code to test my understanding of std::mutex
int main() {
mutex m;
m.lock();
m.lock(); // expect to block the thread
}
And then I got a system_error: device or resource busy. Isn't the second m.lock() supposed to block the thread?
From std::mutex:
A calling thread must not own the mutex prior to calling lock or try_lock.
and from std::mutex::lock:
If lock is called by a thread that already owns the mutex, the program may deadlock. Alternatively, if an implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be observed.
and the exceptions clause:
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.
Therefore it is not supposed to block the thread. On your platform, the implementation appears to be able to detect when a thread is already the owner of a lock and raise an exception. This may not happen on other platforms, as indicated in the descriptions.
Isn't the second m.lock() supposed to block the thread?
No, it gives undefined behaviour. The second m.lock() breaks this requirement:
C++11 30.4.1.2/7 Requires: If m is of type std::mutex or std::timed_mutex, the calling thread does not own the mutex.
It looks like your implementation is able to detect that the calling thread owns the mutex and gives an error; others may block indefinitely, or fail in other ways.
(std::mutex wasn't mentioned in the question when I wrote this answer.)
It depends on the mutex library and mutex type you're using - you haven't told us. Some systems provide a "recursive mutex" that is allowed to be called multiple times like this only if it happens from the same thread (then you must have a matching number of unlocks before another thread can lock it), other libraries consider this an error and may fail gracefully (as yours has) or have undefined behaviour.
Related
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
I wrote some code before but I realized it has a very bad code style so I changed it.
I changed the A.unlock inside of the if block. I know if the if never run then this thread will unlock the mutex which does not belong to itself and then it will return an undefined behavior.
My question is, if it returns an undefined behavior, will the logic here still work? Because if thread t1 didn't have the lock, t1 unlock the mutex A will return undefine behavior and the mutex will still be held by the thread which holds it right? And it will not affect the other logic in this code.
My old code works as same as I put the unlock part inside of the if block. So that's why I am curious how can this work.
mutex A;
if(something)
{
A.lock();
}
A.unlock();
When calling unlock on a mutex, the mutex must be owned by the current thread or the behavior is undefined. Undefined behavior means anything can happen, including the program appearing to run correctly, the program crashing, or memory elsewhere getting corrupted and a problem not being visible until later.
Normally a mutex is not used directly; one of the other standard classes (like std::unique_lock or std::lock_guard are used to manage it. Then you won't have to worry about unlocking the mutex.
You should consider using std::lock_guard :
mutex A;
if (something)
{
lock_guard<mutex> lock(A);
}
It is an undefined behavior to unlock a mutex you haven't locked. It may work on some cases for so many times but some day it will break and behave differently.
You can use a lock_guard in your case and forget about lock/unlock
std::lock_guard<std::mutex> lock(A);
The ISO C++ standard has this to say on the matter, in [thread.mutex.requirements.mutex] (my emphasis):
The expression m.unlock() shall be well-formed and have the following semantics:
Requires: The calling thread shall own the mutex.
That means what you are doing is a violation of the standard and is therefore undefined. It may work, or it may delete all your files while playing the derisive_maniacal_laughter.mp3 file :-)
Bottom line, don't do it.
I also wouldn't put the unlock inside the loop since modern C++ has higher-level mechanisms for dealing with auto-release of resources. In this case, it's a lock guard:
std::mutex mtxProtectData; // probably defined elsewhere (long-lived)
: : :
if (dataNeedsChanging) {
std::lock_guard<std::mutex> mtxGuard(mtxProtectData);
// Change data, mutex will unlock at brace below
// when mtxGuard goes out of scope.
}
As per this article:
If you try and lock a non-recursive mutex twice from the same thread without unlocking in between, you get undefined behavior.
My very naive mind tells me why don't they just return an error? Is there a reason why this has to be UB?
Because it never happens in a correct program, and making a check for something that never happens is wasteful (and to make that check it needs to store the owning thread ID, which is also wasteful).
Note that it being undefined allows debug implementations to throw an exception, for example, while still allowing release implementations to be as efficient as possible.
Undefined behavior allows implementations to do whatever is fastest/most convenient. For example, an efficient implementation of a non-recursive mutex might be a single bit where the lock operation is implemented with an atomic compare-and-swap instruction in a loop. If the thread that owns the mutex tries to lock it again it will deadlock because it is waiting for the mutex to unlock but since nobody else can unlock it (unless there's some other bug where some thread that doesn't own it unlocks it) the thread will wait forever.
From CPPReference, it isn't said explicitly that the lock function of std::mutex won't throw if the lock won't result in a dead lock.
PThread's lock only have a deadlock error. I don't know for window's implementation of thread. I also don't know if they are other implementation of thread used as backend of std::thread/std::mutex.
So my question is "Should I write my code as if, some times, for no special reason, a lock may fail?".
I actually need to lock a mutex in some noexcept methods, and I want to be sure that they are noexcept.
The std::mutex::lock() member function is not declared as noexcept and from section 30.4.1.2 Mutex types of the c++11 standard (draft n3337), clause 6:
The expression m.lock() shall be well-formed and have the following semantics:
...
Throws: system_error when an exception is required (30.2.2).
Error conditions:
operation_not_permitted — if the thread does not have the privilege to perform the operation.
resource_deadlock_would_occur — if the implementation detects that a deadlock would occur.
device_or_resource_busy — if the mutex is already locked and blocking is not possible.
This implies that any function that uses mutex::lock() cannot be marked noexcept, unless that function is capable of handling the exception itself and prevents it from propogating to the caller.
I am unable to comment on the likelihood of these error conditions occuring, but in relation to std::mutex and resource_deadlock_would_occur (which might be thrown) it indicates a bug in the code as opposed to a runtime a failure as this error might be raised if a thread attempts to lock a std::mutex it already owns. From section 30.4.1.2.1 Class mutex, clause 4:
[ Note: A program may 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 may be observed. —end note ]
By selecting std::mutex as the lock type the programmer is explicitly stating that an attempt by the same thread to lock a mutex it already has locked is not possible.
It if is a legal path of execution for a thread to re-lock a mutex then a std:recursive_mutex is the more appropriate choice (but changing to a recursive_lock does not mean the lock() function is exception free).
On a POSIX system, std::mutex will probably be implemented using POSIX mutexes, and std::mutex::lock() will eventually delegate to pthread_mutex_lock(). Although C++ mutexes are not required to be implemented using POSIX mutexes, the authors of the C++ standard multi-threading seem to have modelled the possible error conditions on the POSIX error conditions, so examining those can be instructive. As user hmjd says, the C++ error conditions permitted for the lock method are operation_not_permitted, resource_deadlock_would_occur and device_or_resource_busy.
The POSIX error conditions are:
EINVAL: if a POSIX specific lock-priorty feature is misused, which can never happen if you use only the standard C++ multi-threading facilities. This case might correspond to the operation_not_permitted C++ error code.
EINVAL: if the mutex has not been initialized, which would correspond to a corrupted std::mutex object, use of a dangling reference, or some other undefined behaviour that indicates a program bug.
EAGAIN: if the mutex is recursive and the recursion is too deep. That can't happen to a std::mutex, but could happen for a std::recursive_mutex. This would seem to correspond to the device_or_resource_busy error condition.
EDEADLK: if deadlock would occur because of the thread already holds the lock. This would correspond to the resource_deadlock_would_occur C++ error code, but would indicate a program bug, because a program should not attempt to lock a std::mutex it already holds a lock on (use a std::recursive_mutex if you really want to do that).
The C++ operation_not_permitted error code is evidently intended to correspond to the POSIX EPERM error status. The pthread_mutex_lock() function never gives this status code. But the POSIX manual page that describes that function also describes the pthread_mutex_unlock() function, which may given EPERM if you try to unlock a lock you have not locked. Perhaps the C++ standards authors included operation_not_permitted by a mistaken reading of the POSIX manual page. As C++ has no concept of lock "permissions", it is hard to see how any correctly constructed and manipulated lock (used in accordance with the C++ standard, without invoking any undefined behaviour) could result in EPERM and thus operation_not_permitted.
device_or_resource_busy is not permitted from C++17, which suggests it never really happens in practice, and its inclusion for C++11 was an error.
To summarize, the only cases in which std::mutex::lock() could throw an exception indicate program bugs. So it can be reasonable to assume the method "never" throws an exception.
It's safe to assume that the mutex won't throw if you can guarantee that none of the error conditions (as outlined in hmjd's answer) are present. How to put that call into a noexcept function depends on how you want to handle an (pretty impossible) failure. If the default of noexcept (to call std::terminate is acceptable, you don't need to do anything. If you want to do log the impossible error, wrap the function in a try/catch clause.
As per this article:
If you try and lock a non-recursive mutex twice from the same thread without unlocking in between, you get undefined behavior.
My very naive mind tells me why don't they just return an error? Is there a reason why this has to be UB?
Because it never happens in a correct program, and making a check for something that never happens is wasteful (and to make that check it needs to store the owning thread ID, which is also wasteful).
Note that it being undefined allows debug implementations to throw an exception, for example, while still allowing release implementations to be as efficient as possible.
Undefined behavior allows implementations to do whatever is fastest/most convenient. For example, an efficient implementation of a non-recursive mutex might be a single bit where the lock operation is implemented with an atomic compare-and-swap instruction in a loop. If the thread that owns the mutex tries to lock it again it will deadlock because it is waiting for the mutex to unlock but since nobody else can unlock it (unless there's some other bug where some thread that doesn't own it unlocks it) the thread will wait forever.