I understand that volatile
informs the compiler that the value may be changed, but in order to accomplish this functionality, does the compiler need to introduce a memory fence to make it work?
From my understanding, the sequence of operations on volatile objects cannot be reordered and must be preserved. This seems to imply some memory fences are necessary and that there isn't really a way around this. Am I correct in saying this?
There is an interesting discussion at this related question
Jonathan Wakely writes:
... Accesses to distinct volatile variables cannot be reordered by the compiler as long as they occur in separate full expressions ... right that volatile is useless for thread-safety, but not for the reasons he gives. It's not because the compiler might reorder accesses to volatile objects, but because the CPU might reorder them. Atomic operations and memory barriers prevent the compiler and the CPU from reordering
To which David Schwartz replies in the comments:
... There's no difference, from the point of view of the C++ standard, between the compiler doing something and the compiler emitting instructions that cause the hardware to do something. If the CPU may reorder accesses to volatiles, then the standard doesn't require that their order be preserved. ...
... The C++ standard doesn't make any distinction about what does the reordering. And you can't argue that the CPU can reorder them with no observable effect so that's okay -- the C++ standard defines their order as observable. A compiler is compliant with the C++ standard on a platform if it generates code that makes the platform do what the standard requires. If the standard requires accesses to volatiles not be reordered, then a platform the reorders them isn't compliant. ...
My point is that if the C++ standard prohibits the compiler from reordering accesses to distinct volatiles, on the theory that the order of such accesses is part of the program's observable behavior, then it also requires the compiler to emit code that prohibits the CPU from doing so. The standard does not differentiate between what the compiler does and what the compiler's generate code makes the CPU do.
Which does yield two questions: Is either of them "right"? What do actual implementations really do?
C's volatile keyword is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time--without any action being taken by the code the compiler finds nearby.
volatile in most programming languages does not imply a real CPU read memory barrier but an order to the compiler not to optimize the reads via caching in a register. This means that the reading process/thread will get the value "eventually".
The volatile qualifier is applied to a variable when we declare it. It is used to tell the compiler, that the value may change at any time.
Volatile is used in C programming when we need to go and read the value stored by the pointer at the address pointed by the pointer. If you need to change anything in your code that is out of compiler reach you can use this volatile keyword before the variable for which you want to change the value.
Rather than explaining what volatile
does, allow me to explain when you should use volatile
.
volatile
variable is pretty much the only thing the standard allows you to do from within a signal handler. Since C++11 you can use std::atomic
for that purpose, but only if the atomic is lock-free.setjmp
according to Intel.For example:
volatile int *foo = some_memory_mapped_device; while (*foo) ; // wait until *foo turns false
Without the volatile
specifier, the compiler is allowed to completely optimize the loop away. The volatile
specifier tells the compiler that it may not assume that 2 subsequent reads return the same value.
Note that volatile
has nothing to do with threads. The above example does not work if there was a different thread writing to *foo
because there is no acquire operation involved.
In all other cases, usage of volatile
should be considered non-portable and not pass code review anymore except when dealing with pre-C++11 compilers and compiler extensions (such as msvc's /volatile:ms
switch, which is enabled by default under X86/I64).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With