liveBook · Manning

Happens-before relation

As explained in C++ Concurrency in Action, Second Edition

A C++ memory-model ordering relation between evaluations that establishes when one precedes another, including across threads.

The happens-before relation establishes an ordering between evaluations: it determines when an earlier operation’s effects must be accounted for by a later one. Within one thread, an operation sequenced before another also happens before it. Across threads, synchronization can create the link; happens-before is transitive, so ordering can extend through several operations. section 5.3.2

Publishing ordinary data with an atomic flag

A writer adds 42 to a vector, then sets an atomic ready flag. A reader waits until it observes true before reading the vector. The vector write is sequenced before the flag write; the flag write synchronizes with the read that observes true; that read is sequenced before the vector read. Transitivity puts the non-atomic vector write before the non-atomic read. Without enforced ordering, those accesses would be unsafe. section 5.3

Listing 5.2. Reading and writing variables from different threads
#include <vector>
#include <atomic>
#include <iostream>
std::vector<int> data;
std::atomic<bool> data_ready(false);
void reader_thread()
{
    while(!data_ready.load())                   #1
    {
        std::this_thread::sleep(std::chrono::milliseconds(1));
    }
    std::cout<<"The answer="<<data[0]<<"\n";    #2
}
void writer_thread()
{
    data.push_back(42);                         #3
    data_ready=true;                            #4
}


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":7,\"ch\":48},{\"line\":7,\"ch\":50}],[{\"line\":11,\"ch\":48},{\"line\":11,\"ch\":50}],[{\"line\":15,\"ch\":48},{\"line\":15,\"ch\":50}],[{\"line\":16,\"ch\":48},{\"line\":16,\"ch\":50}]]"}
!@%STYLE%@!

Follow the numbered operations from the vector write through the atomic flag to the vector read. The waiting loop is inefficient, but the ordering established when it reads true is the point of the example. section 5.3

Source-code proximity alone does not order evaluations across threads. Even within one statement, evaluations generally need not be sequenced relative to each other: two calls used as function arguments can produce either argument order. For cross-thread non-atomic data, look for a complete chain from the write to the read, not merely an atomic variable somewhere nearby. section 5.3.2 section 5.3.7

The same reasoning guides other synchronization mechanisms:

  • A spinlock’s release unlock synchronizes with a subsequent acquire lock. Changes made while holding the lock then happen before accesses made after the next thread acquires it. section 5.3.7
  • A lock-free stack needs its node’s non-atomic next field initialized before a pop reads it. A release compare/exchange during push and an acquire compare/exchange during pop supply the required link when the acquire observes the published pointer; failed retries can use relaxed ordering. section 7.2.5
  • Release and acquire fences can also establish ordering around relaxed atomic operations when the specified load observes the relevant store; a fence on only one side does not provide the demonstrated pairing. section 5.3.5

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Derives ordering through sequencing, synchronization, and transitivity, then applies it to mutexes and a lock-free stack.
Examples
Traces an atomic ready flag, unspecified function-argument order, a spinlock, and lock-free stack publication.
What it assumes
Assumes basic single-thread sequencing and atomic operations; develops their cross-thread ordering implications through examples.
What you can do after
Diagnose/debug with it by tracing whether synchronization orders each non-atomic write before a cross-thread read.
How it connects
Connects sequenced-before, synchronizes-with, acquire–release ordering, fences, mutexes, and non-atomic data races.

Also explained in

Compare how 5 books explain happens-before relation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.