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 about semaphore:
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’s a great example from the documentation (link below) that demonstrates how the volatile keyword can be used and its impact on optimization. Experiment with it yourself and observe the time difference.
Here is the documentation for the volatile type qualifier.