C11原子获取/发布和x86_64缺乏加载/存储一致性?

问题描述 投票:9回答:1

我正在努力解决C11标准的5.1.2.4节,尤其是Release / Acquire的语义。我注意到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   

并且提供

R1和X1以此顺序发生,这给出了我期望的结果。

但是我对x86_64的理解是,读取与其他读取按顺序发生,而写入与其他写入按顺序发生,但是读取和写入可能不会彼此按顺序发生。这意味着X1可能会在R1之前发生,甚至X1,X2,W2,R1也可能以该顺序发生-我相信。 [这似乎极不可能,但是如果R1被某些缓存问题阻止了?]

请:我不明白什么?

[我注意,如果将ts->ready的加载/存储更改为memory_order_seq_cst,则为存储生成的代码为:

  xchg   %cl,(%rdi)

这与我对x86_64的理解一致,并且将给出我期望的结果。

我正在努力解决C11标准的5.1.2.4节,尤其是Release / Acquire的语义。我注意到https://preshing.com/20120913/acquire-and-release-semantics/(以及其他)...

c x86-64 memory-barriers memory-model stdatomic
1个回答
0
投票

x86的内存模型基本上是顺序一致性加存储缓冲区(带有存储转发)。因此,每个商店都是一个release-store 1

。这就是为什么只有seq-cst存储区需要任何特殊说明的原因。 (C/C++11 atomics mappings to asm)。此外,https://stackoverflow.com/tags/x86/info包含一些指向x86文档的链接,包括a formal description of the x86-TSO memory model(对于大多数人来说基本上是不可读的;需要花很多时间才能得出结论)。
© www.soinside.com 2019 - 2024. All rights reserved.