volatile

The volatile qualifier in C/C++ is widely misunderstood. Because it is described so vaguely in language standards, many people interpret it as a do-what-I-mean qualifier.

What the standard says is that accesses to volatile objects must be evaluated strictly according to the abstract machine defined by the language standard; this means that if the C/C++ code reads the volatile object twice, then the machine code must do so as wel. The standard says that volatile objects must be stable at sequence points; this means, approximately, that in between statements all reads and writes of volatile objects must have been completed–the value may not be cached in a register. The standard makes clear that it is possible that the values of volatile objects may change in some unknown way between accesses.

One relatively unimportant misunderstanding is due to the fact that the standard only talks about accesses to volatile objects. It does not talk about accesses via volatile qualified pointers. Some programmers believe that using a pointer-to-volatile should be handled as though it pointed to a volatile object. That is not guaranteed by the standard and is therefore not portable. However, this is relatively unimportant because gcc does in fact treat a pointer-to-volatile as though it pointed to a volatile object.

A way to think about volatile is to observe that it was invented to support memory mapped hardware. Some hardware is controlled by accesses to specific memory addresses. For example, a serial controller often handles input by setting a bit in one memory location and making the new byte available in another memory location. The kernel code must observe that the bit is set, read the byte, and set another bit to tell the serial controller that the byte has been read (I’m skipping the interrupt which is also involved). These accesses should use volatile to make sure that they happen in the exact order written in the program.

The standard also explicitly describes two other uses of volatile. One is for setjmp and is relatively uninteresting. The other is that a variable of type volatile sig_atomic_t may be set in a signal handler and read by code outside the signal handler. In fact, just about all that a portable signal handler may do is set such a variable.

For dealing with memory mapped hardware, volatile is exactly what you want. For most other types of code, including multi-threaded code, volatile does not help.

Using volatile does not mean that the variable is accessed atomically; no locks are used. Using volatile does not mean that other cores in a multi-core system will see the memory accesses; no cache flushes are used. While volatile writes are guaranteed to occur in the program order for the core which is executing them, there is no guarantee that any other core will see the writes in the same order. Using volatile does not imply any sort of memory barrier; the processor can and will rearrange volatile memory accesses (this will not happen for address ranges used for memory mapped hardware, but it will for ordinary memory).

Conversely, if you use the locking primitives which are part of any threading library, then you do not need to use volatile. The locking primitives will include the required memory barriers or cache flushes. They will include whatever special directives are needed to tell the compiler that memory must be stable.

There is one case where volatile may be used for multi-threaded programming with some reliability. You may use a single volatile sig_atomic_t variable to communicate between threads, much as you may use such a variable to communicate between a signal handler and the main program. You should not use more than one such variable to communicate between any pair of threads, as there is no guarantee that the different threads will see the accesses in the same order.

In summary, if you are using volatile for anything other than manipulating memory mapped hardware, or for very limited communication between threads, it is very likely that you are making a mistake. Think carefully about what volatile means and about what it does not mean.


Posted

in

by

Tags:

Comments

9 responses to “volatile”

  1. gabe Avatar
    gabe

    Hi Ian. Thanks for this post. I’ve recently started working on some multi threaded C++ apps. Currently using Trolltech/Qt’s libraries but I’ll probably also probably use Boost. Anyway, this discussion of volatile was very helpful, especially the use of volatile sig_atomic_t as a signal which will not require a semaphore or mutex.

    At any rate, I’m curious about your view of this use of volatile, from Andrei Alexandrescu.
    http://www.ddj.com/cpp/184403766

    It seems incorrect or at least unintended according to the standard. However, I have used it on a small project and had good results.

    Thanks!

  2. Ian Lance Taylor Avatar

    Thanks for the pointer to the article. I admit that I had not considered that way of using volatile in C++ to prevent inadvertent unlocked accesses. That seems like a good idea. I was thinking about applying volatile to primitive types, and indeed the article agrees with me: don’t do it for multithreaded code.

  3. Raphael Avatar

    One other – sensible – use of ‘volatile’ is for variables that are modified by interrupt handlers. (Usually the OS handles interrupts for you. But on microcontrollers you have to handle them yourself. )
    For example, you might have a main loop sending data from a buffer over a serial line. When new data is available from the analog-digital converter (ADC) an interrupt occurs. You handle this interrupt in a routine that copies the data from the ADC register into your buffer, then restarts the ADC. If this buffer were not declared ‘volatile’ the main loop might not recognize newly added data.

  4. Ian Lance Taylor Avatar

    Yes, that makes sense. Thanks for the example.

  5. davem Avatar

    But in such a situation you would be doing one of two things to
    coordinate execution in the interrupt handler from execution
    outside of that interrupt handler.

    1) Disable CPU interrupts in non-interrupt handler code accessing
    the buffer or other shared state

    2) Using a spinlock or some other kind of mutex to protect the
    buffer

    Such primitives, in order to be useful, would have to provide the
    same things that volatile give you, yet more directly so. Therefore
    you don’t need to use volatile.

  6. […] Интересная заметка Ian Lance Taylor об использовании модификатора volatile в языке програмирования С. Правда в ней опускается метод использования volatile для много-потоковых программ, поэтому в догонку статья Andrei Alexandrescu - volatile – Multithreaded Programmer’s Best Friend. […]

  7. […] And for those who suggest volatile, we can’t use it under GCC because GCC interprets the volatile keyword differently than Microsoft. See the same thread for a discussion; or see Ian Lance Taylor’s blog on Volatile. […]

  8. […] And for those who suggest volatile, we can’t use it under GCC because GCC interprets the volatile keyword differently than Microsoft. See the same thread for a discussion; or see Ian Lance Taylor’s blog on Volatile. […]

Leave a Reply