Example demos

Runnable companions to the book.

Every claim made in the book has a demo behind it. Each demo is a self-contained Docker project with its own ./demo.sh and its own Containerfile(s). Read the matching chapter for context, then run the demo from its directory.

Runnable examples

Runnable companions to the book. Each one makes a single pattern concrete and runnable with one command. Each card links to the demo's page on this site (a rendering of the README with cross-references), and each page links to the source on GitHub. Each demo also stands alone — run it without reading the book first if you'd rather poke at it directly.

Chapter · 01

Four Execution Models

Counts distinct CPUs touched, peak in-flight tasks, and interleaves across sequential, concurrent-pinned, parallel, and oversubscribed models — proof by observable, not by stopwatch.

Read page →
Chapter · 02

Cache Hierarchy Latency

A single-threaded pointer-chase walks a random permutation at five working-set sizes from 8 KiB to 256 MiB. Latency rises monotonically as each step outgrows the next cache level.

Read page →
Chapter · 03

Deadline Propagation

A four-hop call chain shares one steady_clock deadline end to end. Injecting a slow hop anywhere in the chain still keeps the total wall-clock budget, and a hop entered past its deadline does no work at all.

Read page →
Chapter · 04

Clean jthread Shutdown

SIGTERM sets a lock-free flag; the main thread then calls request_stop() and join() on every worker itself, since request_stop() isn't signal-safe. A Lua harness sends a real SIGTERM and asserts exit 0 inside the grace period, with no forced kill.

Read page →
Chapter · 05

Futex Cost Under Contention

Baseline, uncontended, and contended modes run the identical counter increments. When strace is available, futex counts show the contended case crossing into the kernel far more often than the uncontended one, which should match the no-lock baseline.

Read page →
Chapter · 06

Lock Ordering & Deadlock

A std::latch forces two threads into a textbook AB/BA deadlock under lock_guard on every single run. The identical opposite-order acquisition under std::scoped_lock always completes, because its internal algorithm doesn't care what order its own arguments name.

Read page →
Chapter · 07

Barrier-Phased Merge

A fixed worker pool runs barrier-synchronized rounds, each worker writing a deterministic value into its own slot before arriving. The completion function sums every slot with no extra lock, and the final total matches a closed-form expectation.

Read page →
Chapter · 08

Atomics & Memory Ordering

A one-slot producer/consumer hand-off runs under sequential consistency, under the minimal acquire/release pairing the hand-off actually needs, and under a deliberately unsynchronized mode. The first two always complete correctly; the third reliably stalls from a compiler hoisting a stale read out of the consumer's loop.

Read page →
Chapter · 09

SPSC Ring Buffer

A single-producer/single-consumer ring buffer is checked per item, not just by aggregate sum, for loss, duplication, and reordering across capacities and padding settings. A padded head/tail layout shows the throughput cost of two unrelated counters sharing one cache line.

Read page →
Chapter · 10

Launch Policies & packaged_task

Explicit async and deferred launch policies are checked against the thread id each task actually ran on, not trusted on name alone. A small packaged_task worker pool proves an exception thrown by one task surfaces intact at its own future::get(), leaving every other task unaffected.

Read page →
Chapter · 11

Coroutines and HALO

A co_yield-based generator and a co_await-able task type using symmetric transfer are both hand-rolled, since C++20 has no standard coroutine type. Allocation-counting probes compare a stateless coroutine against one carrying a live 4 KiB buffer across a suspension point.

Read page →
Chapter · 12

Strand vs. Mutex Serialization

The identical unsynchronized counter is incremented by several threads through a shared Asio strand and directly on a multithreaded io_context. The strand always lands on the exact count with zero overlaps; posted directly, real overlaps appear.

Read page →
Chapter · 13

Fibers vs. Threads

One worker loop runs unmodified over Boost.Fiber fibers and over std::thread workers, comparing peak RSS for the same work. A separate guard-page demo turns a fiber stack overflow into an immediate, contained fault instead of silent memory corruption.

Read page →
Chapter · 14

seq vs par vs par_unseq

The same sort and reduction run under std::execution::seq, par, and par_unseq against one fixed dataset. The sort hash and reduction sum must match exactly across all three policies; a CPU-usage probe reports, without asserting, whether par actually spread across cores.

Read page →
Chapter · 15

Shared Queue vs. Work-Stealing

A single-queue thread pool and a work-stealing pool run the identical CPU-bound task mix. A companion probe reads the real cgroup CPU quota from cpu.max and compares it against the raw host core count hardware_concurrency() reports.

Read page →
Chapter · 16

NUMA Placement & Locality

A disposable two-node KVM guest, not a container, gives this demo a real NUMA topology to be remote across. numa_alloc_onnode placement, first-touch binding, and a cpuset.mems fallback are all checked directly; local-vs-remote latency is reported for context.

Read page →
Chapter · 17

AoS vs. SoA & SIMD

The identical particle-update arithmetic runs scalar over an array-of-structs layout, scalar over a struct-of-arrays layout, and vectorized over struct-of-arrays with std::experimental::simd. All three must hash to bit-identical final positions.

Read page →
Chapter · 18

A Harness Checked Against Itself

A trivial atomic fetch_add workload is timed with warmup, per-iteration sampling, and percentile and coordinated-omission reporting in one mode, and with a single naive wall-clock measurement in another. The missing percentile fields in naive mode are the methodology gap made visible.

Read page →
Chapter · 20

False Sharing & Padding

Two pinned threads each increment their own atomic counter, with and without cache-line padding between the two counters. Correctness holds either way, but the unpadded layout runs measurably slower from coherence traffic on a line neither thread actually shares.

Read page →
Chapter · 21

Disruptor-Style Sequence Barrier

One producer feeds a journal consumer and a dependent business-logic consumer over a fixed-capacity ring. Every event is checked for in-order single-consumption, the dependent stage never reads ahead of the journal, and back-pressure engages deterministically.

Read page →
Chapter · 24

Back-Pressure & Load Shedding

A Boost.Beast async HTTP server enforces an in-flight-request cap with one atomic counter, answering HTTP 503 before a handler ever runs. A two-phase load test proves zero rejections below the cap and clean shedding with no dropped connections above it.

Read page →
Chapter · 25

Bounded gRPC Admission

A raw CompletionQueue server implements the CallData state-machine pattern with an explicit admission cap, rejecting overflow calls with RESOURCE_EXHAUSTED instead of streaming anything. Concurrent streaming clients prove the cap holds exactly and the server drains cleanly on SIGTERM.

Read page →
Chapter · 27

Real Races, Caught Live

A racy, unsynchronized counter is proven broken by a ThreadSanitizer build, independent of what any single plain run happens to print. A latch-forced AB/BA deadlock is attached to live with gdb to read the lock cycle directly off both threads' backtraces.

Read page →
Chapter · 28

Two Detectors, One Bug

The same racy counter is checked under ThreadSanitizer's compile-time instrumentation and Valgrind Helgrind's lockset/happens-before hybrid, two independent algorithms agreeing on one source line. A detached thread reading through a freed buffer is caught by AddressSanitizer, naming both the allocation and the free.

Read page →
Chapter · 29

Tracing Across a Thread Pool

A thread pool captures the submitting request's OpenTelemetry context and hands it to worker threads so pool.task spans nest correctly under their request.handle root. A docker compose stack with Tempo and Mimir backs every assertion with a real trace and metric query.

Read page →
Chapter · 32

cgroup Quota vs. hardware_concurrency()

A probe reads cpu.max directly, derives a pool size from the tighter of the real quota and the affinity-visible core count, and runs a small CPU-bound pool-bench sized to that result instead of the host's raw core count.

Read page →
Chapter · 33

Ingest / Match / Publish Pipeline

Multiple ingest producers feed a bounded MPMC ring into a single pinned matching thread running a real price-time-priority order book, which feeds a bounded multi-consumer ring to a publish pool. Sustained and overload load shapes prove conservation, single-writer discipline, and deterministic back-pressure.

Read page →
Chapter · 34

Build Harness Reference Slice

A tiny shared-counter program is built with gcc (always) and clang plus ThreadSanitizer (best-effort) to check gcc-vs-clang output parity and confirm a deliberately injected race is flagged only under the TSan build. conan.lock pins the exact declared dependency end to end.

Read page →

Running every demo

Clone the repo and invoke each demo.sh from its directory. The scripts assume Docker and a handful of host tools (hey, jq, curl).

git clone https://github.com/patterncatalyst/cpp-concurrency-and-parallelism.git
cd cpp-concurrency-and-parallelism/examples/<chapter-slug>
./demo.sh