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.
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
In general it is a good practice to declare a swap and move noexcept as that allows to provide some exception guarantee.
At the same time writing a thread-safe class often implies adding a mutex protecting the internal resources from races.
If I want to implement a swap function for such a class the straightforward solution is to lock in a safe way the resources of both arguments of the swap and then perform the resource swap as, for example, clearly answered in the answer to this question: Implementing swap for class with std::mutex .
The problem with such an algorithm is that a mutex lock is not noexcept, therefore swap cannot, strictly speaking, be noexcept. Is there a solution to safely swap two objects of a class with a mutex?
The only possibility that comes to my mind is to store the resource as a handle so that the swap becomes a simple pointer swap which can be done atomically.
Otherwise one could consider the lock exceptions as unrecoverable error which should anyway terminate the program, but this solution feels like just a way to put the dust under the carpet.
EDIT:
As came out in the comments, I know that the exceptions thrown by the mutexes are not arbitrary but then the question can be rephrased as such:
Are there robust practices to limit the situation a mutex can throw to those when it is actually an unrecoverable OS problem?
What comes to my mind is to check, in the swap algorithm, whether the two objects to swap are not the same. That is a clear deadlock situation which will trigger an exception in the best case scenario but can be easily checked for.
Are there other similar triggers which one can safely check to make a swap function robust and practically noexcept for all the situation that matter?
On POSIX systems it is common for std::mutex to be a thin wrapper around pthread_mutex_t, for which lock and unlock function can fail when:
There is an attempt to acquire already owned lock
The mutex object is not initialized or has been destroyed already
Both of the above are UB in C++ and are not even guaranteed to be returned by POSIX. On Windows both are UB if std::mutex is a wrapper around SRWLOCK.
So it seems that the main point of allowing lock and unlock functions to throw is to signal about errors in program, not to make programmer expect and handle them.
This is confirmed by the recommended locking pattern: the destructor ~unique_lock is noexcept(true), but is supposed to call unlock which is noexcept(false). That means if exception is thrown by unlock function, the whole program gets terminated by std::terminate.
The standard also mentions this:
The error conditions for error codes, if any, reported by member
functions of the mutex types shall be:
(4.1) — resource_unavailable_try_again — if any native handle type
manipulated is not available.
(4.2) — operation_not_permitted — if the thread does not have the
privilege to perform the operation.
(4.3) — invalid_argument — if any native handle type manipulated as
part of mutex construction is incorrect
In theory you might encounter operation_not_permitted error, but situations when this happens are not really defined in the standard.
So unless you cause UB in your program related to the std::mutex usage or use the mutex in some OS-specific scenario, quality implementations of lock and unlock should never throw.
Among the common implementations, there is at least one that might be of low quality: std::mutex implemented on top of CRITICAL_SECTION in old versions of Windows (I think Windows XP and earlier) can throw after failing to lazily allocate internal event during contention. On the other hand, even earlier versions allocated this event during initialization to prevent failing later, so std::mutex::mutex constructor might need to throw there (even though it is noexcept(true) in the standard).
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?
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.
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.
Assuming
no undefined behaviour occurs,
no deadlocks occur,
mutexes are locked and unlocked in the correct order by the correct threads the correct number of times,
non-recursive mutexes are not locked multiple times,
locking recursive mutexes does not exceed the maximum level of ownership,
no predicates passed to condition variables throw, and
only clocks, time points, and durations provided by the standard library are used with the std:: mutexes and condition variables
is it guaranteed that operating on the different types of std:: mutexes and condition variables (other than on constructing them) does not throw any exceptions (especially of type std::system_error)?
For example, in case of methods like:
void MyClass::setVariable() {
std::lock_guard<std::mutex> const guard(m_mutex);
m_var = 42; // m_var is of type int
m_conditionVariable.notify_all();
}
void MyClass::waitVariable() {
std::unique_lock<std::mutex> lock(m_mutex);
m_conditionVariable.wait(lock, [this]() noexcept { return m_var == 42; });
}
Is it safe to assume noexcept or should one write some try-catch blocks around the callsites? Or are there any caveats?
Please consider all types of mutexes and condition variables in C++11, C++14 and later.
Short answer: No (sorry)
Any of these operations will throw std::system_error if the underlying synchronisation object fails to perform its operation.
This is because correct operation of synchronisation primitives depends on:
available system resources.
some other part of the program not invalidating the primitive
Although in fairness, if (1) is happening it's probably time to redesign the application or run it on a less-loaded machine.
And if (2) is happening, the program is not logically consistent.
That being said,
or should one write some try-catch blocks around the callsites?
Also no.
You should write try/catch blocks under the following conditions:
Where the program is in a position to do something useful about the error condition (such as repairing it or asking the user if he wants to try again)
You would like to add some information to the error and re-throw it in order to provide a diagnostic breadcrumb trail (nested exceptions, for example)
You wish to log the failure and carry on.
Otherwise, the whole point of c++ exception handling is that you allow RAII to take care of resource reacquisition and allow the exception to flow up the call stack until is finds a handler that wants to handle it.
example of creating a breadcrumb trail:
void wait_for_object()
try
{
_x.wait(); // let's say it throws a system_error on a loaded system
}
catch(...)
{
std::throw_with_nested(std::runtime_error(__func__));
}
Thank's to the link T.C. provided now I'd say yes — your code should be safe. Since in the future standard device_or_resource_busy will be removed and as the author of the issue says that this situation can't occur in any reasonable way then there are only 2 possibilities for lock to throw:
(13.1) — operation_not_permitted — if the thread does not have the
privilege to perform the operation.
(13.2) — resource_deadlock_would_occur — if the implementation detects
that a deadlock would occur.
And both of these situations are excluded by your preconditions. So your code should be safe to use noexcept.
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.