title: What is the difference between using explicit fences and std::atomic?
body: <p>Assuming that aligned pointer loads and stores are naturally atomic on the target platform, what is the difference between this:</p>
  
  <pre><code>// Case 1: Dumb pointer, manual fence
  int* ptr;
  // ...
  std::atomic_thread_fence(std::memory_order_release);
  ptr = new int(-4);
  </code></pre>
  
  <p>this:</p>
  
  <pre><code>// Case 2: atomic var, automatic fence
  std::atomic&lt;int*&gt; ptr;
  // ...
  ptr.store(new int(-4), std::memory_order_release);
  </code></pre>
  
  <p>and this:</p>
  
  <pre><code>// Case 3: atomic var, manual fence
  std::atomic&lt;int*&gt; ptr;
  // ...
  std::atomic_thread_fence(std::memory_order_release);
  ptr.store(new int(-4), std::memory_order_relaxed);
  </code></pre>
  
  <p>I was under the impression that they were all equivalent, however <a href="http://www.1024cores.net/home/relacy-race-detector" rel="noreferrer">Relacy</a> detects a data race in
  the first case (only):</p>
  
  <pre><code>struct test_relacy_behaviour : public rl::test_suite&lt;test_relacy_behaviour, 2&gt;
  {
      rl::var&lt;std::string*&gt; ptr;
      rl::var&lt;int&gt; data;
  
      void before()
      {
          ptr($) = nullptr;
          rl::atomic_thread_fence(rl::memory_order_seq_cst);
      }
  
      void thread(unsigned int id)
      {
          if (id == 0) {
              std::string* p  = new std::string("Hello");
              data($) = 42;
              rl::atomic_thread_fence(rl::memory_order_release);
              ptr($) = p;
          }
          else {
              std::string* p2 = ptr($);        // &lt;-- Test fails here after the first thread completely finishes executing (no contention)
              rl::atomic_thread_fence(rl::memory_order_acquire);
  
              RL_ASSERT(!p2 || *p2 == "Hello" &amp;&amp; data($) == 42);
          }
      }
  
      void after()
      {
          delete ptr($);
      }
  };
  </code></pre>
  
  <p>I contacted the author of Relacy to find out if this was expected behaviour; he says that there is indeed a data race in my test case.
  However, I'm having trouble spotting it; can someone point out to me what the race is?
  Most importantly, what are the differences between these three cases?</p>
  
  <p><strong>Update</strong>: It's occurred to me that Relacy may simply be complaining about the <em>atomicity</em> (or lack thereof, rather) of the variable being accessed across threads... after all, it doesn't know that I <em>intend</em> only to use this code on platforms where aligned integer/pointer access is naturally atomic.</p>
  
  <p><strong>Another update</strong>: Jeff Preshing has written an excellent blog post <a href="http://preshing.com/20131125/acquire-and-release-fences-dont-work-the-way-youd-expect/" rel="noreferrer">explaining the difference between explicit fences and the built-in ones</a> ("fences" vs "operations"). Cases 2 and 3 are apparently not equivalent! (In certain subtle circumstances, anyway.)</p>