我正在努力理解 C11 标准的第 5.1.2.4 节,特别是释放/获取的语义。我注意到https://preshing.com/20120913/acquire-and-release-semantics/ https://preshing.com/20120913/acquire-and-release-semantics/(除其他外)指出:
...释放语义可防止写入释放的内存重新排序以及按程序顺序位于其之前的任何读取或写入操作。
因此,对于以下情况:
typedef struct test_struct
{
_Atomic(bool) ready ;
int v1 ;
int v2 ;
} test_struct_t ;
extern void
test_init(test_struct_t* ts, int v1, int v2)
{
ts->v1 = v1 ;
ts->v2 = v2 ;
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
}
extern int
test_thread_1(test_struct_t* ts, int v2)
{
int v1 ;
while (atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v2 = v2 ; // expect read to happen before store/release
v1 = ts->v1 ; // expect write to happen before store/release
atomic_store_explicit(&ts->ready, true, memory_order_release) ;
return v1 ;
}
extern int
test_thread_2(test_struct_t* ts, int v1)
{
int v2 ;
while (!atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v1 = v1 ;
v2 = ts->v2 ; // expect write to happen after store/release in thread "1"
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
return v2 ;
}
这些执行的地方:
> in the "main" thread: test_struct_t ts ;
> test_init(&ts, 1, 2) ;
> start thread "2" which does: r2 = test_thread_2(&ts, 3) ;
> start thread "1" which does: r1 = test_thread_1(&ts, 4) ;
因此,我希望线程“1”具有 r1 == 1,线程“2”具有 r2 = 4。
我希望如此,因为(遵循第 5.1.2.4 节第 16 和 18 段):
- 所有(非原子)读取和写入都“在之前排序”,因此“发生在”线程“1”中的原子写入/释放之前,
- 其中“线程间发生在”线程“2”中的原子读取/获取之前(当它读取“true”时),
- 反过来,它“在之前排序”,因此“发生在”(非原子)读取和写入(在线程“2”中)之前。
然而,我完全有可能没能理解这个标准。
我观察到为 x86_64 生成的代码包括:
test_thread_1:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
jne <test_thread_1> -- while is true
mov %esi,0x8(%rdi) -- (W1) ts->v2 = v2
mov 0x4(%rdi),%eax -- (R1) v1 = ts->v1
movb $0x1,(%rdi) -- (X1) atomic_store_explicit(&ts->ready, true, memory_order_release)
retq
test_thread_2:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
je <test_thread_2> -- while is false
mov %esi,0x4(%rdi) -- (W2) ts->v1 = v1
mov 0x8(%rdi),%eax -- (R2) v2 = ts->v2
movb $0x0,(%rdi) -- (X2) atomic_store_explicit(&ts->ready, false, memory_order_release)
retq
And providedR1 和 X1 按此顺序发生,这给出了我期望的结果。
但我对x86_64的理解是,读取与其他读取按顺序发生,写入与其他写入按顺序发生,但读取和写入可能不按顺序发生。这意味着 X1 有可能在 R1 之前发生,甚至 X1、X2、W2、R1 按此顺序发生——我相信。 [这似乎极不可能,但如果 R1 因某些缓存问题而受阻?]
请问:我有什么不明白的地方吗?
我注意到,如果我改变负载/存储ts->ready
to memory_order_seq_cst
,为商店生成的代码是:
xchg %cl,(%rdi)
这与我对 x86_64 的理解一致,并且会给出我期望的结果。