liveBook · Manning

Atomic type

As explained in Modern C

A type is a C specification that determines the representation and permitted values or operations of an entity.

Suppose one thread sets a shared `finished` flag while another checks it. An ordinary `bool` is not enough: even its update may be divisible between threads, and conflicting unprotected accesses can leave the program in an undefined state. Declaring the member `_Atomic(bool) finished;` tells C that it needs atomic access. section 18.1

40     // Parameters that will dynamically be changed by
41     // different threads
42     _Atomic(size_t) constellations; //< Constellations visited
43     _Atomic(size_t) x0;             //< Cursor position, row
44     _Atomic(size_t) x1;             //< Cursor position, column
45     _Atomic(size_t) frames;         //< FPS for display
46     _Atomic(bool)   finished;      //< This game is finished.


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":2}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":2}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":2}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":14}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":14}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":14}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":14}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":14}],[{\"line\":2,\"ch\":15},{\"line\":2,\"ch\":21}],[{\"line\":3,\"ch\":15},{\"line\":3,\"ch\":21}],[{\"line\":4,\"ch\":15},{\"line\":4,\"ch\":21}],[{\"line\":5,\"ch\":15},{\"line\":5,\"ch\":21}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":2}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":14}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":14}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":14}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":14}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":14}],[{\"line\":2,\"ch\":15},{\"line\":2,\"ch\":21}],[{\"line\":3,\"ch\":15},{\"line\":3,\"ch\":21}],[{\"line\":4,\"ch\":15},{\"line\":4,\"ch\":21}],[{\"line\":5,\"ch\":15},{\"line\":5,\"ch\":21}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":2}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":14}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":14}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":14}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":14}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":14}],[{\"line\":2,\"ch\":15},{\"line\":2,\"ch\":21}],[{\"line\":3,\"ch\":15},{\"line\":3,\"ch\":21}],[{\"line\":4,\"ch\":15},{\"line\":4,\"ch\":21}],[{\"line\":5,\"ch\":15},{\"line\":5,\"ch\":21}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":2}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":14}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":14}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":14}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":14}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":14}],[{\"line\":2,\"ch\":15},{\"line\":2,\"ch\":21}],[{\"line\":3,\"ch\":15},{\"line\":3,\"ch\":21}],[{\"line\":4,\"ch\":15},{\"line\":4,\"ch\":21}],[{\"line\":5,\"ch\":15},{\"line\":5,\"ch\":21}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":2}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":14}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":14}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":14}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":14}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":14}]]"}
!@%STYLE%@!

The declarations apply `_Atomic` to the flag, cursor coordinates, and other members changed by different threads. Each member has its own atomic type. section 18.1

TAKEAWAY: In different threads, standard operations on atomic objects are indivisible and linearizable. Indivisible means an operation cannot be observed partway through. Linearizability also lets threads reason about the ordering of computations: when one thread observes `finished` set to true, it can rely on the setting thread having performed the actions that precede that signal. Atomic operations thus help establish synchronization, rather than merely protecting a single value. section 18.1 section 19.5

The primary declaration syntax is the atomic specifier `_Atomic(T)`, where `T` is the type from which the atomic type is derived. `_Atomic` also has a qualifier form. Placement matters in compound declarations: an atomic pointer and a pointer to an atomic object are different types. Nor can an array itself be atomic, although its elements can have an atomic type. section 18.1

   _Atomic(double[45]) C;   // Invalid: atomic cannot be applied to arrays.
   _Atomic(double) D[45];   // Valid: atomic can be applied to array base.


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":3},{\"line\":0,\"ch\":10}],[{\"line\":1,\"ch\":3},{\"line\":1,\"ch\":10}],[{\"line\":0,\"ch\":11},{\"line\":0,\"ch\":17}],[{\"line\":1,\"ch\":11},{\"line\":1,\"ch\":17}],[{\"line\":0,\"ch\":3},{\"line\":0,\"ch\":10}],[{\"line\":1,\"ch\":3},{\"line\":1,\"ch\":10}],[{\"line\":0,\"ch\":11},{\"line\":0,\"ch\":17}],[{\"line\":1,\"ch\":11},{\"line\":1,\"ch\":17}]]"}
!@%STYLE%@!

`_Atomic(double[45])` attempts to make an array atomic and is invalid; `_Atomic(double) D[45]` declares an array whose elements are atomic. section 18.1

Signal handlers need a related but narrower guarantee: an operation must appear either not to have started or to have completed when the handler runs. `sig_atomic_t` provides uninterruptible loads and stores, not a general-purpose atomic counter; `++` may involve separate load, increment, and store steps. C11 also provides `atomic_flag` and other atomic types with the lock-free property for this setting, where supported. section 17.6

Use atomic types for small, critical data shared between threads. They do not replace every synchronization tool: code operating on unprotected shared data generally needs a mutex. Atomics also have a memory-consistency model. Sequential consistency is the default; explicit acquire-release consistency can be more efficient, but requires careful design. section 18.7 section 19.5

How Modern C covers it

How deep it goes
Explains declaration forms, indivisibility, linearizability, array restrictions, signal-handler limits, and consistency choices without developing an atomic algorithm here.
Examples
Uses a shared completion flag and cursor members, contrasting valid atomic array elements with an invalid atomic array.
What it assumes
Assumes familiarity with C declarations and values; introduces conflicting thread accesses and atomic declaration syntax in context.
What you can do after
Apply `_Atomic` to suitable shared members and distinguish atomic access from cases requiring a mutex or stronger signal-handler guarantees.
How it connects
Connects atomic types to race conditions, signal handlers, mutexes, happens-before synchronization, and memory consistency.

Also explained in

Compare how 17 books explain atomic type →

Related concepts

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