Volatile (computer programming): Difference between revisions

Content deleted Content added
What can happen if volatile is not used?: Bring out difference between '1`'
Link suggestions feature: 2 links added.
 
(312 intermediate revisions by more than 100 users not shown)
Line 1:
{{Short description|Keyword used in some programming languages to tag variables}}
In [[computer programming]], a [[variable]] or object declared with the '''volatile''' [[Keyword_(Computer)|keyword]] may be modified externally from the declaring object. For example, a variable that might be concurrently modified by multiple [[Thread_(computer_science)|threads]] (without [[Lock_(computer_science)|locks]] or a similar form of [[mutual exclusion]]) should be declared volatile. Variables declared to be volatile will not be optimized by the [[compiler]] because their value can change at any time.
{{Lowercase title}}
In [[computer programming]], a [[Variable (computer science)|variable]] is said to be '''''volatile''''' if its [[Value (computer science)|value]] can be read or modified asynchronously by something other than the current [[thread (computing)|thread of execution]].
The value of a <code>volatile</code> variable may spontaneously change for reasons such as:
sharing values with other threads;
sharing values with asynchronous [[signal handler]]s;
accessing hardware devices via [[memory-mapped I/O]] (where you can send and receive messages from [[peripheral device]]s by reading from and writing to memory).
Support for these use cases varies considerably among the programming languages that have the <code>volatile</code> keyword.
Volatility can have implications regarding function [[calling convention]]s and how variables are stored, accessed and cached.
 
==In C and C++==
===What can happen if volatile is not used?===
 
In C and C++, <code>volatile</code> is a [[type qualifier]], like <code>[[const (computer programming)|const]]</code>, and is a part of a [[data type|type]] (e.g. the type of a variable or field).
The following piece of [[C_programming_language|C]] [[source code]] demonstrates the use of the volatile keyword.
 
The behavior of the <code>volatile</code> keyword in C and C++ is sometimes given in terms of suppressing optimizations of an [[optimizing compiler]]: 1- don't remove existing <code>volatile</code> reads and writes, 2- don't add new <code>volatile</code> reads and writes, and 3- don't reorder <code>volatile</code> reads and writes. However, this definition is only an approximation for the benefit of new learners, and this approximate definition should not be relied upon to write real production code.
<pre><nowiki>
void foo(void)
{
int *addr;
addr = 100;
*addr = 0;
 
In C, and consequently C++, the <code>volatile</code> keyword was intended to:<ref name="auto">{{cite web |title=Publication on C++ standards committee|url= http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html}}</ref>
while (*addr != 255)
*Allow access to [[memory-mapped I/O]] devices.
;
*Allow preserving values across a <code>[[setjmp|longjmp]]</code>.
}</nowiki></pre>
*Allow sharing values between signal handlers and the rest of the program in <code>volatile</code> <code>sig_atomic_t</code> objects.
 
The C and C++ standards allow writing portable code that shares values across a <code>[[setjmp|longjmp]]</code> in <code>volatile</code> objects, and the standards allow writing portable code that shares values between signal handlers and the rest of the code in <code>volatile</code> <code>sig_atomic_t</code> objects. Any other use of <code>volatile</code> keyword in C and C++ is inherently non-portable or incorrect. In particular, writing code with the <code>volatile</code> keyword for [[memory-mapped I/O]] devices is inherently non-portable and always requires deep knowledge of the specific target C/C++ implementation and platform.
In this example, the code sets the value stored at [[memory address|___location]] 100 in the computer system to 0. It then starts to poll the address until it changes to 255.
An [[Compiler_optimization|optimizing]] compiler will assume that no other code will change the value stored in ___location 100 and so it will remain equal to 0. The compiler will then replace the [[program loop|while loop]] with something similar to this: -
 
=== Multi-threading ===
<pre><nowiki>
It is a common misconception that the <code>volatile</code> keyword is useful in portable [[thread (computing)|multi-threading]] code in C and C++. The <code>volatile</code> keyword in C and C++ has ''never'' functioned as a useful, portable tool for ''any'' multi-threading scenario.<ref>{{cite web |date=21 September 2021 |title=Volatile Keyword In Visual C++ |url=http://msdn2.microsoft.com/en-us/library/12a04hfd.aspx |work=Microsoft MSDN}}</ref><ref>{{cite web |title=Linux Kernel Documentation – Why the "volatile" type class should not be used |url=https://www.kernel.org/doc/html/latest/process/volatile-considered-harmful.html |work=kernel.org}}</ref><ref>{{cite web |author1=Scott Meyers |author2=Andrei Alexandrescu |year=2004 |title=C++ and the Perils of Double-Checked Locking |url=http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf |work=DDJ}}</ref><ref>{{cite web |author1=Jeremy Andrews |year=2007 |title=Linux: Volatile Superstition |url=http://kerneltrap.org/Linux/Volatile_Superstition |archive-url=https://web.archive.org/web/20100620121940/http://kerneltrap.org/Linux/Volatile_Superstition |archive-date=2010-06-20 |access-date=Jan 9, 2011 |publisher=kerneltrap.org}}</ref> Unlike the [[Java (programming language)|Java]] and [[C Sharp (programming language)|C#]] programming languages, operations on <code>volatile</code> variables in C and C++ are not [[atomic operation|atomic]], and operations on <code>volatile</code> variables do not have sufficient [[memory ordering]] guarantees (i.e. [[memory barrier|memory barriers]]). Most C and C++ compilers, linkers, and runtimes simply do not provide the necessary memory ordering guarantees to make the <code>volatile</code> keyword useful for ''any'' multi-threading scenario. Before the C11 and C++11 standards, programmers were forced to rely on guarantees from the individual implementations and platforms (e.g. POSIX and WIN32) to write [[thread (computing)|multi-threading]] code. With the modern C11 and C++11 standards, programmers can write portable [[thread (computing)|multi-threading]] code using new portable constructs such as the <code>std::atomic<T></code> templates.<ref>{{cite web |title=volatile (C++) |url=https://msdn.microsoft.com/en-us/library/12a04hfd.aspx |work=Microsoft MSDN|date=21 September 2021 }}</ref>
void foo(void)
{
int *addr;
addr = 100;
*addr = 0;
 
===Example of memory-mapped I/O in C===
while (1) /* 1 here means TRUE */
In this example, the code sets the value stored in <code>foo</code> to <code>0</code>. It then starts to [[polling (computer science)|poll]] that value repeatedly until it changes to <code>255</code>:
;
}</nowiki></pre>
 
<syntaxhighlight lang="c">
and the program will loop forever.
static int foo;
 
void bar(void) {
However, the address might represent a ___location that can be changed by other elements of the computer system. For example, it could be a [[hardware register]] of a device connected to the [[CPU]]. The value stored there could change at any time. The above code would never detect the change.
foo = 0;
 
while (foo != 255)
To prevent the compiler from modifying code in this way, the volatile keyword is used in the following manner: -
;
}
</syntaxhighlight>
 
An [[optimizing compiler]] will notice that no other code can possibly change the value stored in <code>foo</code>, and will assume that it will remain equal to <code>0</code> at all times. The compiler will therefore replace the function body with an [[infinite loop]] similar to this:
<pre><nowiki>
void foo(void)
{
volatile int *addr;
addr = 100;
*addr = 0;
 
<syntaxhighlight lang="c">
while (*addr != 255)
void bar_optimized(void) {
foo = 0;
 
while (true)
;
}
</syntaxhighlight>
 
However, the programmer may make <code>foo</code> refer to another element of the computer system such as a [[hardware register]] of a device connected to the [[CPU]] which may change the value of <code>foo</code> while this code is running. (This example does not include the details on how to make <code>foo</code> refer to a hardware register of a device connected to the CPU.) Without the <code>volatile</code> keyword, an [[optimizing compiler]] will likely convert the code from the first sample with the read in the loop to the second sample without the read in the loop as part of the common [[Loop-invariant code motion|loop-invariant code-motion optimization]], and thus the code will likely never notice the change that it is waiting for.
 
To prevent the compiler from doing this optimization, the <code>volatile</code> keyword can be used:
 
<syntaxhighlight lang="c">
static volatile int foo;
 
void bar (void) {
foo = 0;
 
while (foo != 255)
;
}
}</nowiki></pre>
</syntaxhighlight>
 
The <code>volatile</code> keyword prevents the compiler from moving the read out of the loop, and thus the code will notice the expected change to the variable <code>foo</code>.
 
===Optimization comparison in C===
The following C programs, and accompanying assembler language excerpts, demonstrate how the <code>volatile</code> keyword affects the compiler's output. The compiler in this case was [[GNU Compiler Collection|GCC]].
 
While observing the assembly code, it is clearly visible that the code generated with <code>volatile</code> objects is more verbose, making it longer so the nature of <code>volatile</code> objects can be fulfilled. The <code>volatile</code> keyword prevents the compiler from performing optimization on code involving volatile objects, thus ensuring that each volatile variable assignment and read has a corresponding memory access. Without the <code>volatile</code> keyword, the compiler knows a variable does not need to be reread from memory at each use, because there should not be any writes to its memory ___location from any other thread or process.
 
{|class="wikitable collapsible collapsed" width="100%"
! style="text-align: center" colspan=2 | Assembly comparison
|-
!Without <code>volatile</code> keyword||With <code>volatile</code> keyword
|-
|
<syntaxhighlight lang="c">
# include <stdio.h>
 
int main() {
/* These variables will never be created on stack*/
int a = 10, b = 100, c = 0, d = 0;
 
/* "printf" will be called with arguments "%d" and
110 (the compiler computes the sum of a+b),
hence no overhead of performing addition at
run-time */
printf("%d", a + b);
 
/* This code will be removed via optimization, but
the impact of 'c' and 'd' becoming 100 can be
seen while calling "printf" */
a = b;
c = b;
d = b;
 
/* Compiler will generate code where printf is
called with arguments "%d" and 200 */
printf("%d", c + d);
 
return 0;
}
</syntaxhighlight>
|
<syntaxhighlight lang="c">
# include <stdio.h>
 
int main() {
 
volatile int a = 10, b = 100, c = 0, d = 0;
 
printf("%d", a + b);
 
a = b;
c = b;
d = b;
 
printf("%d", c + d);
 
return 0;
}
</syntaxhighlight>
|-
 
! {{Mono|gcc -S -O3 -masm{{=}}intel noVolatileVar.c -o without.s}}
! {{Mono|gcc -S -O3 -masm{{=}}intel VolatileVar.c -o with.s}}
|- valign="top"
|<syntaxhighlight lang="asm">
.file "noVolatileVar.c"
.intel_syntax noprefix
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
sub rsp, 8
.cfi_def_cfa_offset 16
mov esi, 110
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 200
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (GNU) 4.8.2"
.section .note.GNU-stack,"",@progbits
</syntaxhighlight>
|<syntaxhighlight lang="asm">
.file "VolatileVar.c"
.intel_syntax noprefix
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
sub rsp, 24
.cfi_def_cfa_offset 32
mov edi, OFFSET FLAT:.LC0
mov DWORD PTR [rsp], 10
mov DWORD PTR [rsp+4], 100
mov DWORD PTR [rsp+8], 0
mov DWORD PTR [rsp+12], 0
mov esi, DWORD PTR [rsp]
mov eax, DWORD PTR [rsp+4]
add esi, eax
xor eax, eax
call printf
mov eax, DWORD PTR [rsp+4]
mov edi, OFFSET FLAT:.LC0
mov DWORD PTR [rsp], eax
mov eax, DWORD PTR [rsp+4]
mov DWORD PTR [rsp+8], eax
mov eax, DWORD PTR [rsp+4]
mov DWORD PTR [rsp+12], eax
mov esi, DWORD PTR [rsp+8]
mov eax, DWORD PTR [rsp+12]
add esi, eax
xor eax, eax
call printf
xor eax, eax
add rsp, 24
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (GNU) 4.8.2"
.section .note.GNU-stack,"",@progbits
</syntaxhighlight>
|}
 
=== Standards defects ===
While intended by both C and C++, the current C standard fails to express that the <code>volatile</code> semantics refer to the lvalue, not the referenced object. The respective defect report ''DR 476'' (to C11) is still under review with [[C17 (C standard revision)|C17]].<ref>[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2244.htm ''Clarification Request Summary for C11.''] Version 1.13, October 2017.</ref>
 
=== Compiler defects ===
Unlike other language features of C and C++, the <code>volatile</code> keyword is not well supported by most C/C++ implementations - even for portable uses according to the C and C++ standards. Most C/C++ implementations are buggy regarding the behavior of the <code>volatile</code> keyword.<ref>{{Cite journal |last1=Eide |first1=Eric |last2=Regehr |first2=John |date=October 2008 |title=Volatiles Are Miscompiled, and What to Do about It |url=https://users.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf |journal=Proceedings of the Eighth ACM and IEEE International Conference on Embedded Software (EMSOFT), Atlanta, Georgia, USA |via=cs.utah.edu}}</ref><ref>{{Cite web |title=Volatile Bugs, Three Years Later – Embedded in Academia |url=https://blog.regehr.org/archives/503 |access-date=2024-08-28 |website=blog.regehr.org}}</ref> Programmers should take great care whenever using the <code>volatile</code> keyword in C and C++.
 
==In Java==
In all modern versions of the [[Java programming language]], the <code>volatile</code> keyword gives the following guarantees:
 
* <code>volatile</code> reads and writes are [[atomic operation|atomic]]. In particular, reads and writes to <code>long</code> and <code>double</code> fields will not tear. (The [[atomic operation|atomic]] guarantee applies only to the <code>volatile</code> primitive value or the <code>volatile</code> reference value, and ''not'' to any Object value.)
* There is a single global ordering of all <code>volatile</code> reads and writes. In other words, a <code>volatile</code> read will read the current value (and not a past or future value), and all <code>volatile</code> reads will agree on a single global order of <code>volatile</code> writes.
* <code>volatile</code> reads and writes have "acquire" and "release" [[memory barrier]] semantics (known in the Java standard as [[happened-before|happens-before]]).<ref>Section 17.4.4: Synchronization Order
{{cite web |year=2013 |title=The Java® Language Specification, Java SE 7 Edition |url=http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4 |access-date=2013-05-12 |publisher=[[Oracle Corporation]]}}</ref><ref>{{cite web |date=2021-03-08 |title=Java Concurrency: Understanding the 'Volatile' Keyword |url=https://dzone.com/articles/java-concurrency-understanding-the-volatile-keyword |archive-url=https://web.archive.org/web/20210509104459/https://dzone.com/articles/java-concurrency-understanding-the-volatile-keyword |archive-date=2021-05-09 |access-date=2021-05-09 |publisher=dzone.com}}</ref> In other words, <code>volatile</code> provides guarantees about the relative order of <code>volatile</code> and non-<code>volatile</code> reads and writes. In other words, <code>volatile</code> basically provides the same memory visibility guarantees as a Java [[lock (computer science)|synchronized block]] (but without the [[mutual exclusion]] guarantees of a [[lock (computer science)|synchronized block]]).
 
Together, these guarantees make <code>volatile</code> into a useful [[thread (computing)|multi-threading]] construct in [[Java programming language|Java]]. In particular, the typical [[double-checked locking]] algorithm with <code>volatile</code> works correctly in [[Java programming language|Java]].<ref>{{cite web |author1=Neil Coffey |title=Double-checked Locking (DCL) and how to fix it |url=http://www.javamex.com/tutorials/double_checked_locking_fixing.shtml |access-date=2009-09-19 |publisher=Javamex}}</ref>
 
=== Early versions of Java ===
Before Java version 5, the Java standard did not guarantee the relative ordering of <code>volatile</code> and non-<code>volatile</code> reads and writes. In other words, <code>volatile</code> did not have "acquire" and "release" [[memory barrier]] semantics. This greatly limited its use as a [[thread (computing)|multi-threading]] construct. In particular, the typical [[double-checked locking]] algorithm with <code>volatile</code> did ''not'' work correctly.
 
==In C#==
 
In [[C Sharp (programming language)|C#]], <code>volatile</code> ensures that code accessing the field is not subject to some thread-unsafe optimizations that may be performed by the compiler, the CLR, or by hardware. When a field is marked <code>volatile</code>, the compiler is instructed to generate a "memory barrier" or "fence" around it, which prevents instruction reordering or caching tied to the field. When reading a <code>volatile</code> field, the compiler generates an ''acquire-fence'', which prevents other reads and writes to the field from being moved ''before'' the fence. When writing to a <code>volatile</code> field, the compiler generates a ''release-fence''; this fence prevents other reads and writes to the field from being moved ''after'' the fence.<ref name="Albahari">{{cite web |last1=Albahari |first1=Joseph |title=Part 4: Advanced Threading |url=http://www.albahari.com/threading/part4.aspx |website=Threading in C# |publisher=O'Reilly Media |access-date=9 December 2019 |archive-url=https://web.archive.org/web/20191212032535/http://www.albahari.com/threading/part4.aspx#_Nonblocking_Synchronization |archive-date=12 December 2019 |url-status=bot: unknown }}</ref>
 
Only the following types can be marked <code>volatile</code>: all reference types, <code>Single</code>, <code>Boolean</code>, <code>Byte</code>, <code>SByte</code>, <code>Int16</code>, <code>UInt16</code>, <code>Int32</code>, <code>UInt32</code>, <code>Char</code>, and all enumerated types with an underlying type of <code>Byte</code>, <code>SByte</code>, <code>Int16</code>, <code>UInt16</code>, <code>Int32</code>, or <code>UInt32</code>.<ref>{{cite book |last1=Richter |first1=Jeffrey |title=CLR Via C# |url=https://archive.org/details/clrviac00rich_000 |url-access=limited |publisher=Microsoft Press |date=February 11, 2010 |pages=[https://archive.org/details/clrviac00rich_000/page/n200 183] |chapter=Chapter 7: Constants and Fields |isbn=978-0-7356-2704-8}}</ref> (This excludes value [[struct]]s, as well as the primitive types <code>Double</code>, <code>Int64</code>, <code>UInt64</code> and <code>Decimal</code>.)
 
Using the <code>volatile</code> keyword does not support fields that are [[Evaluation strategy#Call by reference|passed by reference]] or [[Closure (computer programming)|captured local variables]]; in these cases, <code>Thread.VolatileRead</code> and <code>Thread.VolatileWrite</code> must be used instead.<ref name="Albahari"/>
 
In effect, these methods disable some optimizations usually performed by the C# compiler, the JIT compiler, or the CPU itself. The guarantees provided by <code>Thread.VolatileRead</code> and <code>Thread.VolatileWrite</code> are a superset of the guarantees provided by the <code>volatile</code> keyword: instead of generating a "half fence" (ie an acquire-fence only prevents instruction reordering and caching that comes before it), <code>VolatileRead</code> and <code>VolatileWrite</code> generate a "full fence" which prevent instruction reordering and caching of that field in both directions.<ref name="Albahari"/> These methods work as follows:<ref>{{cite book |last1=Richter |first1=Jeffrey |title=CLR Via C# |url=https://archive.org/details/clrviac00rich_000 |url-access=limited |publisher=Microsoft Press |date=February 11, 2010 |pages=[https://archive.org/details/clrviac00rich_000/page/n814 797]–803 |chapter=Chapter 28: Primitive Thread Synchronization Constructs |isbn=978-0-7356-2704-8}}</ref>
*The <code>Thread.VolatileWrite</code> method forces the value in the field to be written to at the point of the call. In addition, any earlier program-order loads and stores must occur before the call to <code>VolatileWrite</code> and any later program-order loads and stores must occur after the call.
*The <code>Thread.VolatileRead</code> method forces the value in the field to be read from at the point of the call. In addition, any earlier program-order loads and stores must occur before the call to <code>VolatileRead</code> and any later program-order loads and stores must occur after the call.
 
The <code>Thread.VolatileRead</code> and <code>Thread.VolatileWrite</code> methods generate a full fence by calling the <code>Thread.MemoryBarrier</code> method, which constructs a memory barrier that works in both directions. In addition to the motivations for using a full fence given above, one potential problem with the <code>volatile</code> keyword that is solved by using a full fence generated by <code>Thread.MemoryBarrier</code> is as follows: due to the asymmetric nature of half fences, a <code>volatile</code> field with a write instruction followed by a read instruction may still have the execution order swapped by the compiler. Because full fences are symmetric, this is not a problem when using <code>Thread.MemoryBarrier</code>.<ref name="Albahari"/>
 
==In Fortran==
<code>VOLATILE</code> is part of the [[Fortran#Fortran 2003|Fortran 2003]] standard,<ref>{{cite web|url=http://docs.cray.com/books/S-3692-51/html-S-3692-51/zfixedn3c8sk4c.html|title=VOLATILE Attribute and Statement|publisher=Cray|access-date=2016-04-22|archive-date=2018-01-23|archive-url=https://web.archive.org/web/20180123165050/http://docs.cray.com/books/S-3692-51/html-S-3692-51/zfixedn3c8sk4c.html|url-status=dead}}</ref> although earlier version supported it as an extension. Making all variables <code>volatile</code> in a function is also useful finding [[aliasing (computing)|aliasing]] related bugs.
<syntaxhighlight lang="fortran">
integer, volatile :: i ! When not defined volatile the following two lines of code are identical
write(*,*) i**2 ! Loads the variable i once from memory and multiplies that value times itself
write(*,*) i*i ! Loads the variable i twice from memory and multiplies those values
</syntaxhighlight>
 
By always "drilling down" to memory of a VOLATILE, the Fortran compiler is precluded from reordering reads or writes to volatiles. This makes visible to other threads actions done in this thread, and vice versa.<ref>{{cite web
|url=https://software.intel.com/en-us/forums/intel-moderncode-for-parallel-architectures/topic/279191
|title=Volatile and shared array in Fortran |website=Intel.com}}</ref>
 
Use of VOLATILE reduces and can even prevent optimization.<ref>{{cite web
|url=https://docs.oracle.com/cd/E19957-01/805-4939/6j4m0vnbq/index.html
|title=VOLATILE |website=Oracle.com}}</ref>
 
==References==
With this modification the code will remain as it is and the CPU will detect the change when it occurs.
{{Reflist|30em}}
 
==External links==
*[http://www.adaic.com/standards/05rm/html/RM-C-6.html Ada Reference Manual C.6: Shared Variable Control]
*[https://web.archive.org/web/20160304053622/https://www.kernel.org/doc/Documentation/volatile-considered-harmful.txt Linux kernel: volatile-considered-harmful]
 
[[Category:C (programming language)]]
*[http://www.programmersheaven.com/articles/pathak/article1.htm Use of volatile at Programmer's Heaven]
[[Category:Concurrency control]]
[[Category:Variable (computer science)]]