Acquire-release synchronization 2026-10-14
A release on an atomic object can synchronize with an acquire that reads the released value, establishing happens-before for surrounding accesses. A test-and-set spinlock needs suitable acquisition and release semantics for its guarded data; relaxed flag operations alone do not supply that relationship. This is a separate question from the flag's indivisibility.[ref]
Working Draft, Standard for Programming Language C++ (N4861). pp. 1524–1525: relaxed ordering and release/acquire synchronization
Data race 2026-10-14
For the C++ case used here, conflicting accesses by different software threads, at least one non-atomic, with neither ordered before the other by happens-before. Such a data race has undefined behavior. A race condition is broader: the lost update with separate atomic reads and writes depends on their order while avoiding a data race.[ref]
Working Draft, Standard for Programming Language C++ (N4861). pp. 79–80: conflicting actions and undefined behavior
Happens-before 2026-10-14
An ordering binary relation used by a memory model to constrain observation of shared data. For the mutex example, T1's write precedes its release, that release synchronizes with T2's successful acquisition, and that acquisition precedes T2's read. Together these steps order the write before the read. Wall-clock timing alone does not establish happens-before.[ref]
Memory model 2026-10-14
Rules specifying which shared-data observations and operation orders are allowed. Sequential consistency is the model used in the two diagrams. A language memory model can permit weaker ordering and impose additional conditions on conflicting accesses. Correct use of an indivisible flag must therefore be checked against that memory model, including happens-before relationships for the data guarded by the flag.
Mutual exclusion 2026-10-14
Mutual exclusion allows at most one participating software thread at a time to execute a protected critical section. A mutex is an object used to enforce that rule; the critical section is the region of the program that needs the rule. Several different critical sections can use the same mutex to protect the same shared data. The benefit is reasoning about a complete update without another cooperating software thread observing or changing its intermediate state.[ref]
For a small lost update, shared Assume sequential consistency: each step is indivisible, each software thread keeps the displayed order, and a read sees the latest preceding write to
x initially equals . Each of two software threads, T1 and T2, uses its own local r and performs these three steps:r = read(x)
r = r + 1
write(x, r)x. In the thread interleaving T1 read, T2 read, T1 add, T2 add, T1 write, T2 write, both local values become and both writes store . Final instead of : a lost update despite two correctly executed local additions. Separate indivisible reads and writes are insufficient; the complete read, modify and write must be protected.The binomial coefficient counts the thread interleavings preserving the two three-step orders: choose T1's three positions, with the remaining positions belonging to T2. Exhaustive enumeration gives final in 18 orders and in the two serial orders. These counts are not probabilities: no random scheduler has been specified. With the same mutex acquired before reading and released after writing, only the two complete increment orders remain, and both give . Alternatively, an atomic read-modify-write increment updates
x in one indivisible step. Protecting only the write still allows both software threads to read beforehand.Mutual exclusion is a safety property: two participants must never be inside the protected critical section together. Eventual entry for every persistent requester is a liveness property, expressed here as starvation freedom. A protocol that never admits anyone satisfies the first requirement while failing the second. Nor does a mutex alone promise which waiting software thread enters next, or that a producer runs before a consumer. Such application ordering needs an additional synchronization protocol.[ref]
Defining Liveness by Bowen Alpern and others. pp. 181–182: mutual exclusion, finite witnesses to safety violations, and eventual service
The sequential consistency assumptions delimit this calculation. In C++, unsynchronized conflicting accesses to an ordinary shared object can be a data race, which makes behavior undefined; the 20 schedules are not a prediction for that program. An atomic operation with relaxed ordering also supplies no acquire-release synchronization for other data. A real mutex must supply both exclusive entry and the required happens-before ordering between its users.[ref]
