liveBook · Manning

Synchronization

As explained in Modern C, Third Edition

A synchronization event is an operation or occurrence that coordinates concurrent execution and can establish an ordering relationship between threads.

Two threads can access the same data without running in a predictable order. Synchronization provides an ordering relationship between their operations. In C, that relationship can arise when an atomic operation reads a value written by another thread, or through specific paired library calls. section 21.1 section 21.2

Reading a value written by another thread

Suppose `_Atomic(unsigned) x = 11;`. Thread A executes `++x; x = 31; … unsigned y = x;`. Thread B executes `x--; x = 5;`. In one possible execution, B’s decrement reads A’s `31`, and A later reads B’s `5` into `y`. Each read establishes a synchronization with the write whose value it observes. The modifications of `x` have an order consistent with each thread’s own sequenced-before order; this example does not prescribe every possible interleaving. section 21.1

Table 21.1 C library functions that form synchronization pairs
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

Use the release/acquire pairs to identify which library operations can order work across threads: creation and entry, thread completion and join, first and subsequent `call_once` calls, and mutex release and acquisition. section 21.2

For example, after `thrd_join` completes, the joined thread’s effects are visible to the joining thread. With `call_once`, writes made during the first callback are visible after subsequent calls using the same once object. A mutex likewise synchronizes release with acquisition. section 21.2

TAKEAWAY: Reason about cross-thread timing through happened-before, not an assumed schedule. Atomic objects and specific C library calls establish the relevant synchronization. Sequential consistency is the default for atomics, but not for other library calls; explicitly choosing acquire-release consistency requires careful design. section 21.5

How Modern C, Third Edition covers it

How deep it goes
Explains atomic modification order and release/acquire library-call pairs, while setting aside consume consistency and dependency-ordered before.
Examples
Traces two threads reading and modifying one atomic variable, then identifies synchronization through thread joins and once-only initialization.
What it assumes
Assumes familiarity with C threads, atomic objects, and the previously introduced sequenced-before relation.
What you can do after
Evaluate/understand cross-thread visibility by identifying observed atomic writes and paired release/acquire library calls.
How it connects
Connects happened-before and sequenced-before to atomic objects, mutexes, thread joins, call_once, and sequential consistency.

Also explained in

Compare how 11 books explain synchronization →

Related concepts

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