Volatile (computer programming): Difference between revisions

Content deleted Content added
Large cleanup. Fixed the introduction by mentioning that volatile is equally about reads as writes. Tried to clean up the introduction to be more accurate while still preserving the spirit of the volatile keyword in a way that is applicable to all programming languages. Also cleaned up the C/C++ and Java sections by removing unnecessary and duplicate information, and tried to make it flow better.
Tags: references removed Visual edit
No edit summary
Line 3:
In [[computer programming]], a '''volatile''' [[Value (computer science)|value]] is a value that can be read by something else or changed by something else while the current code is running.
 
Despite being a common keyword across many programming languages, the behavior of the <code>volatile</code> keyword differs in subtle and important ways between different programming languages. The common theme is that a <code>volatile</code> variable is a variable that may be read or modified by something outside of the current program[[thread (computing)|thread of execution]] while the current program[[thread (computing)|thread]] is running. In particular, the value of a <code>volatile</code> variable may appear change spontaneously without any apparent cause from the code in the [[thread (computing)|thread]] and sometimes without any apparent cause from the whole in the program. The typical things that can read or modify <code>Volatile</code> variables include hardware devices via [[memory-mapped I/O]] (where reading from or writing to memory is used to communicate with [[peripheral device]]s), asynchronous signal handlers, and other [[thread (computing)|threads of execution]], but not all programming languages use the <code>volatile</code> keyword for all of these examples.
 
Volatility can have implications regarding function [[calling convention]]s and how variables are stored, accessed and cached.
Line 10:
 
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 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 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.
*allowAllow usespreserving ofvalues variablesacross betweena <code>[[setjmp|longjmp]]</code> and <code>longjmp</code>.
*allowAllow usessharing values between signal handlers and the rest of the program in in <code>volatile</code> <code>sig_atomic_t</code> variables in signal handlersobjects.
 
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.
Loosely, the <code>volatile</code> keyword tells a C/C++ [[optimizing compiler]] to not remove reads and writes (including seemingly redundant reads and writes), and to not introduce new reads and writes, and to not change the relative order of reads and writes. For example, the [[optimizing compiler]] cannot store a value written to a <code>volatile</code> variable in a register and then use that register for a later read of the same <code>volatile</code> variable. Instead, the compiler must flush the value to main memory for every write instruction in the code, and it must fetch the value from main memory for every read instruction in the code. These strict guarantees about preserving the exact number and order of reads and writes are required to write correct code to access [[memory-mapped I/O]] devices, to write correct code using <code>[[setjmp]]</code> and <code>longjmp</code>, and to write correct code that shares values with asynchronous signal handlers.
 
=== Multi-Threading ===
It is a common misconception that the <code>volatile</code> keyword is useful forin portable [[thread (computing)|multi-threading]] code in C and C++. 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]]. The <code>volatile</code> keyword has ''never'' functioned as a useful, portable tool for ''any'' multi-threading scenario.<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 |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> Most C and C++ compilers, linkers, and runtimes simply do not provide the necessary guarantees (such as [[memory barrier|memory barriers]]) 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. TheWith the modern C11 and C++11 standards, programmers can introduceduse new constructs such as the <code>std::atomic<T></code> templatesto which allowed writingwrite portable [[thread (computing)|multi-threading]] code.<ref>{{cite web |title=volatile (C++) |url=https://msdn.microsoft.com/en-us/library/12a04hfd.aspx |work=Microsoft MSDN}}</ref>
 
===Example of memory-mapped I/O in C===
Line 214 ⟶ 216:
 
* <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., Itand it will not read an older value or a value the future, and itall <code>volatile</code> reads will notagree readon a valuesingle fromglobal theorder futureof <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 theprovidesthe 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>