volatile
stops the compiler optimizing it, forcing the compiler
- To always read the memory, not a cached value from a register
- To not move things before, or after the volatile read/write
On a complex CPU (e.g. x86), it is possible for the CPU to re-order operations before, or after a volatile access.
It is typically for memory-mapped-io, where regions of memory, are actually devices, and can change (even on a single core CPU), without visible cause.
The mechanism for C++11
is to use std::atomic to change a value which may occur on different threads of execution.
With a single core, the code will safely modify the value, and store it. If you use volatile, then it will be written to the memory point, before the interrupts are enabled.
If you don't use volatile, then the code may still have the new value in a register before it is used in the interrupt.
int * volatile foo;
Describes that foo can change, but the values it points to, are stable.
int volatile * volatile foo
Describes foo can change, and the things it points to can also change. I think you want int * volatile foo;
Update
For those who doubt that volatile
is a compiler barrier.
From the standard n4296
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects, which are changes in the
state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes
both value computations (including determining the identity of an object for glvalue evaluation and fetching
a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call
to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered
complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile
access may not have completed yet.
and
From cppreference cv object
volatile object - an object whose type is volatile-qualified, or a subobject of a volatile object, or a mutable subobject of a const-volatile object. Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order). Any attempt to refer to a volatile object through a non-volatile glvalue (e.g. through a reference or pointer to non-volatile type) results in undefined behavior.
These seem to concur, that there is a compiler barrier, but some of the side effects of interacting with the volatile object may not have completed. For the single core processor, it appears to be a suitable mechanism if C++11 atomics are not available.
From : C++ standard : n4296
We have :-
Every value computation and side effect associated with a full-expression is sequenced before every value
computation and side effect associated with the next full-expression to be evaluated.
From this I understand, there is a happens-before
relationship for any operation with a side-effect.
Access to volatile objects are evaluated strictly according to the rules of the abstract machine
From this I understand, that there are rules (which maybe opaque).
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects, which are changes in the
state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes
both value computations (including determining the identity of an object for glvalue evaluation and fetching
a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call
to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered
complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile
access may not have completed yet.
From this I understand that access to volatile (and a few other things), create a side effect, which stops the compiler from re-ordering statements near a volatile access.