Skip to content

TSan seems to ignore std::shared_timed_mutex #142370

Open
@mhier

Description

@mhier

The ThreadSanitizer seems to ignore std::shared_timed_mutexes which have been locked via try_lock_for() and consequently reports false-positive data races.

Here is a minimal but complete code example:

#include <unistd.h>

#include <iostream>
#include <shared_mutex>
#include <thread>

int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
  std::shared_timed_mutex mx;

  int x = 0;

  auto t0 = std::thread([&] {
    for(size_t i = 0; i < 10; ++i) {
      usleep(100000);
      auto ok = mx.try_lock_for(std::chrono::seconds(600));
      if(!ok) {
        throw std::logic_error("No lock!");
      }
      x++;
      mx.unlock();
    }
  });

  for(size_t i = 0; i < 10; ++i) {
    usleep(100000);
    auto ok = mx.try_lock_for(std::chrono::seconds(600));
    if(!ok) {
      throw std::logic_error("No lock!");
    }
    std::cout << x << std::endl;
    mx.unlock();
  }

  t0.join();
}

Compiling this code with TSan enabled and running it will produce the following output:

0
/usr/bin/llvm-symbolizer-18: error: '[stack]': No such file or directory
==================
WARNING: ThreadSanitizer: data race (pid=511614)
  Write of size 4 at 0x7fffffffce8c by thread T1:
    #0 main::$_0::operator()() const /home/mhier/software/sources/CppPlayground/src/main.cc:19:8 (CppPlayground+0xe006b) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #1 void std::__invoke_impl<void, main::$_0>(std::__invoke_other, main::$_0&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14 (CppPlayground+0xdffb5) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #2 std::__invoke_result<main::$_0>::type std::__invoke<main::$_0>(main::$_0&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (CppPlayground+0xdff85) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #3 void std::thread::_Invoker<std::tuple<main::$_0>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (CppPlayground+0xdff55) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #4 std::thread::_Invoker<std::tuple<main::$_0>>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (CppPlayground+0xdff25) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_0>>>::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (CppPlayground+0xdfe49) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #6 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Previous read of size 4 at 0x7fffffffce8c by main thread:
    #0 main /home/mhier/software/sources/CppPlayground/src/main.cc:30:18 (CppPlayground+0xdfbfd) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)

  As if synchronized via sleep:
    #0 usleep <null> (CppPlayground+0x5dbca) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #1 main::$_0::operator()() const /home/mhier/software/sources/CppPlayground/src/main.cc:14:7 (CppPlayground+0xe0019) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #2 void std::__invoke_impl<void, main::$_0>(std::__invoke_other, main::$_0&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14 (CppPlayground+0xdffb5) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #3 std::__invoke_result<main::$_0>::type std::__invoke<main::$_0>(main::$_0&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (CppPlayground+0xdff85) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #4 void std::thread::_Invoker<std::tuple<main::$_0>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (CppPlayground+0xdff55) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #5 std::thread::_Invoker<std::tuple<main::$_0>>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (CppPlayground+0xdff25) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_0>>>::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (CppPlayground+0xdfe49) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #7 execute_native_thread_routine /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)

  Location is stack of main thread.

  Location is global '??' at 0x7ffffffdd000 ([stack]+0x1fe8c)

  Thread T1 (tid=511616, running) created by main thread at:
    #0 pthread_create <null> (CppPlayground+0x6083b) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)
    #1 __gthread_create /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:676:35 (libstdc++.so.6+0xeceb0) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xeceb0)
    #3 main /home/mhier/software/sources/CppPlayground/src/main.cc:12:13 (CppPlayground+0xdfb95) (BuildId: dc419eb81c4ef89babd371b15b4ef9c1a4f8715b)

SUMMARY: ThreadSanitizer: data race /home/mhier/software/sources/CppPlayground/src/main.cc:19:8 in main::$_0::operator()() const
==================
1
2
3
4
5
6
7
8
9
ThreadSanitizer: reported 1 warnings

The above output has been generated with clang 18.1.3 on Ubuntu but the issue can be reproduced also with the latest clang 21.1 installed through https://apt.llvm.org/llvm.sh. In all cases I have been compiling and running this code on Ubuntu 24.04, hence I am using libstdc++ rather than libc++.

The problem goes away when:

  • using mx.lock() instead of try_lock_for(),
  • changing the shared_timed_mutex into a timed_mutex (despite we always lock the mutex exclusively!)
  • changing to the boost equivalent boost::shared_mutex.

Metadata

Metadata

Assignees

No one assigned

    Labels

    compiler-rt:tsanThread sanitizerlibstdc++GNU libstdc++ C++ standard library

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions