tsan 投诉 notify_all_at_thread_exit

tsan complaints with notify_all_at_thread_exit

当我 运行 从分离线程调用 notify_all_at_thread_exit() 的代码时,tsan 抱怨 pthread_cond_broadcast 和 pthread_cond_destroy 之间存在数据竞争。难道我做错了什么?或者这是来自 tsan 的误报?

#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <iostream>

using namespace std;
using namespace std::chrono;

int main(int, char**){
    mutex m;
    condition_variable cv;

    bool done = false;
    thread([&](){
               this_thread::sleep_for(milliseconds(100));
               unique_lock<mutex> lk(m);
               done = true;
               notify_all_at_thread_exit(cv, std::move(lk));
           }).detach();

    unique_lock<mutex> lk(m);
    cv.wait(lk, [&]{ return done; });
    std::cout << "Done\n";
    return 0;
}
drdws0134$ garden with -m gcc/9.2.0-02c7/bin -- g++ -std=c++17 -fsanitize=thread bug1.cpp -O0 -ggdb -pthread && ./a.out
Done
==================
WARNING: ThreadSanitizer: data race (pid=27370)
  Write of size 8 at 0x7ffd05131540 by main thread:
    #0 pthread_cond_destroy /tmp/garden-install.88e036900cee/+tmp+/untar/gcc-9.2.0/libsanitizer/tsan/tsan_interceptors.cc:1171 (libtsan.so.0+0x30c66)
    #1 main /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:12 (a.out+0x402627)

  Previous read of size 8 at 0x7ffd05131540 by thread T1:
    #0 pthread_cond_broadcast /tmp/garden-install.88e036900cee/+tmp+/untar/gcc-9.2.0/libsanitizer/tsan/tsan_interceptors.cc:1164 (libtsan.so.0+0x30b63)
    #1 __gthread_cond_broadcast /tmp/garden-install.88e036900cee/+tmp+/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:853 (libstdc++.so.6+0xd1ad8)
    #2 std::condition_variable::notify_all() /tmp/garden-install.88e036900cee/+tmp+/untar/gcc-9.2.0/libstdc++-v3/src/c++11/condition_variable.cc:73 (libstdc++.so.6+0xd1ad8)

  Location is stack of main thread.

  Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e540)

  Thread T1 (tid=27372, finished) created by main thread at:
    #0 pthread_create /tmp/garden-install.88e036900cee/+tmp+/untar/gcc-9.2.0/libsanitizer/tsan/tsan_interceptors.cc:964 (libtsan.so.0+0x3057b)
    #1 __gthread_create /tmp/garden-install.88e036900cee/+tmp+/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663 (libstdc++.so.6+0xd6fe4)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) /tmp/garden-install.88e036900cee/+tmp+/untar/gcc-9.2.0/libstdc++-v3/src/c++11/thread.cc:135 (libstdc++.so.6+0xd6fe4)
    #3 main /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:15 (a.out+0x4025a4)

SUMMARY: ThreadSanitizer: data race /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:12 in main
==================
ThreadSanitizer: reported 1 warnings
drdws0134$ 

FWIW,我从 TSAN 使用 clang8 和 libc++ 得到了一个非常相似的错误:

drdws0134$ garden with -m clang/8.0.0-06c7/bin -- clang++ -Wl,-rpath,/gdn/centos7/0001/x3/prefixes/clang/8.0.0-06c7__4bf264b1013a/lib -stdlib=libc++ -std=c++17 -fsanitize=thread bug1.cpp -O0 -ggdb -pthread && ./a.out
Done
==================
WARNING: ThreadSanitizer: data race (pid=27396)
  Write of size 8 at 0x7ffea2b603e8 by main thread:
    #0 pthread_cond_destroy /tmp/garden-install.d86e7e77c946/clang-8.0.0-06c7/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:1216 (a.out+0x46b7b1)
    #1 main /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:26 (a.out+0x4c69b1)

  Previous read of size 8 at 0x7ffea2b603e8 by thread T1:
    #0 pthread_cond_broadcast /tmp/garden-install.d86e7e77c946/clang-8.0.0-06c7/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:1209 (a.out+0x46b6bd)
    #1 std::__1::__thread_struct_imp::~__thread_struct_imp() /tmp/garden-install.d86e7e77c946/clang-8.0.0-06c7/libcxx/src/thread.cpp:173 (libc++.so.1+0x8b17d)

  Location is stack of main thread.

  Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e3e8)

  Thread T1 (tid=27398, finished) created by main thread at:
    #0 pthread_create /tmp/garden-install.d86e7e77c946/clang-8.0.0-06c7/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:980 (a.out+0x452aa6)
    #1 std::__1::__libcpp_thread_create(unsigned long*, void* (*)(void*), void*) /gdn/centos7/0001/x3/prefixes/clang/8.0.0-06c7__4bf264b1013a/bin/../include/c++/v1/__threading_support:328 (a.out+0x4c7eec)
    #2 thread<(lambda at bug1.cpp:15:12), void> /gdn/centos7/0001/x3/prefixes/clang/8.0.0-06c7__4bf264b1013a/bin/../include/c++/v1/thread:368 (a.out+0x4c6b65)
    #3 main /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:15 (a.out+0x4c6922)

SUMMARY: ThreadSanitizer: data race /u/nyc/salmonj/junk/thread_notify_bug/bug1.cpp:26 in main
==================
ThreadSanitizer: reported 1 warnings
drdws0134$ 

是的,我认为可能存在竞争条件。

std::notify_all_at_thread_exit 被指定为在线程退出时执行相当于 lk.unlock(); cv.notify_all() 的操作。如果您的主线程在 m 解锁后立即经历虚假唤醒,那么它会看到 donetrue 并且可能到达 main 的末尾并销毁 cvcv.notify_all() 被调用之前。