[英]Why does the thread sanitizer complain about acquire/release thread fences?
I'm learning about different memory orders.我正在了解不同的 memory 订单。
I have this code, which works and passes GCC's and Clang's thread sanitizers :我有这段代码,它可以工作并通过 GCC 和 Clang 的线程消毒剂:
#include <atomic>
#include <iostream>
#include <future>
int state = 0;
std::atomic_int a = 0;
void foo(int from, int to)
{
for (int i = 0; i < 10; i++)
{
while (a.load(std::memory_order_acquire) != from) {}
state++;
a.store(to, std::memory_order_release);
}
}
int main()
{
auto x = std::async(std::launch::async, foo, 0, 1);
auto y = std::async(std::launch::async, foo, 1, 0);
}
I reckon that an 'acquire' load is unnecessary if it doesn't end up returning from
, so I decided to use a 'relaxed' load, followed by an 'acquire' fence.我认为如果“获取”负载最终没有from
.
I expected it to work, but it's rejected by the thread sanitizers , which claim that concurrent state++
s are a data race.我希望它能够工作,但它被 thread sanitizers 拒绝,它声称并发state++
是数据竞争。
#include <atomic>
#include <iostream>
#include <future>
int state = 0;
std::atomic_int a = 0;
void foo(int from, int to)
{
for (int i = 0; i < 10; i++)
{
while (a.load(std::memory_order_relaxed) != from) {}
std::atomic_thread_fence(std::memory_order_acquire);
state++;
a.store(to, std::memory_order_release);
}
}
int main()
{
auto x = std::async(std::launch::async, foo, 0, 1);
auto y = std::async(std::launch::async, foo, 1, 0);
}
Why is this a data race?为什么这是一场数据竞赛?
Cppreference says thatCppreference说
Atomic-fence synchronization原子栅同步
An atomic release operation X in thread A synchronizes-with an acquire fence F in thread B, if线程 A 中的原子释放操作 X 与线程 B 中的获取栅栏 F 同步,如果
- there exists an atomic read Y (with any memory order)存在原子读取 Y(使用任何 memory 顺序)
- Y reads the value written by X (or by the release sequence headed by X) Y 读取 X 写入的值(或以 X 为首的释放序列)
- Y is sequenced-before F in thread B Y 在线程 B 中排在 F 之前
In this case, all non-atomic and relaxed atomic stores that are sequenced-before X in thread A will happen-before all non-atomic and relaxed atomic loads from the same locations made in thread B after F.在这种情况下,在线程 A 中的 X 之前排序的所有非原子和松弛原子存储将发生在 F 之后线程 B 中相同位置的所有非原子和松弛原子加载之前。
In my understanding, all conditions are met:据我了解,所有条件都满足:
a.load(std::memory_order_relaxed)
. “存在原子读取 Y(具有任何 memory 顺序)” - 检查: a.load(std::memory_order_relaxed)
。a.store(to, std::memory_order_release);
"Y 读取 X 写入的值" — 检查,它从a.store(to, std::memory_order_release);
. . The thread sanitizer currently doesn't support std::atomic_thread_fence
.线程清理程序当前不支持std::atomic_thread_fence
。 (GCC and Clang use the same thread sanitizer, so it applies to both.) (GCC 和 Clang 使用相同的线程消毒剂,所以它适用于两者。)
GCC 12 (currently trunk) warns about it: GCC 12(当前主干)警告它:
atomic_base.h:133:26: warning: 'atomic_thread_fence' is not supported with '-fsanitize=thread' [-Wtsan]
133 | { __atomic_thread_fence(int(__m)); }
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
To stop the sanitizer from disregarding fences, you can manually instrument them, using __tsan_acquire
and __tsan_release
.要阻止 sanitizer 忽略栅栏,您可以使用__tsan_acquire
和__tsan_release
手动检测它们。
#include <sanitizer/tsan_interface.h>
while (a.load(std::memory_order_relaxed) != from) {}
__tsan_acquire(&a); // <--
std::atomic_thread_fence(std::memory_order_acquire);
I assume it's tricky to automatically determine which atomic variables are affected by the fence.我认为自动确定哪些原子变量受围栏影响是很棘手的。
Even though the bug report says seq_cst
fences are not affected , the code is still rejected if I use such a fence, and I'm not sure how to instrument it correctly.即使错误报告说seq_cst
栅栏不受影响,如果我使用这样的栅栏,代码仍然会被拒绝,而且我不确定如何正确检测它。 Maybe __tsan_acquire
+ __tsan_release
are enough.也许__tsan_acquire
+ __tsan_release
就足够了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.