liveBook · Manning

Acquire-release semantics

As explained in Modern C

A release operation is a concurrency operation that publishes preceding effects so that a corresponding acquire can observe them.

A write that another thread can see

An input thread changes data, then writes an atomic object. An account thread reads the value written by the input thread. That read synchronizes with the write: the earlier changes are now visible to the account thread. The direction matters—the write is the releasing side, and the read is the acquiring side. section 19.1

A release operation publishes effects that happened before it. An acquire operation that reads the value written by that release synchronizes with it. TAKEAWAY: if a release synchronizes with an acquire, effects before the release are visible at evaluations after the acquire. An atomic read-modify-write operation can have both acquire and release semantics. section 19.1

C provides these properties through both atomic operations and paired library calls:

  • Use memory_order_release for a release-only operation, typically atomic_store or atomic_flag_clear; use memory_order_acquire for an acquire-only operation, typically atomic_load. section 19.4
  • Use memory_order_acq_rel when an atomic operation both reads and writes, as with atomic_fetch_add, atomic_compare_exchange_weak, or atomic_flag_test_and_set. section 19.4
  • Use matching library synchronization, such as the first successful call_once initialization and subsequent calls with the same object, or mutex release and acquisition. section 19.2

Selecting a memory order

atomic_fetch_add_explicit(&at, 1, memory_order_acq_rel) requests acquire-release consistency for one read-modify-write operation. The _explicit interface selects a memory order; the corresponding interface without _explicit uses sequential consistency. A store-only operation cannot request acquire semantics, because it has no acquiring side. 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

The table pairs releasing and acquiring C library events, including thread creation and joining, call_once, and mutex operations. section 19.2

TAKEAWAY: acquire-release consistency can avoid the additional total ordering required by sequential consistency, but choosing explicit orders requires care. A relaxed atomic operation remains indivisible but adds no synchronization requirement; it cannot substitute for a release-acquire pair that must make earlier effects visible. section 19.4 section 19.5

How Modern C covers it

How deep it goes
Defines the synchronization rule and visibility consequence, then identifies atomic memory orders and library pairs without developing implementation internals.
Examples
Uses an input/account-thread exchange, call_once initialization, and atomic_fetch_add_explicit with memory_order_acq_rel.
What it assumes
Assumes familiarity with C threads and atomic objects while defining synchronization and visibility in context.
What you can do after
Apply it via C atomic _explicit interfaces or paired library calls to make preceding effects visible across threads.
How it connects
Connects happened-before, atomic read-modify-write operations, sequential consistency, relaxed memory order, mutexes, and call_once.

Also explained in

Compare how 4 books explain acquire-release semantics →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C. The section links point to the parts of the book it draws on.