Chapter 21: Atomic access and memory consistency
The introduction frames memory consistency in terms of concurrent access, effects on a program’s abstract state, and the limits of a single timeline across threads.
After this chapter you will be able to
- Concurrent access and visibility. Distinguish the visibility and consistency concerns raised by ordinary C execution, atomic objects, and threads, and name the synchronization examples given here.
- Abstract state and effects. Describe the components of a program execution’s abstract state and explain why the section calls every evaluation an effect.
- No shared thread timeline. Explain why the introduction avoids assuming one fine-grained time or fully observable overall state for a multithreaded execution.
- Happened-before chains. Explain how sequenced-before and synchronizes-with relationships combine to establish that one evaluation happened before another.
- Acquire-release synchronization. Explain when an atomic read synchronizes with a write in another thread, including the two sides of a read-modify-write operation.
- Visibility across synchronization. Trace how effects made before a release become visible after a matching acquire, using the section’s atomic examples.
- Thread creation and joining. Explain what synchronization through thrd_create and thrd_join establishes, particularly what the joining thread can observe after joining its threads.
- One-time initialization visibility. Explain how call_once makes initialization performed by its callback visible to other threads calling it with the same object.
- Mutex critical-section visibility. Explain why critical sections protected by the same mutex occur sequentially and why a later section sees effects of earlier ones.
- Condition-wait mutex handoff. Describe the release and acquire operations performed by cnd_wait and cnd_timedwait and identify where their synchronization occurs.
- Condition-signal placement. Justify placing cnd_signal or cnd_broadcast inside a critical section protected by the waiters’ mutex when signaling changes to non-atomic shared data.
- Global sequential ordering. Explain how sequential consistency orders atomic operations across different objects and how it strengthens acquire-release consistency.
- Explicit atomic memory orders. Explain how an atomic operation’s _explicit interface uses a memory_order argument and how that compares with the interface without _explicit.
- Orders and typical operations. Distinguish the ordering roles and typical uses of acquire-release, release, acquire, and relaxed atomic operations.
- Operation-specific ordering limits. Explain why stores, loads, and failed compare-exchanges have different allowed orders, including the two orders supplied to explicit compare-exchange.
What it builds on
- Critical data and critical sections (chapter 20) · Shared data and critical sections in threaded programs.
- The abstract state machine (chapter 5) · The abstract state machine view of program execution.
- (chapter intro) (chapter 20) · The existence of concurrent threads.
- Sequencing (chapter 19) · Introduces sequencing of evaluations.
- Simple interthread control (chapter 20) · Provides the context of controlling communication between threads.
- Race-free initialization and destruction (chapter 20) · The errlog initialization example.
- Communicating through condition variables (chapter 20) · Communicating through condition variables.
Section by section
Introduction
The introduction frames memory consistency in terms of concurrent access, effects on a program’s abstract state, and the limits of a single timeline across threads.
- Concurrent access and visibility: Distinguish the visibility and consistency concerns raised by ordinary C execution, atomic objects, and threads, and name the synchronization examples given here.
Why it matters: These distinctions explain why reasoning about shared data requires more than reading statements in source order. - Abstract state and effects: Describe the components of a program execution’s abstract state and explain why the section calls every evaluation an effect.
Why it matters: It gives you a broader way to describe what execution changes besides writes to memory. - No shared thread timeline: Explain why the introduction avoids assuming one fine-grained time or fully observable overall state for a multithreaded execution.
Why it matters: It cautions against reasoning about concurrent execution as though all threads shared one precise clock.
Key terms: sequence points, atomic objects, threads, synchronization, abstract state, effect, points of execution, intermediate values, stored values, hidden state
21.1 The “happened-before” relation
Explains how sequencing within threads and synchronization between threads establish happened-before relationships that make effects visible across threads.
- Happened-before chains: Explain how sequenced-before and synchronizes-with relationships combine to establish that one evaluation happened before another.
Why it matters: The chain gives you a basis for saying which events are ordered, even when they occur in different threads. - Atomic modification order: Describe the modification order of an atomic object and how it relates to the order of operations in threads that use that object.
Why it matters: It lets you reason about changes to a shared atomic object without losing the ordering imposed within each thread. - Acquire-release synchronization: Explain when an atomic read synchronizes with a write in another thread, including the two sides of a read-modify-write operation.
Why it matters: Reading the written value is what establishes the cross-thread link; using the same atomic object alone is not enough. - Visibility across synchronization: Trace how effects made before a release become visible after a matching acquire, using the section’s atomic examples.
Why it matters: This is how reasoning about an atomic exchange helps establish what one thread can observe of another thread’s earlier work.
Key terms: happened-before relation, sequenced-before relation, synchronizes-with relation, transitive closure, atomic object, modification order, acquire semantics, release semantics, read-modify-write operations, synchronizes with
21.2 C library calls that provide synchronization
This section explains how C library calls synchronize threads through release and acquire operations, making earlier effects visible to later operations.
- Thread creation and joining: Explain what synchronization through thrd_create and thrd_join establishes, particularly what the joining thread can observe after joining its threads.
Why it matters: Joining threads provides a basis for using their completed work. - One-time initialization visibility: Explain how call_once makes initialization performed by its callback visible to other threads calling it with the same object.
Why it matters: It explains why threads can safely use an object after passing its one-time initialization call. - Mutex critical-section visibility: Explain why critical sections protected by the same mutex occur sequentially and why a later section sees effects of earlier ones.
Why it matters: A mutex protects shared data by making one protected update visible to the next holder. - Condition-wait mutex handoff: Describe the release and acquire operations performed by cnd_wait and cnd_timedwait and identify where their synchronization occurs.
Why it matters: The wait operation’s mutex handoff determines which shared changes a returning waiter can rely on. - Condition-signal placement: Justify placing cnd_signal or cnd_broadcast inside a critical section protected by the waiters’ mutex when signaling changes to non-atomic shared data.
Why it matters: Waking a thread is not, by itself, enough to ensure it sees non-atomic changes to the condition it checks.
Key terms: thrd_create, thrd_join, thrd_exit, release, acquire, call_once, release operation, acquire operation, errlog, mutex
21.3 Sequential consistency
The section explains the global ordering required by sequential consistency, the default consistency of atomic operators and interfaces, and the exception for atomic_init.
- Global sequential ordering: Explain how sequential consistency orders atomic operations across different objects and how it strengthens acquire-release consistency.
Why it matters: This stronger ordering can constrain parallel execution of atomic accesses. - Default atomic interface behavior: State the default consistency of atomic operators and functional interfaces, and distinguish a fetch-and-operator return value from a compound assignment result.
Why it matters: The default ordering and the returned value both affect how an atomic expression behaves. - Atomic initialization exception: Distinguish atomic_init from synchronizing atomic operations, including the consequence of concurrent calls.
Why it matters: An initialization call should not be mistaken for a way to synchronize threads.
Key terms: sequential consistency, acquire-release consistency, global modification order, functional interfaces, fetch and operator interfaces, compound assignment operator, atomic_init, synchronization, race
21.4 Other consistency models
This section explains how C’s explicit atomic interfaces select memory consistency orders, which orders fit particular operations, and why consume ordering exists.
- Explicit atomic memory orders: Explain how an atomic operation’s _explicit interface uses a memory_order argument and how that compares with the interface without _explicit.
Why it matters: It lets you recognize where an atomic operation’s consistency requirements are specified. - Orders and typical operations: Distinguish the ordering roles and typical uses of acquire-release, release, acquire, and relaxed atomic operations.
Why it matters: The operation’s role determines which synchronization requirements are useful. - Operation-specific ordering limits: Explain why stores, loads, and failed compare-exchanges have different allowed orders, including the two orders supplied to explicit compare-exchange.
Why it matters: These limits prevent requesting synchronization semantics that an operation cannot perform. - Consume ordering motivation: Explain the limited motivation given for consume consistency and the caution about using it.
Why it matters: It explains why consume exists without suggesting that its detailed behavior was covered here.
Key terms: _explicit, memory_order, memory_order_seq_cst, memory_order_acq_rel, memory_order_release, memory_order_acquire, memory_order_relaxed, read-modify-write, atomic_store, atomic_flag_clear
21.5 Summary
Concepts explained in this chapter
- synchronizationA synchronization event is an operation or occurrence that coordinates concurrent execution and can establish an ordering relationship between threads.
- acquire-release semanticsRelease semantics are memory-ordering properties that make preceding effects available to a thread that synchronizes with the release operation.
- acquire semanticsAcquire semantics are memory-ordering properties that make effects preceding a matching release operation visible after the acquire operation.
- happens-before relationA happened-before relation is a partial ordering that establishes that one event in a concurrent execution precedes another for purposes of reasoning about their effects.
- evaluationAn evaluation is the execution of a C expression or other construct to determine its value and associated effects.
- modification orderModification order is the total order of modifications to a particular atomic object that is consistent with each participating thread's sequenced-before order.
- atomic typeA type specifies the kind of values an entity can represent and the operations applicable to those values.
- read-modify-write operationA read-modify-write operation atomically reads an object, computes or selects a new value, and writes that value as one indivisible operation.
- synchronizes-with relationA synchronizes-with relation is an ordering relationship in which an acquire evaluation reads a value written by a release evaluation in another thread.
- call_oncecall_once is a C threads-library function that invokes a callback at most once for a specified once-control object, coordinating concurrent callers.
- cnd_broadcastcnd_broadcast is a C threads-library function that wakes all threads waiting on a condition variable.
- cnd_signalcnd_signal is a C threads-library function that wakes one thread waiting on a condition variable.
- cnd_timedwaitcnd_timedwait is a C threads-library function that atomically releases a mutex while waiting for a condition variable and returns when signaled or when an absolute time limit is reached.
- cnd_waitcnd_wait is a C threads-library function that atomically releases a mutex while waiting on a condition variable and reacquires it before returning.
- condition variableA condition variable is a thread-synchronization object that allows threads to wait until a shared-state condition may have become true.
- lockingA mutex is a mutual-exclusion synchronization object that allows only one thread at a time to hold a protected critical section.
- release operationA release operation is a synchronization operation that makes preceding effects available to a corresponding acquire operation.
- thrd_jointhrd_join is a C threads-library function that blocks the calling thread until a specified thread terminates and optionally retrieves its result.
- side effectA side effect is an observable change to program state caused by evaluating an expression or executing an operation.
- mtx_lockmtx_lock is a C threads-library function that blocks until the calling thread acquires a mutex.
- mtx_unlockmtx_unlock is a C threads-library function that releases a mutex previously acquired by the calling thread.
- stateHidden state is implementation or library state that affects program behavior without being directly represented by ordinary program values.
- race conditionA race condition is an execution condition in which the result depends on the relative timing or interleaving of accesses by concurrent or asynchronously executing code.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
