Skip to content

[libc++] False positive -Wthread-safety-analysis with std::lock() #151733

@rupprecht

Description

@rupprecht

After #117497, using std::lock to acquire a mutex (i.e. not calling .lock() on the mutex itself) appears to not register that it acquires a lock, which can lead to erroneous thread safety warnings later that it needs to be held.

Live example: https://godbolt.org/z/vTrfK1f1r

void func() {
    std::mutex a, b;
    std::lock(a, b);
    // error: calling function 'lock_guard' requires holding mutex 'a' exclusively [-Werror,-Wthread-safety-analysis]
    std::lock_guard<std::mutex> a_guard(a, std::adopt_lock);
    // error: calling function 'lock_guard' requires holding mutex 'b' exclusively [-Werror,-Wthread-safety-analysis]
    std::lock_guard<std::mutex> b_guard(b, std::adopt_lock);
}

Real example in LLDB:

if (this != &rhs) {
std::lock(m_collection_mutex, rhs.m_collection_mutex);
std::lock_guard<std::mutex> lhs_guard(m_collection_mutex, std::adopt_lock);
std::lock_guard<std::mutex> rhs_guard(rhs.m_collection_mutex, std::adopt_lock);
m_break_loc_collection = rhs.m_break_loc_collection;
}

Also, use of this pattern when needing to acquire more than one lock is suggested at https://en.cppreference.com/w/cpp/thread/lock.html#Example

Here's a suggested fix that works for the breakages I've seen:

--- a/libcxx/include/mutex
+++ b/libcxx/include/mutex
@@ -351,7 +351,7 @@
 #    endif // _LIBCPP_CXX03_LANG

 template <class _L0, class _L1>
-_LIBCPP_HIDE_FROM_ABI void lock(_L0& __l0, _L1& __l1) {
+_LIBCPP_HIDE_FROM_ABI void lock(_L0& __l0, _L1& __l1) _LIBCPP_ACQUIRE_CAPABILITY(__l0, __l1) {
   while (true) {
     {
       unique_lock<_L0> __u0(__l0);

Metadata

Metadata

Assignees

No one assigned

    Labels

    libc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions