Template:cpp/atomic/sequentially-consistent
Atomic operations tagged memory_order_seq_cst not only order memory the same way as release/acquire ordering (everything that happened-before a store in one thread becomes a visible side effect in the thread that did a load), but also establish a single total modification order of all atomic operations that are so tagged.
Formally,
each
If there was a
For a pair of atomic operations on M called A and B, where A writes and B reads M's value, if there are two
For a pair of atomic modifications of M called A and B, B occurs after A in M's modification order if
Note that this means that: 1) as soon as atomic operations that are not tagged
memory_order_seq_cst enter the picture, the sequential consistency is lost,2) the sequentially-consistent fences are only establishing total ordering for the fences themselves, not for the atomic operations in the general case (sequenced-before is not a cross-thread relationship, unlike happens-before). |
(until C++20) |
Formally,
an atomic operation A on some atomic object M is coherence-ordered-before another atomic operation B on M if any of the following is true: 1) A is a modification, and B reads the value stored by A,
2) A precedes B in the modification order of M,
3) A reads the value stored by an atomic modification X, X precedes B in the modification order, and A and B are not the same atomic read-modify-write operation,
4) A is coherence-ordered-before X, and X is coherence-ordered-before B.
There is a single total order S on all 1) if A and B are
memory_order_seq_cst operations, and A strongly happens-before B, then A precedes B in S,2) for every pair of atomic operations A and B on an object M, where A is coherence-ordered-before B:
a) if A and B are both
memory_order_seq_cst operations, then A precedes B in S,b) if A is a
memory_order_seq_cst operation, and B happens-before a memory_order_seq_cst fence Y, then A precedes Y in S,c) if a
memory_order_seq_cst fence X happens-before A, and B is a memory_order_seq_cst operation, then X precedes B in S,d) if a
memory_order_seq_cst fence X happens-before A, and B happens-before a memory_order_seq_cst fence Y, then X precedes Y in S.The formal definition ensures that: 1) the single total order is consistent with the modification order of any atomic object,
2) a
memory_order_seq_cst load gets its value either from the last memory_order_seq_cst modification, or from some non-memory_order_seq_cst modification that does not happen-before preceding memory_order_seq_cst modifications.The single total order might not be consistent with happens-before. This allows more efficient implementation of For example, with // Thread 1: x.store(1, std::memory_order_seq_cst); // A y.store(1, std::memory_order_release); // B // Thread 2: r1 = y.fetch_add(1, std::memory_order_seq_cst); // C r2 = y.load(std::memory_order_relaxed); // D // Thread 3: y.store(3, std::memory_order_seq_cst); // E r3 = x.load(std::memory_order_seq_cst); // F is allowed to produce r1 == 1 && r2 == 3 && r3 == 0, where A happens-before C, but C precedes A in the single total order C-E-F-A of Note that: 1) as soon as atomic operations that are not tagged
memory_order_seq_cst enter the picture, the sequential consistency guarantee for the program is lost,2) in many cases, memory_order_seq_cst atomic operations are reorderable
with respect to other atomic operations performed by the same thread. |
(since C++20) |
Sequential ordering may be necessary for multiple producer-multiple consumer situations where all consumers must observe the actions of all producers occurring in the same order.
Total sequential ordering requires a full memory fence CPU instruction on all multi-core systems. This may become a performance bottleneck since it forces the affected memory accesses to propagate to every core.