2023-05-03 11:06:20 +02:00
== Why is this an issue?
2023-05-19 16:58:24 +02:00
_Mutexes_ are synchronization primitives that allow the managing of concurrency. It is a common situation to have to use multiple _mutexes_ to protect multiple resources with different access patterns.
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
In such a situation, it is crucial to define an order on the set of all _mutexes_:
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
* This order should be strictly followed when _locking_ _mutexes_.
* The reverse order should be strictly followed when _unlocking_ _mutexes_.
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
Failure to do so can lead to _deadlocks_. i.e., situations where two or more threads are blocked forever, each holding one mutex and waiting for one held by the other(s).
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
In {cpp}, an easy way to make sure the unlocks are called in reverse order from the lock is to wrap the lock/unlock operations in an RAII class (since destructors of local variables are called in reverse order of their creation).
2021-04-28 16:49:39 +02:00
If instead of ``++pthread_mutex_t++`` you are using ``++std::mutex++``, there are other mechanisms that allow you to avoid deadlocks in that case, see S5524.
2023-05-19 16:58:24 +02:00
== How to fix it
2021-04-28 18:08:03 +02:00
2023-05-19 16:58:24 +02:00
Reorder locking and unlocking operations to always lock in the same order and unlock in the reverse order.
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
=== Code examples
==== Noncompliant code example
[source,c]
2021-04-28 16:49:39 +02:00
----
2023-05-19 16:58:24 +02:00
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
void thread_safe_operation(void) {
2021-04-28 16:49:39 +02:00
pthread_mutex_lock(&mtx1);
pthread_mutex_lock(&mtx2);
2023-05-19 16:58:24 +02:00
use_resources();
pthread_mutex_unlock(&mtx1); // Noncompliant
2021-04-28 16:49:39 +02:00
pthread_mutex_unlock(&mtx2);
}
----
2021-04-28 18:08:03 +02:00
2023-05-19 16:58:24 +02:00
==== Compliant solution
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
C solution:
[source,c]
2021-04-28 16:49:39 +02:00
----
2023-05-19 16:58:24 +02:00
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
2021-04-28 16:49:39 +02:00
2023-05-19 16:58:24 +02:00
void thread_safe_operation(void) {
2021-04-28 16:49:39 +02:00
pthread_mutex_lock(&mtx1);
pthread_mutex_lock(&mtx2);
2023-05-19 16:58:24 +02:00
use_resources();
2021-04-28 16:49:39 +02:00
pthread_mutex_unlock(&mtx2);
pthread_mutex_unlock(&mtx1);
}
----
2021-04-28 18:08:03 +02:00
2023-05-19 16:58:24 +02:00
{cpp}03 solution:
[source,cpp]
----
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
struct MutexLocker {
MutexLocker(pthread_mutex_t* mtx) : mtx(mtx) {
pthread_mutex_lock(mtx);
}
~MutexLocker() {
pthread_mutex_unlock(mtx);
}
pthread_mutex_t* mtx;
};
struct ResourcesLocker {
ResourcesLocker() : m1(&mtx1), m2(&mtx2) {}
MutexLocker m1;
MutexLocker m2;
};
void thread_safe_operation(void) {
ResourcesLocker locker;
use_resources();
}
----
{cpp}11 and {cpp}14 solution:
[source,cpp]
----
std::mutex m1;
std::mutex m2;
void thread_safe_operation(void) {
std::lock(m1, m2);
std::lock_guard<std::mutex> lck1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lck2(m2, std::adopt_lock);
use_resources();
}
----
{cpp}17 and after:
[source,cpp]
----
std::mutex m1;
std::mutex m2;
void thread_safe_operation(void) {
2023-08-02 16:34:08 +02:00
std::scoped_lock lck1(m1, m2);
2023-05-19 16:58:24 +02:00
use_resources();
}
----
2021-04-28 18:08:03 +02:00
2021-06-02 20:44:38 +02:00
2021-06-03 09:05:38 +02:00
ifdef::env-github,rspecator-view[]
2021-06-08 15:52:13 +02:00
'''
2021-06-02 20:44:38 +02:00
== Comments And Links
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== relates to: S5486
=== relates to: S5487
=== relates to: S5524
=== is related to: S5486
=== is related to: S5487
=== on 6 Nov 2019, 23:54:57 Loïc Joly wrote:
\[~geoffray.adde]
* Can you please review my changes?
* It's not clear to me if this rule is supposed to detect that in on place mtx1 is locked before mtx2, and in another place the order is reversed?
2021-06-03 09:05:38 +02:00
endif::env-github,rspecator-view[]