当我运行从分离的线程调用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,我使用clang8和libc ++从TSAN收到了非常类似的错误:
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
解锁后立即经历了虚假唤醒,那么它将看到done
是true
并可能到达main
的结尾并在cv
被调用之前销毁cv.notify_all()
。