title: Memory ordering behavior of std::atomic::load
body: <p>Am I wrong to assume that the atomic::load should also act as a memory barrier ensuring that all previous <em>non-atomic</em> writes will become visible by other threads?</p>
  
  <p>To illustrate:</p>
  
  <pre><code>volatile bool arm1 = false;
  std::atomic_bool arm2 = false;
  bool triggered = false;
  </code></pre>
  
  <p>Thread1:</p>
  
  <pre><code>arm1 = true;
  //std::std::atomic_thread_fence(std::memory_order_seq_cst); // this would do the trick 
  if (arm2.load())
      triggered = true;
  </code></pre>
  
  <p>Thread2:</p>
  
  <pre><code>arm2.store(true);
  if (arm1)
      triggered = true;
  </code></pre>
  
  <p>I expected that after executing both 'triggered' would be true. Please don't suggest to make arm1 atomic, the point is to explore the behavior of atomic::load.</p>
  
  <p>While I have to admit I don't fully understand the formal definitions of the different relaxed semantics of <a href="http://en.cppreference.com/w/cpp/atomic/memory_order" rel="nofollow">memory order</a> I thought that the <em>sequentially consistent ordering</em> was pretty straightforward in that it guarantees that "a single total order exists in which all threads observe all modifications in the same order." To me this implies that the std::atomic::load with the default memory order of std::memory_order_seq_cst will also act as a memory fence. This is further corroborated by the following statement under "Sequentially-consistent ordering":</p>
  
  <p>Total sequential ordering requires a full memory fence CPU instruction on all multi-core systems. </p>
  
  <p>Yet, my simple example below demonstrates this is not the case with MSVC 2013, gcc 4.9 (x86) and clang 3.5.1 (x86), where the atomic load simply translates to a load instruction.</p>
  
  <pre><code>#include &lt;atomic&gt;
  
  std::atomic_long al;
  
  #ifdef _WIN32
  __declspec(noinline)
  #else
  __attribute__((noinline))
  #endif
  long load() {
      return al.load(std::memory_order_seq_cst);
  }
  
  int main(int argc, char* argv[]) {
      long r = load();
  }
  </code></pre>
  
  <p>With gcc this looks like:</p>
  
  <pre><code>load():
     mov  rax, QWORD PTR al[rip]   ; &lt;--- plain load here, no fence or xchg
     ret
  main:
     call load()
     xor  eax, eax
     ret
  </code></pre>
  
  <p>I'll omit the msvc and clang which are essentially identical. Now on gcc for ARM we get what I expected:</p>
  
  <pre><code>load():
       dmb    sy                         ; &lt;---- data memory barrier here
       movw   r3, #:lower16:.LANCHOR0
       movt   r3, #:upper16:.LANCHOR0
       ldr    r0, [r3]                   
       dmb    sy                         ; &lt;----- and here
       bx lr
  main:
      push    {r3, lr}
      bl  load()
      movs    r0, #0
      pop {r3, pc}
  </code></pre>
  
  <p>This is not an academic question, it results in a subtle race condition in our code which called into question my understanding of the behavior of std::atomic. </p>