Volatile: C Keyword Myths Dispelled
Subscribe To Our Feed | Follow Us On Twitter | Get Updates on Email
Last time we explained the real meaning of const keyword, this time it’s going to be Volatile, the other sibling of this most misunderstood duo in C history. Let’s separate out the myths and the facts first and then we will discuss the how’s and why’s of it.
FACTS:
- A volatile qualifier is important to be used for auto-storage variables within setjmp and longjmp.
- A volatile qualifier must be used when reading the contents of a memory location whose value can change unknown to the current program.
- A volatile qualifier must be used for shared data modified in signal handlers or interrupt service routines.
MYTHS:
- All shared data in multi-threaded programs must be declared volatile.
Now, we’ll see how we made the above classfication.
The only information/directive that volatile keyword means to the compiler is to not apply any implementation specific optimization to the object to which this qualifier has been attached. e.g. One of the most general optimizations that a compiler can apply to a variable is that if it cannot find a variable’s value being changed within a certain scope, it will cache the value of that variable, probably in a register, and then refer to that cached value whenever that variable is accessed instead of fetching it from the main memory.
#include "stdio.h" #include "setjmp.h" static jmp_buf buf; int main( ) { int a; a = 0; if (setjmp(buf)!=0) { printf("%d n", a) ; exit(0); } a = 1; longjmp(buf , 1); return 0; }
If you were to compile the above program without any optimization, you would see the output as 1, which is correct. But if you were to compile it with optimization turned on (e.g. by using -O2 option with gcc), the output would be 0. This is because the compiler saw that “a” was not used after setting it to one and hence optimized out that statement. Then, if you try it out by declaring “a” as volatile, you’d see the output as 1 even with optimization turned on, because the volatile qualifier would tell the compiler not to optimize any accesses related to a. For the 3 situations stated under facts, compiler cannot determine that a particular variable is needed or not because the accesses happen unknown to the compiler. Hence, we need to declare such variables as volatile.
Now, let’s turn towards the myths. Many people say that since in multi-threaded environment, a shared variable’s value can be changed by a thread before the other thread reads it, such variable should be declared as a volatile. But this is completely false. This is because a volatile qualifier just promises us that the accesses won’t be optimized and would be read from main memory each time but a multi-threaded program needs much more, namely:
- Atomicity – The read/write access to the shared variables should be atomic in nature but access to volatile can be non-atomic and are completly implementation defined.
- Preserve order of access – A volatile variable does not mandate this, especially when mixed with non-volatile variables.
So, if you are writing a multi-threaded program, what you need is something that ensures these 2 particular things by providing you proper memory barriers. e.g. You could use mutexes, pthreads, or something similar that provides you the above mentioned semantics.
If you rely completely on volatile to get a proper working multi-threaded program for you, let me assure you that all you will achieve is introduce serious slowness to the program (by taking optimization out of the equation). e.g. nothing stops another thread from coming in changing your variable’s value while you are half way through reading it in the current thread. And if you still require to use a volatile even after using a proper memory fencing “lock”, then there is something wrong with it and you need to take a hard look at your locking mechanism. But do not confuse it with the volatile provided in other languages like java where it does provide you with proper memory barriers.
I hope the article helped in clearing out your doubts about this mysterious little keyword from the C stable. Let us know if you have more questions about this.
© Safer Code | Volatile: C Keyword Myths Dispelled
|
Liked this post? Get FREE Updates Subscribe to RSS feed |
Related posts
Tags: -O2, atomicity, C, const, CPP, gcc, keyword, memory barriers, memory fences, non-atomic access, Optimization, order of access, volatile, volatile keyword






This is true for vanilla C/C++ but the Microsoft compiler (starting with 2005 I believe) adds additional barriers around volatile accesses, making volatile in MS C/C++ similar to volatile in Java (and more guarantees are provided over volatile in standard C/C++).
Not pretending to say you should use volatile in a multithreaded environment… but I disagree strongly with the speed argument. Most synchronization primitives and sync barriers are orders of magnitude slower than a simple access to central memory.
yet another awesome post, just saying thanks for the good info they don’t teach in school.
@Jason: yeah but that’s implementation dependent, what if you rebuild with gcc, intel, borland, or some other compiler. Isn’t it better to make sure that your code can be recompiled with other things, instead of relying on that it will always be build with the same compiler.
Great post.
@ jackjeff : I think you misunderstood his point. His point is that you should not use volatile for correctness in multi threaded environment, and – if you do use – it will not only make your program prone to sporadic errors and also slower. I am sure he agrees that pthread and other lock semantics will likely be slower that volatile access.
@Caleb: I think jackjeff is talking about the actual builtins used to implement synchronization primitives (e.g. compare and swap, etc) which are usually 100-300 times slower than their non-atomic counterparts.
Jackjeff, yes sync primitives and memory barriers might be slower but the point is that they are anyways needed even if you do use volatile. The point was that if you use these semantics correctly, you don’t need volatile at all, using volatile in these cases will not make your program any more safe but just slow it down further.
@harringf You totally misunderstood my point…
I totally agree that volatile as multithreaded synchronization is just plainly wrong. It’s wrong because automicity is not garanteed and it cannot be used as a synchronization primitive safely. But it’s not wrong because of speed.
Volatile, despite being totally utterly wrong, is probably faster than most of the synchronization primitives offered by modern operating system. The latter are likely to trigger system calls and/or require memory synchronization instructions. Proper synchronization primitives are expensive.
Declaring objects to be volatile does cause the order of access to be fixed (there may be cases where multiple orders of access are permitted by the language, but that is another issue):
“Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3.”
http://c0x.coding-guidelines.com/6.7.3.html Sentence 1482
It is true that: All shared data in multi-threaded programs must be declared volatile. Atomicity is an unrelated issue and covered by the type sig_atomic_t
It seems that developers are not the only people that sometimes have trouble with volatile, compilers often get it wrong as well
http://shape-of-code.coding-guidelines.com/2009/03/volatile-handling-sometimes-overly-volatile/
@Derek: Not necessarily when mixed with non-volatiles. Consider the following example:
int main()
{
int i = 2;
volatile int j = 1;
while ( i )
{
printf("%d\n", i);
--i;
j = 2;
}
return 0;
}
If you were to compile it with optimization on, most compilers will move the “j=2″ before the loop.
@Shantanu: A compiler that performs the optimization you give does not conform to the requirements contained in the C Standard.
Two sentences does from the previously given quote:
http://c0x.coding-guidelines.com/6.7.3.html Sentence 1484
“Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine …”
Derek, this is interesting. BTW I have used gcc 3.x and gcc 4.2.4 and both give similar results (at O1 as well as O2). Both these versions output different code but do move around the statement that stores 2 to j. I guess that is all the more reason to not use volatiles with this particular “feature” in mind as many compilers haven’t got it right
Author completely misunderstood ‘volatile’ and multithreading. Multithread program completely do not need atomicly access! what for? What if platform do not support atomic access? Atomic access is also not specified in Ansi C 89 (i do not know what about C99). Mutual exclusion is enough to secure multithreading access.
volatile is related to optimization which impacts the hardwar related access of variables. WHile multi threading requires exclusive operations (meaning no other thread is alowed to access the critical section). These both may be required at times, but totally un related.
Suppose, a program is multithreaded but it uses a variable which stores some hardware address, and 2 threads accesses that variable. Then, too variable can be declared as volatile and can be protected with mutex.
There is another keyword I can’t understand well: restrict for pointers.