I wrote the following code example
#include <thread>
#include <condition_variable>
#include <mutex>
int main()
{
std::mutex mutex;
bool done = false;
std::condition_variable condvar;
std::jthread thread([&](std::stop_token stoken)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
{
std::unique_lock lock(mutex);
done = true;
}
condvar.notify_all();
while (!stoken.stop_requested())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
std::unique_lock lock(mutex);
condvar.wait_for(lock, std::chrono::seconds(60), [&]{ return done; });
thread.request_stop();
}
When compiling it on g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 with the command g++ main.cc -std=gnu++20 -fsanitize=thread, I observe thread sinitize triggering:
==================
WARNING: ThreadSanitizer: double lock of a mutex (pid=2103)
#0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
#1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
#2 std::mutex::lock() <null> (a.out+0x354e)
#3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
#4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
#5 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2649)
#6 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
#7 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
#8 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
#9 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
#10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
#11 <null> <null> (libstdc++.so.6+0xdc252)
Location is stack of main thread.
Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e2e0)
Mutex M9 (0x7ffe4c7db2e0) created at:
#0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
#1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
#2 std::mutex::lock() <null> (a.out+0x354e)
#3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
#4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
#5 main <null> (a.out+0x285f)
SUMMARY: ThreadSanitizer: double lock of a mutex (/mnt/c/Users/d7d1c/Documents/Linux/test/a.out+0x34c5) in __gthread_mutex_lock(pthread_mutex_t*)
==================
==================
WARNING: ThreadSanitizer: data race (pid=2103)
Write of size 1 at 0x7ffe4c7db2a3 by thread T1 (mutexes: write M9):
#0 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2669)
#1 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
#2 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
#3 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
#4 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
#5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
#6 <null> <null> (libstdc++.so.6+0xdc252)
Previous read of size 1 at 0x7ffe4c7db2a3 by main thread (mutexes: write M9):
#0 main::{lambda()#2}::operator()() const <null> (a.out+0x2741)
#1 bool std::condition_variable::wait_until<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> >, main::{lambda()#2}>(std::unique_lock<std::mutex>&, std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&, main::{lambda()#2}) <null> (a.out+0x2c09)
#2 bool std::condition_variable::wait_for<long, std::ratio<1l, 1l>, main::{lambda()#2}>(std::unique_lock<std::mutex>&, std::chrono::duration<long, std::ratio<1l, 1l> > const&, main::{lambda()#2}) <null> (a.out+0x2a93)
#3 main <null> (a.out+0x28a9)
As if synchronized via sleep:
#0 nanosleep ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:366 (libtsan.so.0+0x6696c)
#1 void std::this_thread::sleep_for<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> > const&) <null> (a.out+0x5450)
#2 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2627)
#3 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
#4 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
#5 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
#6 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
#7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
#8 <null> <null> (libstdc++.so.6+0xdc252)
Location is stack of main thread.
Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e2a3)
Mutex M9 (0x7ffe4c7db2e0) created at:
#0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
#1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
#2 std::mutex::lock() <null> (a.out+0x354e)
#3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
#4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
#5 main <null> (a.out+0x285f)
Thread T1 (tid=2105, running) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc328)
#2 std::thread std::jthread::_S_create<main::{lambda(std::stop_token)#1}>(std::stop_source&, main::{lambda(std::stop_token)#1}&&) <null> (a.out+0x2b43)
#3 std::jthread::jthread<main::{lambda(std::stop_token)#1}, , void>(main::{lambda(std::stop_token)#1}&&) <null> (a.out+0x29bb)
#4 main <null> (a.out+0x2849)
SUMMARY: ThreadSanitizer: data race (/mnt/c/Users/d7d1c/Documents/Linux/test/a.out+0x2669) in main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const
==================
ThreadSanitizer: reported 2 warnings
Tell me, is there something wrong with the code or is this a false positive of the sanitizer? What do you recommend doing? I encountered this behavior in a large project, and I would like to check it for racing.