Volatile Type Qualifier

Today, I learned about the volatile keyword. This keyword is particularly useful when working with memory-mapped hardware devices. It indicates to the compiler that the value of a variable may change at any time, often due to factors external to the programā€˜s normal flow of control, such as the use of semaphores, mutexes, or threads. By using the volatile keyword, you ensure that accesses to such variables are not optimized away by the compiler, as it recognizes that the variableā€˜s value could be altered asynchronously. This is crucial, especially when dealing with device registers or shared variables in concurrent programming scenarios.

Some examples where the volatile keyword should be used:

  • To declare a global variable accessible (by current use or scope) by any interrupt service routine.

  • To declare a global variable accessible (by current use or scope) by two or more threads.

  • To declare a pointer to a memory-mapped I/O peripheral register set

  • To declare a delay loop counter.

Here is how we could declare a volatile semaphore in C based on my previous article:

volatile sem_t mutex;}

An interesting anecdote from the book ā€œEmbedded C Coding Standardā€œ where I first learned about volatile:

Anecdotal evidence suggests that programmers unfamiliar with the volatile keyword believe their compilerā€˜s optimization feature is more broken than helpful and disable optimization. We believe that the vast majority of embedded systems contain bugs waiting to happen due to missing volatile keywords. Such bugs typically manifest themselves as ā€œglitchesā€ or only after changes are made to a ā€œprovenā€ code base.


Here is a good example from the documentation (link below) to see how the volatile keyword can be used and how it affects optimization. Try it yourself and see how the time differs:

#include <stdio.h>
#include <time.h>
  
int main(void)
{
    clock_t t = clock();
    double d = 0.0;
    for (int n = 0; n < 10000; ++n)
    {
      for (int m = 0; m < 10000; ++m)
      {
        d += d * n * m; // reads from and writes to a non-volatile 
      }
    }
    printf("Modified a non-volatile variable 100m times. "
          "Time used: %.2f seconds",
          (double)(clock() - t)/CLOCKS_PER_SEC);
  
    t = clock();
    volatile double vd = 0.0;
    for (int n = 0; n < 10000; ++n)
    {
      for (int m = 0; m < 10000; ++m)
      {
        double prod = vd * n * m; // reads from a volatile
        vd += prod; // reads from and writes to a volatile
      } 
    }
    printf("Modified a volatile variable 100m times. "
            "Time used: %.2f seconds",
            (double)(clock() - t)/CLOCKS_PER_SEC);
}

Here is the documentation for the volatile type qualifier.

Last updated