A read that makes earlier writes visible
Suppose one thread changes shared data and then writes an atomic object. Another thread reads the value written by that atomic operation. If the write has release semantics and the read has acquire semantics, the read synchronizes with the write. The first thread’s earlier effects are then visible to evaluations after the read in the second thread. Merely calling an operation “acquire” is not enough: the read must obtain the value written by the corresponding release. section 19.1
TAKEAWAY: Acquire semantics belong to the reader side of a synchronization; release semantics belong to the writer side. Their pairing establishes a happened-before relation across threads. An atomic read-modify-write operation can have both roles. section 19.1
C provides acquire behavior through both atomic operations and library calls:
- An explicit atomic load can request memory_order_acquire. The corresponding release-only choice is memory_order_release, typically used with an atomic store. A read-modify-write operation can request memory_order_acq_rel. section 19.4
- The ordinary atomic operations described here give reads acquire semantics and modifications release semantics. Explicit memory orders also permit weaker choices: memory_order_relaxed adds no synchronization requirement. section 19.1 section 19.4
- Library synchronization pairs include thread creation and entry, thread exit and join, the first and subsequent calls to call_once for one object, and mutex release and acquisition. section 19.2
One-time initialization
A first call to call_once(&obj, g) runs g and acts as the releasing side for subsequent calls using obj. After another thread returns from its call with obj, it can use the initialized state: the writes performed during g are visible to it. section 19.2
An explicit memory order must fit the operation. A store-only atomic operation cannot request acquire semantics; an atomic load cannot request release semantics. On compare-exchange failure, the operation only loads, so its explicit form has a separate failure-order argument. section 19.4
Table 19.1. C library functions that form synchronization pairs (view table figure)
| Release |
Acquire |
|---|---|
| thrd_create(.., f, x) | Entry to f(x) |
| thrd_exit by thread id or return from f | Start of tss_t destructors for id |
| End of tss_t destructors for id | thrd_join(id) or atexit/at_quick_exit handlers |
| call_once(&obj, g), first call | call_once(&obj, h), all subsequent calls |
| Mutex release | Mutex acquisition |
Compare the releasing and acquiring sides of C library calls, including thread, initialization, and mutex operations. section 19.2




