The C++ Standard Library supplies both recursive and non-recursive mutex classes used to protect critical sections. The recursive mutex classes (std::recursive_mutex
and std::recursive_timed_mutex
) differ from the non-recursive mutex classes (std::mutex
, std::timed_mutex
, and std::shared_timed_mutex
) in that a recursive mutex may be locked recursively by the thread that currently owns the mutex. All mutex classes support the ability to speculatively lock the mutex through functions such as try_lock()
, try_lock_for()
, try_lock_until()
, try_lock_shared_for()
, and try_lock_shared_until()
. These speculative locking functions attempt to obtain ownership of the mutex for the calling thread, but will not block in the event the ownership cannot be obtained. Instead, they return a Boolean value specifying whether the ownership of the mutex was obtained or not.
The C++ Standard, [thread.mutex.requirements.mutex], paragraphs 14 and 15 [ISO/IEC 14882-2014], state the following:
The expression
m.try_lock()
shall be well-formed and have the following semantics:
Requires: Ifm
is of typestd::mutex
,std::timed_mutex
, orstd::shared_timed_mutex
, the calling thread does not own the mutex.
Further, [thread.timedmutex.class], paragraph 3, in part, states the following:
The behavior of a program is undefined if:
— a thread that owns atimed_mutex
object callslock()
,try_lock()
,try_lock_for()
, ortry_lock_until()
on that object
Finally, [thread.sharedtimedmutex.class], paragraph 3, in part, states the following:
The behavior of a program is undefined if:
— a thread attempts to recursively gain any ownership of ashared_timed_mutex
.
Thus, attempting to speculatively lock a non-recursive mutex object that is already owned by the calling thread is undefined behavior. Do not call try_lock()
, try_lock_for()
, try_lock_until()
, try_lock_shared_for()
, or try_lock_shared_until()
on a non-recursive mutex object from a thread that already owns that mutex object.
Noncompliant Code Example
In this noncompliant code example, the mutex m
is locked by the thread's initial entry point and is speculatively locked in the do_work()
function from the same thread, resulting in undefined behavior because it is not a recursive mutex. With common implementations, this may result in deadlock.
#include <mutex> #include <thread> std::mutex m; void do_thread_safe_work(); void do_work() { while (!m.try_lock()) { // The lock is not owned yet, do other work while waiting. do_thread_safe_work(); } try { // The mutex is now locked; perform work on shared resources. // ... // Release the mutex. catch (...) { m.unlock(); throw; } m.unlock(); } void start_func() { std::lock_guard<std::mutex> lock(m); do_work(); } int main() { std::thread t(start_func); do_work(); t.join(); }
Compliant Solution
This compliant solution removes the lock from the thread's initial entry point, allowing the mutex to be speculatively locked, but not recursively.
#include <mutex> #include <thread> std::mutex m; void do_thread_safe_work(); void do_work() { while (!m.try_lock()) { // The lock is not owned yet, do other work while waiting. do_thread_safe_work(); } try { // The mutex is now locked; perform work on shared resources. // ... // Release the mutex. catch (...) { m.unlock(); throw; } m.unlock(); } void start_func() { do_work(); } int main() { std::thread t(start_func); do_work(); t.join(); }
Risk Assessment
Speculatively locking a non-recursive mutex in a recursive manner is undefined behavior that can lead to deadlock.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON56-CPP | Low | Unlikely | High | P1 | L3 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
CodeSonar | 8.1p0 | CONCURRENCY.TL | Try-lock that will never succeed |
Helix QAC | 2024.2 | C++4986, C++4987 | |
Parasoft C/C++test | 2023.1 | CERT_CPP-CON56-a | Avoid double locking |
Polyspace Bug Finder | R2024a | CERT C++: CON56-CPP | Checks for attempt to lock mutex that is already owned by calling thread (rule fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[ISO/IEC 14882-2014] | Subclause 30.4.1, "Mutex Requirements" |