liveBook · Manning

Chapter 5: Chapter 5. The C++ memory model and operations on atomic types

What you will learn in chapter 5 of C++ Concurrency in Action, Second Edition

This introduction explains why C++ needs a multithreading-aware memory model, when programmers need its details, and how the chapter will approach atomic synchronization.

After this chapter you will be able to

What it builds on

Section by section

Introduction

This introduction explains why C++ needs a multithreading-aware memory model, when programmers need its details, and how the chapter will approach atomic synchronization.

Key terms: multithreading-aware memory model, mutexes, condition variables, futures, latches, barriers, atomic types, atomic operations, synchronization, lock-free data structures

5.1 Memory model basics

Introduces the structural and concurrency aspects of the C++ memory model and explains why the discussion begins with objects and memory locations.

Key terms: memory model, structural aspects, concurrency aspects, objects, memory locations, low-level atomic operations

5.1.1 Objects and memory locations

This section defines C++ objects and memory locations, then shows how ordinary members and bit fields divide a struct’s storage.

Key terms: object, sub-object, memory location, scalar type, adjacent bit fields, zero-length bit field

5.1.2 Objects, memory locations, and concurrency

The section explains when concurrent access to a memory location becomes a data race and how ordering or atomic access keeps behavior defined.

Key terms: memory location, read-only data, race condition, enforced ordering, mutexes, synchronization properties of atomic operations, data race, undefined behavior, atomic operations

5.1.3 Modification orders

This section defines each object’s modification order and explains the agreement and ordering constraints it imposes on threads.

Key terms: modification order, initialization, object, non-atomic, atomic operations, data race, undefined behavior, synchronization, speculative execution, subsequent reads

5.2 Atomic operations and types in C++

This section introduces atomic operations as indivisible, contrasts them with potentially mixed observations of non-atomic operations, and explains why C++ generally uses atomic types for atomic operations.

Key terms: atomic operation, atomic load, atomic type, non-atomic operation, data race, undefined behavior

5.2.1 The standard atomic types

This section introduces the standard atomic types, how to check whether they are lock-free, their available operations, and the memory-ordering arguments those operations accept.

Key terms: <atomic>, is_lock_free(), is_always_lock_free, ATOMIC_*_LOCK_FREE, std::atomic_flag, test_and_set(), clear(), std::atomic<T>, atomic_ prefix, atomic typedefs

5.2.2 Operations on std::atomic_flag

This section introduces std::atomic_flag’s initialization and operations, then uses it to build a basic spin-lock mutex while explaining its limitations.

Key terms: std::atomic_flag, ATOMIC_FLAG_INIT, clear state, lock-free, static storage duration, clear(), test_and_set(), read-modify-write, memory_order_acquire, memory_order_acq_rel

5.2.3 Operations on std::atomic<bool>

This section introduces the operations on std::atomic<bool>, especially compare-exchange, and explains how retries, memory ordering, and lock-free support affect their use.

Key terms: std::atomic<bool>, std::atomic_flag, store(), load(), exchange(), assignment operator, non-atomic bool, compare-exchange, expected, desired

5.2.4 Operations on std::atomic<T*>: pointer arithmetic

This section explains the interface and pointer arithmetic of std::atomic<T*>, including return values and memory-ordering options.

Key terms: std::atomic<T*>, load(), store(), exchange(), compare_exchange_weak(), compare_exchange_strong(), fetch_add(), fetch_sub(), exchange-and-add, +=

5.2.5 Operations on standard atomic integral types

This section describes the extra operations available on standard atomic integral types and distinguishes which value each form returns.

Key terms: atomic integral types, fetch_add(), fetch_sub(), fetch_and(), fetch_or(), fetch_xor(), compound-assignment operators, pre-increment, post-increment

5.2.6 The std::atomic<> primary class template

Explains which types can use the std::atomic<> primary template, what operations it offers, and why representation and implementation choices matter.

Key terms: std::atomic<UDT>, primary template, trivial copy-assignment operator, load(), store(), exchange(), bitwise comparison, memcmp, compare_exchange_strong, compare_exchange_weak

5.2.7 Free functions for atomic operations

This section explains the free-function forms of atomic operations, their use with shared pointers, and why the atomic shared-pointer type can be a safer choice.

Key terms: free functions, atomic_, _explicit, memory-ordering tag, std::atomic_compare_exchange_weak_explicit, success and failure memory orders, std::atomic_flag_test_and_set, std::atomic_flag_clear, std::shared_ptr, atomic load

5.3 Synchronizing operations and enforcing ordering

This section uses an atomic ready flag to show how synchronization can order non-atomic data accesses across threads.

Key terms: std::atomic<bool>, data_ready, synchronizes-with, happens-before, non-atomic, default atomic operations, ordering requirements

5.3.1 The synchronizes-with relationship

Defines when an atomic write synchronizes with an atomic read and explains that memory-order tagging determines whether the relationship applies.

Key terms: synchronizes-with relationship, atomic write, atomic read, atomic read-modify-write operations, fetch_add(), compare_exchange_weak(), suitably-tagged, memory ordering, ordering constraints

5.3.2 The happens-before relationship

This section explains how sequencing within a thread and synchronization between threads establish happens-before ordering, and where strongly-happens-before differs.

Key terms: sequenced-before, happens-before, strongly-happens-before, synchronizes-with, inter-thread happens-before, transitive, memory_order_consume

5.3.3 Memory ordering for atomic operations

This section compares sequentially consistent, relaxed, and acquire-release atomic ordering by showing which observations and synchronization each permits.

Key terms: memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst, single total ordering, sequential consistency, synchronizes-with, modification order

5.3.4 Release sequences and synchronizes-with

This section explains how a release sequence lets an atomic store synchronize with later loads through intervening read-modify-write operations, and why that matters for consumers of a shared queue.

Key terms: release sequence, synchronizes-with, dependency-ordered-before, read-modify-write, memory_order_release, memory_order_acquire, atomic<int>, count.store, fetch_sub, shared buffer

5.3.5 Fences

This section shows how release and acquire fences can order relaxed atomic operations, why fence placement matters, and how that ordering can ultimately protect non-atomic data.

Key terms: fence, memory barrier, memory_order_relaxed, release fence, acquire fence, synchronizes-with, happens-before, synchronization point, non-atomic operations, data race

5.3.6 Ordering non-atomic operations with atomics

This section shows how atomic operations and fences can order accesses to non-atomic data across threads without a data race.

Key terms: fences, non-atomic bool, happens-before, data race, atomic, memory_order_release, memory_order_consume, non-atomic accesses, memory_order_relaxed

5.3.7 Ordering non-atomic operations

This section shows how synchronization operations establish happens-before ordering for non-atomic data, then identifies the guarantees provided by C++ concurrency facilities.

Key terms: non-atomic operation, sequenced before, synchronizes-with, happens-before, std::atomic_flag, test_and_set, clear, std::memory_order_acquire, std::memory_order_release, lock

5.4 Summary

The chapter recap connects C++ atomic types, memory ordering, and fences to cross-thread synchronization, then points toward their use in concurrent containers and parallel algorithms.

Where this is used later

Concepts explained in this chapter

← Chapter 4: Chapter 4. Synchronizing concurrent operations Chapter 6: Chapter 6. Designing lock-based concurrent data structures →

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.