为什么线程清理器会抱怨 acquire/release 线程栅栏?
Why does the thread sanitizer complain about acquire/release thread fences?
我正在了解不同的内存顺序。
我有这段代码,可以使用 passes GCC's and Clang's thread sanitizers:
#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);
}
我认为如果不返回 from
就不需要 'acquire' 加载,所以我决定使用 'relaxed' 加载,然后是 'acquire'围栏.
我希望它能工作,但它是 rejected by the 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);
}
为什么这是一场数据竞赛?
Atomic-fence synchronization
An atomic release operation X in thread A synchronizes-with an acquire
fence F in thread B, if
- there exists an atomic read Y (with any memory order)
- Y reads the value written by X (or by the release sequence headed by X)
- Y is sequenced-before F in thread B
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.
据我了解,所有条件都满足:
- “存在原子读取 Y(具有任何内存顺序)”— 检查:
a.load(std::memory_order_relaxed)
。
- "Y 读取 X 写入的值" — 检查,它从
a.store(to, std::memory_order_release);
. 读取值
- “在线程 B 中,Y 在 F 之前排序”— 检查。
线程清理器当前 doesn't support std::atomic_thread_fence
。 (GCC 和 Clang 使用相同的线程清理器,因此它适用于两者。)
GCC 12(当前为主干)对此发出警告:
atomic_base.h:133:26: warning: 'atomic_thread_fence' is not supported with '-fsanitize=thread' [-Wtsan]
133 | { __atomic_thread_fence(int(__m)); }
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
要阻止消毒剂忽略围栏,您可以使用 __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);
我认为自动确定哪些原子变量受栅栏影响很棘手。
尽管错误报告说 seq_cst
fences are not affected, the code is still rejected if I use such a fence, they still need to be annotated with __tsan_acquire
+__tsan_release
, exactly the same as acq-rel fences。
我正在了解不同的内存顺序。
我有这段代码,可以使用 passes GCC's and Clang's thread sanitizers:
#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);
}
我认为如果不返回 from
就不需要 'acquire' 加载,所以我决定使用 'relaxed' 加载,然后是 'acquire'围栏.
我希望它能工作,但它是 rejected by the 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);
}
为什么这是一场数据竞赛?
Atomic-fence synchronization
An atomic release operation X in thread A synchronizes-with an acquire fence F in thread B, if
- there exists an atomic read Y (with any memory order)
- Y reads the value written by X (or by the release sequence headed by X)
- Y is sequenced-before F in thread B
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.
据我了解,所有条件都满足:
- “存在原子读取 Y(具有任何内存顺序)”— 检查:
a.load(std::memory_order_relaxed)
。 - "Y 读取 X 写入的值" — 检查,它从
a.store(to, std::memory_order_release);
. 读取值
- “在线程 B 中,Y 在 F 之前排序”— 检查。
线程清理器当前 doesn't support std::atomic_thread_fence
。 (GCC 和 Clang 使用相同的线程清理器,因此它适用于两者。)
GCC 12(当前为主干)对此发出警告:
atomic_base.h:133:26: warning: 'atomic_thread_fence' is not supported with '-fsanitize=thread' [-Wtsan]
133 | { __atomic_thread_fence(int(__m)); }
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
要阻止消毒剂忽略围栏,您可以使用 __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);
我认为自动确定哪些原子变量受栅栏影响很棘手。
尽管错误报告说 seq_cst
fences are not affected, the code is still rejected if I use such a fence, they still need to be annotated with __tsan_acquire
+__tsan_release
, exactly the same as acq-rel fences。