Volatile (computer programming): Difference between revisions

Content deleted Content added
expand ref
Link suggestions feature: 2 links added.
 
(44 intermediate revisions by 24 users not shown)
Line 1:
{{Short description|Keyword used in some programming languages to tag variables}}
{{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++==
In [[computer programming]], particularly in the [[C (programming language)|C]], [[C++]], [[C Sharp (programming language)|C#]], and [[Java (programming language)|Java]] [[programming language]]s, the '''volatile''' [[keyword (computer programming)|keyword]] indicates that a [[Value (computer science)|value]] may change between different accesses, even if it does not appear to be modified. This keyword prevents an [[optimizing compiler]] from optimizing away subsequent reads or writes and thus incorrectly reusing a stale value or omitting writes. Volatile values primarily arise in hardware access ([[memory-mapped I/O]]), where reading from or writing to memory is used to communicate with [[peripheral device]]s, and in [[thread (computing)|threading]], where a different thread may have modified a value.
 
DespiteIn beingC aand common keywordC++, the behavior of <code>volatile</code> differs significantly between programming languages, and is easily misunderstood. In C and C++, it is a [[type qualifier]], like <code>[[const (computer programming)|const]]</code>, and is a propertypart of thea ''[[data type|type]]''. Furthermore, in C and C++ it does ''not'' work in most threading scenarios, and that use is discouraged(e.g. Inthe Java and C#, it is a propertytype of a [[variable (computeror sciencefield)|variable]] and indicates that the [[object (computer science)|object]] to which the variable is bound may mutate, and is specifically intended for threading. In the [[D (programming language)|D]] programming language, there is a separate keyword <code>shared</code> for the threading usage, but no <code>volatile</code> keyword exists.
 
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.
==In C and C++==
 
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>
*allowAllow access to [[memory-mapped I/O]] devices.
*Allow preserving values across a <code>[[setjmp|longjmp]]</code>.
*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 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>
*allow access to [[memory-mapped I/O]] devices
*allow uses of variables between <code>[[setjmp]]</code> and <code>longjmp</code>
*allow uses of <code>sig_atomic_t</code> variables in signal handlers.
 
=== Multi-threading ===
Operations on <code>volatile</code> variables are not [[atomic operation|atomic]], nor do they establish a proper happens-before relationship for threading. This is specified in the relevant standards (C, C++, [[POSIX]], WIN32),<ref name="auto"/> and volatile variables are not threadsafe in the vast majority of current implementations. Thus, the usage of <code>volatile</code> keyword as a portable synchronization mechanism is discouraged by many C/C++ groups.<ref>{{cite web |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 |title=C++ and the Perils of Double-Checked Locking|url=http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf|work=DDJ}}</ref>
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>
 
===Example of memory-mapped I/O in C===
Line 39 ⟶ 50:
</syntaxhighlight>
 
However, the programmer may make <code>foo</code> mightrefer represent a ___location thatto cananother be changed by other elementselement of the computer system at any time, 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. The(This aboveexample does not include the details on how to make <code>foo</code> wouldrefer neverto detecta suchhardware register of a change;device withoutconnected to the CPU.) Without the <code>volatile</code> keyword, thean [[optimizing compiler]] assumeswill thatlikely convert the currentcode programfrom isthe first sample with the onlyread in the loop to the second sample without the read in the loop as part of the systemcommon that[[Loop-invariant couldcode changemotion|loop-invariant code-motion optimization]], and thus the valuecode (whichwill islikely bynever farnotice the mostchange commonthat situation)it is waiting for.
 
To prevent the compiler from optimizingdoing code asthis aboveoptimization, the <code>volatile</code> keyword iscan be used:
 
<syntaxhighlight lang="c">
Line 54 ⟶ 65:
</syntaxhighlight>
 
WithThe this<code>volatile</code> modificationkeyword prevents the loopcompiler conditionfrom willmoving notthe beread optimizedout awayof the loop, and thus the systemcode will detectnotice the expected change whento the itvariable occurs<code>foo</code>.
 
Generally, there are [[memory barrier]] operations available on platforms (which are exposed in C++11) that should be preferred instead of volatile as they allow the compiler to perform better optimization and more importantly they guarantee correct behaviour in multi-threaded scenarios; neither the C specification (before C11) nor the C++ specification (before C++11) specifies a multi-threaded memory model, so volatile may not behave deterministically across OSes/compilers/CPUs).<ref>{{cite web |url=http://kerneltrap.org/Linux/Volatile_Superstition|title=Linux: Volatile Superstition|publisher=kerneltrap.org|accessdate=Jan 9, 2011|archive-url=https://web.archive.org/web/20100620121940/http://kerneltrap.org/Linux/Volatile_Superstition|author1=Jeremy Andrews|year=2007}}</ref>
 
===Optimization comparison in C===
The following C programs, and accompanying assembliesassembler 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.
Line 202 ⟶ 211:
|}
 
===C++11 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>
According to the C++11 ISO Standard, the volatile keyword is only meant for use for hardware access; do not use it for inter-thread communication. For inter-thread communication, the standard library provides <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}}</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==
TheIn all modern versions of the [[Java programming language]] also has, the <code>volatile</code> keyword, but it is used for a somewhat different purpose. When applied to a field, the Java qualifier <code>volatile</code> providesgives the following guarantees:
*In all versions of Java, there is a global ordering on reads and writes of all volatile variables (this global ordering on volatiles is a partial order over the larger ''synchronization order'' (which is a total order over all ''synchronization actions'')). This implies that every [[thread (computer science)|thread]] accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)
*In Java 5 or later, volatile reads and writes establish a [[happened-before|happens-before relationship]], much like acquiring and releasing a mutex.<ref>Section 17.4.4: Synchronization Order
{{cite web
|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
|publisher=[[Oracle Corporation]]
|year=2013
|accessdate=2013-05-12
}}</ref>
 
* <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.)
Using <code>volatile</code> may be faster than a [[lock (computer science)|lock]], but it will not work in some situations before Java 5<ref>{{cite web
* 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.
|title=JSR 133 (Java Memory Model) FAQ
* <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
|url=https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
{{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]]).
|author1=Jeremy Manson
 
|author2=Brian Goetz
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>
|date=February 2004
 
|accessdate=2019-11-05
=== Early versions of Java ===
}}</ref>. The range of situations in which volatile is effective was expanded in Java 5; in particular, [[double-checked locking]] now works correctly.<ref>{{cite web
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.
|title=Double-checked Locking (DCL) and how to fix it
|url=http://www.javamex.com/tutorials/double_checked_locking_fixing.shtml
|author1=Neil Coffey
|publisher=Javamex
|accessdate=2009-09-19}}</ref>
 
==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. OnlyWhen a field is marked <code>volatile</code>, the followingcompiler typesis caninstructed beto markedgenerate volatile:a all"memory referencebarrier" types,or Single,"fence" Boolean,around Byteit, SByte,which Int16,prevents UInt16,instruction Int32,reordering UInt32or caching tied to the field. When reading a <code>volatile</code> field, Charthe compiler generates an ''acquire-fence'', which prevents other reads and allwrites enumeratedto typesthe withfield anfrom underlyingbeing typemoved of''before'' Byte,the SByte,fence. Int16,When UInt16,writing Int32to a <code>volatile</code> field, orthe UInt32compiler generates a ''release-fence''; this fence prevents other reads and writes to the field from being moved ''after'' the fence.<ref name="Albahari">{{cite bookweb |last1=RichterAlbahari |first1=JeffreyJoseph |title=CLRPart Via4: Advanced Threading |url=http://www.albahari.com/threading/part4.aspx |website=Threading in C# |publisher=MicrosoftO'Reilly PressMedia |access-date=February9 11,December 20102019 |pagesarchive-url=183https://web.archive.org/web/20191212032535/http://www.albahari.com/threading/part4.aspx#_Nonblocking_Synchronization |chapterarchive-date=Chapter12 7:December Constants and Fields2019 |isbnurl-status=978-0-7356-2704-8bot: unknown }}</ref> (This excludes value [[struct]]s, as well as the primitive types Double, Int64, UInt64 and Decimal.)
 
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 addressthe 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 addressthe field to be read from at the point of the call. In addition, any laterearlier program-order loads and stores must occur afterbefore 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"/>
Basically <code>volatile</code> is a shorthand for calling <code>Thread.VolatileRead</code> and <code>Thread.VolatileWrite</code>. These methods are special. In effect, these methods disable some optimizations usually performed by the C# compiler, the JIT compiler, and the CPU itself. The methods work as follows:<ref>{{cite book |last1=Richter |first1=Jeffrey |title=CLR Via C# |publisher=Microsoft Press |date=February 11, 2010 |pages=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 address 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 VolatileWrite.
*The <code>Thread.VolatileRead</code> method forces the value in address to be read from at the point of the call. In addition, any later program-order loads and stores must occur after the call to VolatileRead.
*The <code>Thread.MemoryBarrier</code> method does not access memory but it forces any earlier program order loads and stores to be completed before the call to MemoryBarrier. It also forces any later program-order loads and stores to be completed after the call to MemoryBarrier. MemoryBarrier is much less useful than the other two methods.{{Citation needed|date=September 2012}}
 
==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
Line 250 ⟶ 254:
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