Memory Model in C++ : sequential consistency and atomicity

The C++ memory model guarantees sequential consistency if you use atomic operations with the appropriate memory orderings to guarantee sequential consistency. If you just use plain non-atomic operations, or relaxed atomics, and no mutexes, then sequential consistency is not guaranteed.

Compilers are free to re-order operations if the difference in behaviour cannot be observed, that's the as-if rule. So for example, if re-ordering sequentially consistent atomics would produce a different observable result then it doesn't meet the as-if rule. If it would not produce a different observable result, then reordering is allowed.


I think I figured out what that slide is talking about, from reading the earlier slides:

slide 12: sequential consistency [Leslie Lamport, 1979]
the result of any execution is the same as-if

  1. the operations of all threads are executed in some sequential order
  2. the operations of each thread appear in this sequence in the order specified by their program

slide14: sequential consistency for data-race-free programs
SC-DRF:

  • We take care our program does not contain data races
  • The system guarantees sequentially consistent execution

So on slide 29, the authors are saying that once you avoid data-race UB using std::atomic, the program runs as-if everything happened in program order. (If all your std::atomic operations use the default memory_order_seq_cst).

This is an interesting way to look at C++'s weak (for non-atomic objects) memory model. This looks like a good set of slides. SC atomic operations are strongly ordered, and are kind of like one-way barriers for non-atomic operations. (And for relaxed atomic operations if you have any).

Note that being data-race-free means you can't look at non-atomic variables at arbitrary times, only when you've established that no other thread is writing them. (Usually via a synchronizes-with relationship with an acquire load seeing a release store done by the writer, or a mutex.) The data-race-free part is the key here; it's very easy to have data-race UB if you're not careful. When compiling to asm for real CPUs, this means non-atomic accesses can work as normal, while atomic<T> seq_cst accesses need to block compile-time and run-time reordering. https://preshing.com/20120625/memory-ordering-at-compile-time/


Part two: Please don't make a habit of asking two very different questions at once.

This "how does the CPU do it?" question would be a better fit as part of your later question: Atomicity on x86

I have most of an answer to it already written, which I'll put there instead.