<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/63239>63239</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Thread safety warning when conditionally releasing a `unique_lock`
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
greg7mdp
</td>
</tr>
</table>
<pre>
When conditionally releasing a `unique_lock`, the unique_lock destructor warns with:
`mutex 'm' is not held on every path through here [-Wthread-safety-analysis]`
however hitting the destructor with an unlocked mutex is expected with `unique_lock` which supports `unlock()`.
In the doc, it says explicitly:
```c++
// Release *this and all associated mutexes, if they are still held.
// There is no warning if the scope was already unlocked before.
~MutexLocker() RELEASE() {
if (locked)
mut->GenericUnlock();
}
```
but I do get a warning if the scope was unlocked before.
Here is a self-contained program reproducing the issue when compiled with `clang++-17`:
```
// compile with: clang++-17 -Wthread-safety tsa.cpp
// warning:
// tsa.cpp:119:1: warning: mutex 'm' is not held on every path through here [-Wthread-safety-analysis]
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define RELEASE_GENERIC(...) THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#include <mutex>
#include <shared_mutex>
// Defines an annotated interface for mutexes.
// These methods can be implemented to use any internal mutex implementation.
class CAPABILITY("mutex") mutex {
private:
std::mutex mutex_;
public:
// Acquire/lock this mutex exclusively. Only one thread can have exclusive
// access at any one time. Write operations to guarded data require an
// exclusive lock.
void lock() ACQUIRE() { mutex_.lock(); }
// Release/unlock an exclusive mutex.
void unlock() RELEASE() { mutex_.unlock(); }
// Try to acquire the mutex. Returns true on success, and false on failure.
bool try_lock() TRY_ACQUIRE(true) { return mutex_.try_lock(); }
};
// Tag types for selecting a constructor.
struct adopt_lock_t {} inline constexpr adopt_lock = {};
struct defer_lock_t {} inline constexpr defer_lock = {};
struct shared_lock_t {} inline constexpr shared_lock = {};
struct try_to_lock_t {} inline constexpr try_to_lock = {};
// unique_lock is an RAII class that acquires a mutex in its constructor, and
// releases it in its destructor.
template <typename M>
class SCOPED_CAPABILITY unique_lock {
private:
M* mut;
bool locked;
public:
unique_lock() noexcept
: mut(nullptr)
, locked(false) {}
// Assume mu is held, implicitly acquire *this and associate it with mu.
unique_lock(M& mu, adopt_lock_t) REQUIRES(mu)
: mut(&mu)
, locked(true) {}
// Release *this and all associated mutexes, if they are still held.
// There is no warning if the scope was already unlocked before.
~unique_lock() RELEASE() {
if (locked)
mut->unlock();
}
// Acquire all associated mutexes exclusively.
void lock() ACQUIRE() {
mut->lock();
locked = true;
}
// Release all associated mutexes. Warn on double unlock.
void unlock() RELEASE() {
mut->unlock();
locked = false;
}
};
mutex m;
void release_mutex(int i) RELEASE(m) {
unique_lock l(m, adopt_lock);
if (i)
l.unlock();
}
int main() {
m.lock();
release_mutex(1);
return 0;
}
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy0WU1z4rrS_jXOpisUEQmBBQsnkBmqZpJ5gbxzZ-USdoN1ryz5SHISNue339KHwXwkQHJuKkUKW3qe7qellrpDtWZLgTiIbu6im-EFrUwu1WCpcHlbZOXFXGarwe8cBaRSZMwwKSjnK1DIkWomlkAh6rYrwf6qMOEy_U_UbUfkHkyO0HgKGWqjqtRIBa9UCQ2vzORRJ47aw6gdPi1SURl8g4jcFhG5BaZBSAM58gykAHxBtYKSmhxMrmS1zCFHhRDd3F3-NrlCml1qukCzuqSC8pVmOroZWosaLLl8tTiQM2OsA9bSpnXM5EAFVMLajRl4i5gGfCsxNZj5IXtew2vO0hx0VZZSGe0HuHekF5F-1G23oGnHWHhqmVq9mAFNV46Es5QZvtoRx3rhftOI3NlfLxlE5CEiDzBxAUGISGxypoGKDCjnQLWWKaOmdgS1Y1tY7hVQhaAN49xJ3NrBnDlxXQxc0KxafiboVJYIr1QD5Vb21UavOS6kwjXW3z8t7Q_7TnklYDL6MYqno_Atul37AhY-Ij2PZEXzL4rKXEad0TcUqFj63FS1sxHidrijVFO-eWVgDJmEJRqg77tz2A3_-T3IQUEjX1ymUhjKBGZQKrlUtACFpZJZldbLimldIbz67VOUjDcWT8qpWPpQXl7dWmvfiXf46kMSYOrdA9sosLMLwGjaSstyCyK4vqHzj-uhnfjqqm8_LfxmLPwvNmbg77AFZLiwUkaklyTOqSRxi4N0I9IFF-yrzZjp7_E3G_16hUSk41_C7PtkFA-T-PHxaRbPxk-PSTybTcZ3z7ORRey9WdQkocYoNq8MuofheRMOucZPYQc5hbyU5QZNZGyxFd016H38K74b_xjP_tQIx2hSWtI548ys9uzewZ7eP_0aDZMNxVFstxEyl9LonOO7yN-e48lwNEzuTrZ6WVGVYZbMj1r9a5acD1-a5HSG-P7_nscTiz96eJrYRNRqtU5hoelfFVOWw2UHt1r_P07iybepW6_HGeOH2WjyCUK6MC5_nsg3GTm-6elMCh2TTraW15l0yfR7PBkNP8Gqc2rd_Ax50PZsUb9CdrajNecX_NwcmyeL6-4EXyH7REA959f9TL6NHkeT8f353Et_T_gU-WzyJzl7QRm1Sr6yqBqkZwve5P6C6KN_3f94Hp6TMOwZoRN8S3mVhYP7tB00nY4ms-T8c49qjcokpx9_gckr-nnCQ6p-yDsZzZ4nj58gtJImCk2l_DXnQ5bHpyTgTeOH0exPEj_GP_5Mx9OjNEIm_k6W-DtZsr6T7XEx4cILUefeXQCjzujQuyDRzpDm9XLojLZ1CVAhpHEVCRMG1YKmCAup6vqktTVvlqNGKNDkMtOQUgFzBFaUHAsUFsNIqDQCFSsPJyivS7Z6FLVla4BNOdV6-9IVEeINJ8QGKVx066qkVOyFGlxfl0GbzH7pxH6g-0zWdUiYVM05Szdz1iVV7HdqRB5cVexKNY_jNpJmL8hXLYAnwVcgBYIPlHM8py-4GbaLTNMUtQZqnBRuKiuwBfBbMYMgS1ROB20VC1clyKihEM5goGIXc00G1tp1SQcvkmWwKcIaJ3Ao6IIqra1CrVGh7fCE4jUiD778sqtkw-2wdsiblfV-PVnTV-JEA2ZqZWUJedTVbp4VYOK2owajKrSljq6c0LaKtjX2gnLtni8o41Wj7oW5lBxsdm5Yun26WMjaYr_ra8O3p22bfjvcWWy1D3QJZlWidptJI8fU-AZNKkXd3gjm-a9AM1kaR5QYt-Jvh8AEt9nFzcG3UjUGQdQZhmFrEwJShgtUx5A2gz5ACqnkCFRj1AdYVkYjj2E1Rh3C2hK52dJyXRaYxOMx-Kxicrv7_BrSQOs0JIAZ3QxCWDtbwOHuooGZesamKRWiZrAoOTUu49pIC1og_FznW2_Efs3XtPn9tAbwMyL2T1GZRlfFL-PQkPk4yTXbYX65C4lvKZZm098BCL0EewxVnJdGbdo8_j25r-lIz-2uukn03uaNta4Ku2FtRHLkmetwFXUfbb2rt1pjdVvM6u1aMkW12brbnvyMSBeKykWtsWF85lmXWXZA_7CjEenuv2262UgEx3LkP9Pg-0c7fPD3fuTfa_Ed7u_VP6HPVx1u8L2fvsOp-o4eW0fr6WfYdu_xsEUAIYgucbgwnmJvHcvD9rbgN1XCnimZrOYcg_BnnYBNC4-ouu2D33EHndjLieECtPPUWVeXYuFi1WPCANu2tNg1tZmnuB_Q3HE7ZvuVxHYWEd898ze2N0y01hSUif1gtw5rtOvO1d57d3q3DxDWbdyLbNDJ-p0-vcDBVbfXvSbX7Xb3Ih-QG9ruY3fevk57bdIh14TepDd4fd2mPUL76QUbkDbptLtX7at-u3vdbWX9215GCOl22_NORtLouo0FZbzF-UvRkmp54RrPg26HdPoXnM6Ra_cPHkIEvvqutL3s3gwv1MDOuZxXSx1dtznTRm9QDDMcBzN_Aw0d5TpLvJ75P6GLSvFBbkyp7YHhtsGSmbyat1JZ2Oswf6n_XJZK_htTE5EHZ6qOyINz5b8BAAD__woi-xc">