noexcept swap and move for classes with mutexes - c++

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).

Related

Is std::coroutine_handle thread safe in any way?

Are there any parts of std::coroutine_handle that are defined as thread safe in the standard?
I could for example see std::coroutine_handle::done() being implemented with an atomic variable, which would allow for completion checks without locking everything first.
But if nothing related to thread safety is defined in the standard then I would have to assume the worst case scenario and always lock everything.
None of the functions of coroutine_handle are specified to not provoke data races. Therefore, the standard library's common rules apply: concurrently calling any functions with an object provokes a data race on that object unless all potentially conflicting functions access the object via a const pointer/reference (like const members).
The observers, such as explicit operator bool() and done are both const and thus do not provoke a data race, unless other, non-const functions are being called. And of course, operator() and resume() are both non-const, and thus can provoke data races with the observers. However, the observers have the precondition that the handle in question is suspended, so you couldn't really do that anyway.
Really though, you shouldn't be trying to access a handle concurrently to begin with. The promise type should manage the handle for these scenarios, and any interaction between the future and the handle should happen through the promise. And if concurrent interaction is needed, the promise can provide it.

scoped_lock() - an RAII implementation using pthread

I have a socket shared between 4 threads and I wanted to use the RAII principle for acquiring and releasing the mutex.
The ground realities
I am using the pthread library.
I cannot use Boost.
I cannot use anything newer than C++03.
I cannot use exceptions.
The Background
Instead of having to lock the mutex for the socket everytime before using it, and then unlocking the mutex right afterwards, I thought I could write a scoped_lock() which would lock the mutex, and once it goes out of scope, it would automatically unlock the mutex.
So, quite simply I do a lock in the constructor and an unlock in the destructor, as shown here.
ScopedLock::ScopedLock(pthread_mutex_t& mutex, int& errorCode)
: m_Mutex(mutex)
{
errorCode = m_lock();
}
ScopedLock::~ScopedLock()
{
errorCode = m_unlock();
}
where m_lock() and m_unlock() are quite simply two wrapper functions around the pthread_mutex_lock() and the pthread_mutex_unlock() functions respectively, with some additional tracelines/logging.
In this way, I would not have to write at least two unlock statements, one for the good case and one for the bad case (at least one, could be more bad-paths in some situations).
The Problem
The problem that I have bumped into and the thing that I don't like about this scheme is the destructor.
I have diligiently done for every function the error-handling, but from the destructor of this ScopedLock(), I cannot inform the caller about any errors that might be returned my m_unlock().
This is a fundamental problem with RAII, but in this case, you're in luck. pthread_unlock only fails if you set up the mutex wrong (EINVAL) or if you're attempting to unlock an error checking mutex from a thread that doesn't own it (EPERM). These errors are indications of bugs in your code, not runtime errors that you should be taking into account. asserting errorCode==0 is a reasonable strategy in this case.

Exception handling for <mutex> and <condition_variable>

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.

What special purpose does unique_lock have over using a mutex?

I'm not quite sure why std::unique_lock<std::mutex> is useful over just using a normal lock. An example in the code I'm looking at is:
{//aquire lock
std::unique_lock<std::mutex> lock(queue_mutex);
//add task
tasks.push_back(std::function<void()>(f));
}//release lock
why would this preferred over
queue_mutex.lock();
//add task
//...
queue_mutex.unlock();
do these snippets of code accomplish the same thing?
[Do] these snippets of code accomplish the same thing?
No.
The first one will release the lock at the end of the block, no matter what the block is. The second will not release the lock at the end if the critical section is exited with a break, continue, return, goto, exception, or any other kind of non-local jump that I'm forgetting about.
The use of unique_lock offers resiliency in the face of changes and errors.
If you change the flow to add intermediate "jumps" (return for example)
If an exception is thrown
...
in any case, the lock is automatically released.
On the other hand, if you attempt to do it manually, you may miss a case. And even if you don't right now, a later edit might.
Note: this is a usual idiom in C++, referred to as SBRM (Scoped Bound Resources Management) where you tie down a clean-up action to stack unwinding so you are assured that, unless crash/ungraceful exit, it is executed.
It also shows off RAII (Resources Acquisition is Initialization) since the very construction of unique_lock acquires the resource (here the mutex). Despite its name, this acronym is also colloquially used to refer to deterministic release at destruction time, which covers a broader scope than SBRM since it refers to all kind of deterministic releases, not only those based on stack unwinding.

Could std::mutex::lock throw even if everything looks "good"?

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.