Os3*_*Os3 14 c++ multithreading c++11 thread-sanitizer
我正在尝试使用g ++和线程消毒剂,我认为我得到了误报.这是真的吗,还是我犯了一些大错?
程序(剪切和粘贴来自Anthony Williams:C++ Concurrency in Action,第145页,清单5.13)
#include <atomic>
#include <thread>
#include <assert.h>
bool x=false;
std::atomic<bool> y;
std::atomic<int> z;
void write_x_then_y()
{
x=true;
std::atomic_thread_fence(std::memory_order_release);
y.store(true,std::memory_order_relaxed);
}
void read_y_then_x()
{
while(!y.load(std::memory_order_relaxed));
std::atomic_thread_fence(std::memory_order_acquire);
if(x)
++z;
}
int main()
{
x=false;
y=false;
z=0;
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load()!=0);
}
Run Code Online (Sandbox Code Playgroud)
编译:
g++ -o a -g -Og -pthread a.cpp -fsanitize=thread
Run Code Online (Sandbox Code Playgroud)
g ++版本
~/build/px> g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/6.1.1/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 6.1.1 20160621 (Red Hat 6.1.1-3) (GCC)
Run Code Online (Sandbox Code Playgroud)
我正进入(状态:
~/build/px> ./a
==================
WARNING: ThreadSanitizer: data race (pid=13794)
Read of size 1 at 0x000000602151 by thread T2:
#0 read_y_then_x() /home/ostri/build/px/a.cpp:17 (a+0x000000401014)
#1 void std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) /usr/include/c++/6.1.1/functional:1400 (a+0x000000401179)
#2 std::_Bind_simple<void (*())()>::operator()() /usr/include/c++/6.1.1/functional:1389 (a+0x000000401179)
#3 std::thread::_State_impl<std::_Bind_simple<void (*())()> >::_M_run() /usr/include/c++/6.1.1/thread:196 (a+0x000000401179)
#4 <null> <null> (libstdc++.so.6+0x0000000baaae)
Previous write of size 1 at 0x000000602151 by thread T1:
#0 write_x_then_y() /home/ostri/build/px/a.cpp:9 (a+0x000000400fbd)
#1 void std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) /usr/include/c++/6.1.1/functional:1400 (a+0x000000401179)
#2 std::_Bind_simple<void (*())()>::operator()() /usr/include/c++/6.1.1/functional:1389 (a+0x000000401179)
#3 std::thread::_State_impl<std::_Bind_simple<void (*())()> >::_M_run() /usr/include/c++/6.1.1/thread:196 (a+0x000000401179)
#4 <null> <null> (libstdc++.so.6+0x0000000baaae)
Location is global 'x' of size 1 at 0x000000602151 (a+0x000000602151)
Thread T2 (tid=13797, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.0+0x000000028380)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0x0000000badc4)
#2 main /home/ostri/build/px/a.cpp:26 (a+0x000000401097)
Thread T1 (tid=13796, finished) created by main thread at:
#0 pthread_create <null> (libtsan.so.0+0x000000028380)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0x0000000badc4)
#2 main /home/ostri/build/px/a.cpp:25 (a+0x00000040108a)
SUMMARY: ThreadSanitizer: data race /home/ostri/build/px/a.cpp:17 in read_y_then_x()
==================
ThreadSanitizer: reported 1 warnings
Run Code Online (Sandbox Code Playgroud)
我在更复杂的程序中得到了这个警告,我认为这是我的错误,但现在即使是"学校书籍程序"也会显示相同的行为.它(即缺少一些编译器开关)me或g ++?
更新 来自链接
#if defined(__SANITIZE_THREAD__)
#define TSAN_ENABLED
#elif defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define TSAN_ENABLED
#endif
#endif
#ifdef TSAN_ENABLED
#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr) \
AnnotateHappensBefore(__FILE__, __LINE__, (void*)(addr))
#define TSAN_ANNOTATE_HAPPENS_AFTER(addr) \
AnnotateHappensAfter(__FILE__, __LINE__, (void*)(addr))
extern "C" void AnnotateHappensBefore(const char* f, int l, void* addr);
extern "C" void AnnotateHappensAfter(const char* f, int l, void* addr);
#else
#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr)
#define TSAN_ANNOTATE_HAPPENS_AFTER(addr)
#endif
#include <atomic>
#include <thread>
#include <assert.h>
bool x=false;
std::atomic<bool> y;
std::atomic<int> z;
void write_x_then_y()
{
x=true;
std::atomic_thread_fence(std::memory_order_release);
TSAN_ANNOTATE_HAPPENS_BEFORE(&x);
y.store(true,std::memory_order_relaxed);
}
void read_y_then_x()
{
while(!y.load(std::memory_order_relaxed));
std::atomic_thread_fence(std::memory_order_acquire);
TSAN_ANNOTATE_HAPPENS_AFTER(&x);
if(x)
++z;
}
{
x=false;
y=false;
z=0;
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load()!=0);
}
Run Code Online (Sandbox Code Playgroud)
编译命令
g++ -o a -g -Og -pthread a.cpp -fsanitize=thread -D__SANITIZE_THREAD__
Run Code Online (Sandbox Code Playgroud)
TL; DR:这是TSAN假阳性.代码有效.
线程1:
x=true; // W
std::atomic_thread_fence(std::memory_order_release); // A
y.store(true,std::memory_order_relaxed); // X
Run Code Online (Sandbox Code Playgroud)
线程2:
while(!y.load(std::memory_order_relaxed)); // Y
std::atomic_thread_fence(std::memory_order_acquire); // B
if(x) // R
++z;
Run Code Online (Sandbox Code Playgroud)
如果存在原子操作X和Y,则释放栅栏A与获取栅栏B同步,两者都在某个原子对象M上操作,使得A在X之前被排序,X修改M,Y在B之前被排序,并且Y读取值如果是释放操作,则由X写入或由假设释放序列中的任何副作用写入的值X将结束.
我们来看看清单:
y.因此,释放栅栏A与获取栅栏B同步.
写入W在A之前排序,读取R在B之后排序,因此W 之间的线程发生在之前,因此发生在之前,R [intro.races]/9-10:
评估A 线程在评估B 之前发生
- A与B同步,或
- A是在B之前依赖排序的,或者
- 对于某些评估X.
- 与X同步,X在B之前排序,或
- 在X和X之间的线程发生在B之前或之前,A被排序
- 线程发生在X和X之间的线程发生在B之前.
评估A 发生在评估B 之前(或等效地,B 发生在 A 之后),如果:
- A在B之前排序,或
- 线程发生在B之前.
由于之前发生的关系([intro.races]/19),没有数据竞争:
程序的执行包含数据竞争,如果它包含两个可能同时发生冲突的动作,其中至少有一个不是原子的,并且除了下面描述的信号处理程序的特殊情况之外,它们都不会发生在另一个之前.任何此类数据争用都会导致未定义的行为.
此外,读取R保证读取W写入的值,因为W是可见的副作用,x线程启动后没有其他副作用([intro.races]/11):
相对于M的值计算B,标量对象或位字段M上的可见副作用A满足条件:
- A发生在B和之前
- 没有其他副作用X到M使得A发生在X之前,而X发生在B之前.
由评估B确定的非原子标量对象或位字段M的值应该是由可见副作用A存储的值.