title: Acquire/release semantics with non-temporal stores on x64
body: <p>I have something like:</p>
  
  <pre><code>if (f = acquire_load() == ) {
     ... use Foo
  }
  </code></pre>
  
  <p>and:</p>
  
  <pre><code>auto f = new Foo();
  release_store(f)
  </code></pre>
  
  <p>You could easily imagine an implementation of acquire_load and release_store that uses atomic with load(memory_order_acquire) and store(memory_order_release). But now what if release_store is implemented with _mm_stream_si64, a non-temporal write, which is not ordered with respect to other stores on x64? How to get the same semantics?</p>
  
  <p>I think the following is the minimum required:</p>
  
  <pre><code>atomic&lt;Foo*&gt; gFoo;
  
  Foo* acquire_load() {
      return gFoo.load(memory_order_relaxed);
  }
  
  void release_store(Foo* f) {
     _mm_stream_si64(*(Foo**)&amp;gFoo, f);
  }
  </code></pre>
  
  <p>And use it as so:</p>
  
  <pre><code>// thread 1
  if (f = acquire_load() == ) {
     _mm_lfence(); 
     ... use Foo
  }
  </code></pre>
  
  <p>and:</p>
  
  <pre><code>// thread 2
  auto f = new Foo();
  _mm_sfence(); // ensures Foo is constructed by the time f is published to gFoo
  release_store(f)
  </code></pre>
  
  <p>Is that correct? I'm pretty sure the sfence is absolutely required here. But what about the lfence? Is it required or would a simple compiler barrier be enough for x64? e.g. asm volatile("": : :"memory"). According the the x86 memory model, loads are not re-ordered with other loads. So to my understanding, acquire_load() must happen before any load inside the if statement, as long as there's a compiler barrier.</p>