There is It is a misconception that the volatile
qualifier will provide provides the following desired properties necessary for a multithreaded program:
- Atomicity: indivisible Indivisible memory operations.
- Visibility: the The effects of a write action by a thread are visible by visible to other threads.
- Ordering: Sequences of memory operations by a thread are guaranteed to be seen in the same order by other threads.
Unfortunately, the volatile
qualifier does not provide guarantees for any of these properties, neither by definition nor by the way it is implemented in various platforms. For more information on how volatile
is implemented, consult recommendation DCL17-C. Beware of miscompiled volatile-qualified variables.
The C99 standardC Standard, section 5.1.2.3, paragraph 2, says
Accessing a volatile object, modifying an object, modifying a file, 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 may produce in general includes both value computations and initiation of side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete, and no side effects of subsequent evaluations shall have taken placeValue computation for an lvalue expression includes determining the identity of the designated object.
In a nutshell, all this keyword does is to inform the compiler that this variable may change in ways that cannot be determined; therefore, the compiler should not perform optimizations in these memory areas marked as volatile. In other words, the compiler should not store the value in a register and use the register , instead of doing expensive memory accesses. This concept is closely related to multithreading because , if a shared variable is cached, a thread may change it, and the other threads may read stale data.
This property of the volatile
keyword is sometimes confused as providing atomicity sometimes misunderstood to provide atomicity of a variable that is shared between threads in a multithreaded program. A variable declared as volatile
is not cached in a register, leading to this confusion that it can be used safely as a synchronization primitive. When a variable is declared volatile
, the compiler does not re-order reorder the sequence of reads and writes to that memory location. However, the compiler might re-order reorder these reads and writes with those to other memory locations. This might result in non-atomic nonatomic operations on the synchronization variable resulting in , causing errors.
Noncompliant Code Example
...
In this example, the value of flag
is used to determine if whether the critical section can be executed or not. Because the flag
variable is not declared volatile
, it may be cached in registers. Before the value in the register is written to memory, another thread might be scheduled to run, resulting in that thread reading stale data.
...
Declaring flag
volatile solves the problem of reading stale data , but still does not provide atomicity guarantees needed for synchronization primitives to work correctly. The volatile
keyword does not promise to provide the guarantees needed for synchronization primitives.
...
CERT C++ Secure Coding Standard: CON01-CPP. Do not use volatile as a synchronization primitive
ISO/IEC 9899:1999 Section 2011 Section 5.1.2.3 "Program Executionexecution"
...
Sources
[Open Group 2004] Section 4.11 "Memory Synchronizationsynchronization"
...