liveBook · Manning

Volatile qualification

As explained in Modern C

A volatile qualification is a C type qualifier indicating that an object's value may change independently of ordinary program execution and must not be optimized away from accesses.

Keeping a measurement loop

An empty loop can disappear during compilation because it has no effect. In `for (uint64_t volatile i = 0; i < iterations; ++i) { }`, the qualifier keeps accesses to `i` from being optimized away. That preserves a loop to measure, but it also changes what the measurement includes. section 15.3

TAKEAWAY: An access to a volatile object reloads its value from memory; a modification stores it to memory. The qualifier applies in a declaration like `unsigned volatile depth = 0`. It blocks optimizations that would otherwise make assumptions about that object's value, so use it only when needed. section 17.5

That cost matters in the loop example. GCC and Clang emit repeated loads and stores for the volatile counter. Keeping an ordinary counter and making only an assignment to an auxiliary object volatile produces a shorter loop; the measured version with that store was 80% faster than the volatile-counter version. Volatile preserves the intended accesses, not a negligible measurement overhead. section 15.3

Listing 15.6. Instrumenting three for loops with struct timespec
53      timespec_get(&t[0], TIME_UTC);
54      /* Volatile for i ensures that the loop is effected */
55      for (uint64_t volatile i = 0; i < iterations; ++i) {
56        /* do nothing */
57      }
58      timespec_get(&t[1], TIME_UTC);
59      /* s must be volatile to ensure that the loop is effected */
60      for (uint64_t i = 0; i < iterations; ++i) {
61        s = i;
62      }
63      timespec_get(&t[2], TIME_UTC);
64      /* Opaque computation ensures that the loop is effected */
65      for (uint64_t i = 1; accu0 < upper; i += 2) {
66        accu0 += i;
67      }
68      timespec_get(&t[3], TIME_UTC);


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":2}],[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":20}],[{\"line\":5,\"ch\":8},{\"line\":5,\"ch\":20}],[{\"line\":10,\"ch\":8},{\"line\":10,\"ch\":20}],[{\"line\":15,\"ch\":8},{\"line\":15,\"ch\":20}],[{\"line\":0,\"ch\":28},{\"line\":0,\"ch\":36}],[{\"line\":5,\"ch\":28},{\"line\":5,\"ch\":36}],[{\"line\":10,\"ch\":28},{\"line\":10,\"ch\":36}],[{\"line\":15,\"ch\":28},{\"line\":15,\"ch\":36}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":2}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":2}],[{\"line\":1,\"ch\":20},{\"line\":1,\"ch\":23}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":11}],[{\"line\":7,\"ch\":8},{\"line\":7,\"ch\":11}],[{\"line\":12,\"ch\":8},{\"line\":12,\"ch\":11}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":21}],[{\"line\":7,\"ch\":13},{\"line\":7,\"ch\":21}],[{\"line\":12,\"ch\":13},{\"line\":12,\"ch\":21}],[{\"line\":2,\"ch\":22},{\"line\":2,\"ch\":30}],[{\"line\":6,\"ch\":21},{\"line\":6,\"ch\":29}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":2}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":2}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":2}],[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":20}],[{\"line\":5,\"ch\":8},{\"line\":5,\"ch\":20}],[{\"line\":10,\"ch\":8},{\"line\":10,\"ch\":20}],[{\"line\":15,\"ch\":8},{\"line\":15,\"ch\":20}],[{\"line\":0,\"ch\":28},{\"line\":0,\"ch\":36}],[{\"line\":5,\"ch\":28},{\"line\":5,\"ch\":36}],[{\"line\":10,\"ch\":28},{\"line\":10,\"ch\":36}],[{\"line\":15,\"ch\":28},{\"line\":15,\"ch\":36}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":2}],[{\"line\":7,\"ch\":0},{\"line\":7,\"ch\":2}],[{\"line\":1,\"ch\":20},{\"line\":1,\"ch\":23}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":11}],[{\"line\":7,\"ch\":8},{\"line\":7,\"ch\":11}],[{\"line\":12,\"ch\":8},{\"line\":12,\"ch\":11}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":21}],[{\"line\":7,\"ch\":13},{\"line\":7,\"ch\":21}],[{\"line\":12,\"ch\":13},{\"line\":12,\"ch\":21}],[{\"line\":8,\"ch\":0},{\"line\":8,\"ch\":2}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":2}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":2}],[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":20}],[{\"line\":5,\"ch\":8},{\"line\":5,\"ch\":20}],[{\"line\":10,\"ch\":8},{\"line\":10,\"ch\":20}],[{\"line\":15,\"ch\":8},{\"line\":15,\"ch\":20}],[{\"line\":0,\"ch\":28},{\"line\":0,\"ch\":36}],[{\"line\":5,\"ch\":28},{\"line\":5,\"ch\":36}],[{\"line\":10,\"ch\":28},{\"line\":10,\"ch\":36}],[{\"line\":15,\"ch\":28},{\"line\":15,\"ch\":36}],[{\"line\":2,\"ch\":17},{\"line\":2,\"ch\":19}],[{\"line\":7,\"ch\":17},{\"line\":7,\"ch\":19}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":2}],[{\"line\":12,\"ch\":17},{\"line\":12,\"ch\":19}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":2}],[{\"line\":1,\"ch\":20},{\"line\":1,\"ch\":23}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":11}],[{\"line\":7,\"ch\":8},{\"line\":7,\"ch\":11}],[{\"line\":12,\"ch\":8},{\"line\":12,\"ch\":11}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":21}],[{\"line\":7,\"ch\":13},{\"line\":7,\"ch\":21}],[{\"line\":12,\"ch\":13},{\"line\":12,\"ch\":21}],[{\"line\":13,\"ch\":0},{\"line\":13,\"ch\":2}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":2}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":2}],[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":20}],[{\"line\":5,\"ch\":8},{\"line\":5,\"ch\":20}],[{\"line\":10,\"ch\":8},{\"line\":10,\"ch\":20}],[{\"line\":15,\"ch\":8},{\"line\":15,\"ch\":20}],[{\"line\":0,\"ch\":28},{\"line\":0,\"ch\":36}],[{\"line\":5,\"ch\":28},{\"line\":5,\"ch\":36}],[{\"line\":10,\"ch\":28},{\"line\":10,\"ch\":36}],[{\"line\":15,\"ch\":28},{\"line\":15,\"ch\":36}]]"}
!@%STYLE%@!

Compare the volatile counter, the ordinary counter with a volatile store, and the opaque computation. Each keeps a loop available for measurement by a different means. section 15.3

A value used after a long jump

A parser records nesting depth before an exceptional path returns through `longjmp`. If that depth was modified across the jump and is used afterward for a diagnostic, an ordinary declaration can let optimization assume the wrong value. Declaring it `unsigned volatile depth = 0` and updating the corresponding function parameter keeps the stored value available to the diagnostic. section 17.5

TAKEAWAY: Objects modified across `longjmp` must be volatile when their values are needed afterward. Signal handling presents another restricted use: an interrupt condition can be marked with a `volatile sig_atomic_t` flag, or with the listed atomic alternatives, for handling after the interrupted context resumes. section 17.5 section 17.7

How Modern C covers it

How deep it goes
Explains reloads, stores, and optimization costs through loop assembly and a longjmp repair; does not develop a general concurrency model.
Examples
Compares three timed loops and repairs a parser's nesting-depth diagnostic after longjmp.
What it assumes
Assumes familiarity with C declarations and loops; explains volatile's access effects alongside concrete examples.
What you can do after
Diagnose/debug with it an eliminated measurement loop or an incorrect value used after longjmp.
How it connects
Connects volatile qualification to compiler optimization, performance measurement, setjmp/longjmp, and signal handling.

Also explained in

Compare how 2 books explain volatile qualification →

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.