<p>Regarding (3) - it depends on the memory order used. If both, the store and the RMW operation use <code>std::memory_order_seq_cst</code>, then both operations are ordered in some way - i.e., either the store happens before the RMW, or the other way round. If the store is order before the RMW, then it is guaranteed that the RMW operation "sees" the value that was stored. If the store is ordered after the RMW, it would overwrite the value written by the RMW operation.</p>

<p>If you use more relaxed memory orders, the modifications will still be ordered in some way (the modification order of the variable), but you have no guarantees on whether the RMW "sees" the value from the store operation - even if the RMW operation is order <em>after</em> the write in the variable's modification order.</p>

<p>In case you want to read yet another article I can refer you to <a href="https://arxiv.org/abs/1803.04432" rel="nofollow noreferrer">Memory Models for C/C++ Programmers</a>.</p>
What is guaranteed with C++ std::atomic at the programmer level?
<p>I have listened and read to several articles, talks and stackoverflow questions about <code>std::atomic</code>, and I would like to be sure that I have understood it well. Because I am still a bit confused with cache line writes visibility due to possible delays in MESI (or derived) cache coherency protocols, store buffers, invalidate queues, and so on. </p>

<p>I read x86 has a stronger memory model, and that if a cache invalidation is delayed x86 can revert started operations. But I am now interested only on what I should assume as a C++ programmer, independently of the platform.</p>

<p>[T1: thread1   T2: thread2  V1: shared atomic variable]</p>

<p>I understand that std::atomic guarantees that,</p>

<p>(1) No data races occur on a variable (thanks to exclusive access to the cache line).</p>

<p>(2) Depending which memory_order we use, it guarantees (with barriers) that sequential consistency happens (before a barrier, after a barrier or both).</p>

<p>(3) After an atomic write(V1) on T1, an atomic RMW(V1) on T2 will be coherent (its cache line will have been updated with the written value on T1).</p>

<p>But as <a href="https://fgiesen.wordpress.com/2014/07/07/cache-coherency/" rel="nofollow noreferrer">cache coherency primer</a> mention,</p>

<blockquote>
  <p>The implication of all these things is that, by default, loads can fetch stale data (if a corresponding invalidation request was sitting in the invalidation queue)</p>
</blockquote>

<p>So, is the following correct?</p>

<p>(4) <code>std::atomic</code> does NOT guarantee that T2 won't read a 'stale' value on an atomic read(V) after an atomic write(V) on T1.</p>

<p>Questions if (4) is right: if the atomic write on T1 invalidates the cache line no matter the delay, why is T2 waiting for the invalidation to be effective when does an atomic RMW operation but not on an atomic read?</p>

<p>Questions if (4) is wrong: when can a thread read a 'stale' value and "it's visible" in the execution, then?</p>

<p>I appreciate your answers a lot</p>

<p><strong>Update 1</strong></p>

<p>So it seems I was wrong on (3) then. Imagine the following interleave, for an initial V1=0:</p>

<pre><code>T1: W(1)
T2:      R(0) M(++) W(1)
</code></pre>

<p>Even though T2's RMW is guaranteed to happen entirely after W(1) in this case, it can still read a 'stale' value (I was wrong). According to this, atomic doesn't guarantee full cache coherency, only sequential consistency.</p>

<p><strong>Update 2</strong></p>

<p>(5) Now imagine this example (x = y = 0 and are atomic):</p>

<pre><code>T1: x = 1;
T2: y = 1;
T3: if (x==1 &amp;&amp; y==0) print("msg");
</code></pre>

<p>according to what we've talked, seeing the "msg" displayed on screen wouldn't give us information beyond that T2 was executed after T1. So either of the following executions might have happened:</p>

<ul>
<li>T1 &lt; T3 &lt; T2</li>
<li>T1 &lt; T2 &lt; T3 (where T3 sees x = 1 but not y = 1 yet)</li>
</ul>

<p>is that right?</p>

<p>(6) If a thread can always read 'stale' values, what would happen if we took the typical "publish" scenario but instead of signaling that some data is ready, we do just the opposite (delete the data)?</p>

<pre><code>T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr-&gt;doSomething();
</code></pre>

<p>where T2 would still be using a deleted ptr until sees that is_enabled is false.</p>

<p>(7) Also, the fact that threads may read 'stale' values means that a <strong>mutex</strong> cannot be implemented with just one lock-free atomic right? It would require a synch mechanism between threads. Would it require a lockable atomic?</p>