Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Referenced SIG31-C in discussion and clarified examples.

...

Wiki Markup
The {{volatile}} keyword eliminates this confusion by imposing restrictions on access and caching. According to the C99 Rationale \[[ISO/IEC 03|AA. C References#ISO/IEC 03]\]:

No cacheing caching through this lvalue: each operation in the abstract semantics must be performed (that is, no cacheing assumptions may be made, since the location is not guaranteed to contain any previous value). In the absence of this qualifier, the contents of the designated location may be assumed to be unchanged except for possible aliasing.

Please keep in mind that while adding volatile will ensure that a compiler does not perform unintended reordering or optimization, it in no way guarantees synchronization between multiple threads nor does it otherwise , ward against simultaneous memory accesses, and unless used to declare objects of type sig_atomic_t guarantee atomicity of accesses to the object. For restrictions specific to signal handlers see SIG31-C. Do not access or modify shared objects in signal handlers.

Noncompliant Code Example (Missing volatile)

This noncompliant code example relies on the reception of a SIGINT signal to toggle a flag to terminate a loop. However, since interrupted is not declared volatile, the read from it in main() may be optimized away by the compiler despite the assignment to the variable in the signal handler and the loop may never terminate. When compiled on GCC with the -O optimization flag, for example, the program fails to terminate even upon receiving a SIGINT.

Code Block
bgColor#ffcccc

#include <signal.h>

sig_atomic_t i;interrupted;   /* bug: not declared volatile */

void sigint_handler(int signum) {
  iinterrupted = 0;1;   /* assignment may not be visible in main() */
}

int main(void) {
  i = 1;
  signal(SIGINT, sigint_handler);
  while (i!interrupted) {   /* loop may never terminate */
   /* do something */
  }
  return 0;
}

However, if the value of i is cached, the while loop may never terminate. When compiled on GCC with the -O optimization flag, for example, the program fails to terminate even upon receiving a SIGINT.

Noncompliant Code Example (Cast to volatile)

This noncompliant code example prevents attempts to prevent the compiler from optimizing away the loop condition, by type casting the variable to volatile within the while loop. However, since interrupted is not declared volatile, the assignment to it in the signal handler need not be flushed from a register into memory and thus not be visible in main(), despite the cast.

Code Block
bgColor#ffcccc
#include <signal.h>

sig_atomic_t i;interrupted;   /* bug: not declared volatile */

void sigint_handler(int signum) {
  iinterrupted = 0;1;   /* assignment may not be visible in main() */
}

int main(void) {
  i = 1;
  signal(SIGINT, sigint_handler);
  while (!*(volatile sig_atomic_t *)&iinterrupted) {
   /* do something */
  }
  return 0;
}

...

By adding the volatile qualifier to the variable declaration, i interrupted is guaranteed to be accessed from its original address for every iteration of the while loop as well as from within the signal handler.

Code Block
bgColor#ccccff
#include <signal.h>

volatile sig_atomic_t iinterrupted;

void sigint_handler(int signum) {
  iinterrupted = 01;
}

int main(void) {
  i = 1;
  signal(SIGINT, sigint_handler);
  while (i!interrupted) {
   /* do something */
  }
  return 0;
}

...