The C Standard, section 5.1.2.3, paragraph 2 [ISO/IEC 9899:2011], 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 in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object.
In a nutshell, all this keyword does is inform the compiler that this variable may change in ways that cannot be determined; thereforeconsequently, the compiler should not perform optimizations in memory areas marked as volatile. For example, the compiler should not store the value in a register and use the register instead of accessing expensive memory directly. This concept is closely related to multithreading because if a shared variable is cached, a thread may change it, and the other threads may consequently read stale data.
This property of the volatile
keyword is 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 the misunderstanding that it can be used safely as a synchronization primitive. When a variable is declared volatile
, the compiler does not reorder the sequence of reads and writes to that memory location. However, the compiler might reorder these reads and writes with those to other memory locations. This Reordering might result in nonatomic operations on the synchronization variable, causing errors. Do not assume that the volatile
qualifier provides any of the following desired properties necessary for a multithreaded program:
...
This noncompliant code example uses flag
as a synchronization primitive but qualifies flag
as a volatile
type.
Code Block | ||||
---|---|---|---|---|
| ||||
volatile bool flag = false; void test() { while (!flag){ sleep(1000); } } void wakeup(){ flag = true; } void debit(unsigned int amount) { test(); account_balance -= amount; } |
Declaring flag
as volatile
solves the problem of values being cached, which causes stale data to be read. However, volatile flag
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.
...
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON02-C | Medium | Probable | Medium | P8 | L2 |
Related Guidelines
...
Bibliography
...
] | Section 5.1.2.3, "Program |
...
Execution" |
...
[Open Group 2004] | Section 4.11, "Memory |
...
Synchronization" |
...