Why is volatile needed in C? What is it used for? What will it do?
- 4http://stackoverflow.com/questions/72552/c-when-has-the-volatile-keyword-ever-helped-youFrederik Slijkerman– Frederik Slijkerman2008-10-29 08:45:53 +00:00Commented Oct 29, 2008 at 8:45
- Related: When to use volatile with multi threading? applies to C11 and later as well. It's not needed for that, but was used until C11 gave a fully standardized way to do lock-free atomic load/store.Peter Cordes– Peter Cordes2022-07-05 14:42:14 +00:00Commented Jul 5, 2022 at 14:42
- Similar for C# and Java: What is the "volatile" keyword used for?Peter Mortensen– Peter Mortensen2023-06-19 14:48:32 +00:00Commented Jun 19, 2023 at 14:48
- An even earlier one (2008-09-16): Why does volatile exist?Peter Mortensen– Peter Mortensen2023-06-19 15:14:28 +00:00Commented Jun 19, 2023 at 15:14
19 Answers
volatile tells the compiler not to optimize anything that has to do with the volatile variable.
There are at least three common reasons to use it, all involving situations where the value of the variable can change without action from the visible code:
- When you interface with hardware that changes the value itself
- when there's another thread running that also uses the variable
- when there's a signal handler that might change the value of the variable.
Let's say you have a little piece of hardware that is mapped into RAM somewhere and that has two addresses: a command port and a data port:
typedef struct { int command; int data; int isBusy; } MyHardwareGadget; Now you want to send some command:
void SendCommand(MyHardwareGadget* gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isBusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; } Looks easy, but it can fail because the compiler is free to change the order in which data and commands are written. This would cause our little gadget to issue commands with the previous data-value. Also take a look at the wait while busy loop. That one will be optimized out. The compiler will try to be clever, read the value of isBusy just once and then go into an infinite loop. That's not what you want.
The way to get around this is to declare the pointer gadget as volatile. This way the compiler is forced to do what you wrote. It can't remove the memory assignments, it can't cache variables in registers and it can't change the order of assignments either
This is the correct version:
void SendCommand(volatile MyHardwareGadget* gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isBusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; } 20 Comments
volatile in C actually came into existence for the purpose of not caching the values of the variable automatically. It will tell the compiler not to cache the value of this variable. So it will generate code to take the value of the given volatile variable from the main memory every time it encounters it. This mechanism is used because at any time the value can be modified by the OS or any interrupt. So using volatile will help us accessing the value afresh every time.
10 Comments
volatile was to make it possible for compilers to optimize code while still allowing programmers to achieve the semantics that would be achieved without such optimizations. The authors of the Standard expected that quality implementations would support whatever semantics were useful given their target platforms and application fields, and didn't expect that compiler writers would seek to offer the lowest quality semantics that conform to the Standard and weren't 100% stupid (note that the authors of the Standard explicitly recognize in the rationale...Another use for volatile is signal handlers. If you have code like this:
int quit = 0; while (!quit) { /* very small loop which is completely visible to the compiler */ } The compiler is allowed to notice the loop body does not touch the quit variable and convert the loop to a while (true) loop. Even if the quit variable is set on the signal handler for SIGINT and SIGTERM; the compiler has no way to know that.
However, if the quit variable is declared volatile, the compiler is forced to load it every time, because it can be modified elsewhere. This is exactly what you want in this situation.
9 Comments
quit, the compiler can optimise it into a constant loop, assuming that there's no way for quit to be changed between iterations. N.B.: This isn't necessarily a good substitute for actual threadsafe programming.volatile or other markers, it will assume that nothing outside the loop modifies that variable once it enters the loop, even if it's a global variable.extern int global; void fn(void) { while (global != 0) { } } with gcc -O3 -S and look at the resulting assembly file, on my machine it does movl global(%rip), %eax; testl %eax, %eax; je .L1; .L4: jmp .L4, that is, an infinite loop if the global is not zero. Then try adding volatile and see the difference.volatile tells the compiler that your variable may be changed by other means, than the code that is accessing it. e.g., it may be a I/O-mapped memory location. If this is not specified in such cases, some variable accesses can be optimised, e.g., its contents can be held in a register, and the memory location not read back in again.
1 Comment
See this article by Andrei Alexandrescu, "volatile - Multithreaded Programmer's Best Friend"
The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events. For example, if you declare a primitive variable as volatile, the compiler is not permitted to cache it in a register -- a common optimization that would be disastrous if that variable were shared among multiple threads. So the general rule is, if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile. But you can actually do a lot more with this keyword: you can use it to catch code that is not thread safe, and you can do so at compile time. This article shows how it is done; the solution involves a simple smart pointer that also makes it easy to serialize critical sections of code.
The article applies to both C and C++.
Also see the article "C++ and the Perils of Double-Checked Locking" by Scott Meyers and Andrei Alexandrescu:
So when dealing with some memory locations (e.g. memory mapped ports or memory referenced by ISRs [ Interrupt Service Routines ] ), some optimizations must be suspended. volatile exists for specifying special treatment for such locations, specifically: (1) the content of a volatile variable is "unstable" (can change by means unknown to the compiler), (2) all writes to volatile data are "observable" so they must be executed religiously, and (3) all operations on volatile data are executed in the sequence in which they appear in the source code. The first two rules ensure proper reading and writing. The last one allows implementation of I/O protocols that mix input and output. This is informally what C and C++'s volatile guarantees.
8 Comments
volatile does not guarantee atomicity.volatile behavior would be necessary to make an implementation suitable for any particular purpose, such as programming a particular real machine. Instead, it expects implementers to recognize the ways in which the real target machine may differ from the abstract one, and thus recognize what semantics users of particular platforms will need and give it to them. On a hardware platform where a load or store may trigger actions that affect program execution, a quality implementation should treat such actions as...My simple explanation is:
In some scenarios, based on the logic or code, the compiler will do optimisation of variables which it thinks do not change. The volatile keyword prevents a variable being optimised.
For example:
bool usb_interface_flag = 0; while(usb_interface_flag == 0) { // execute logic for the scenario where the USB isn't connected } From the above code, the compiler may think usb_interface_flag is defined as 0, and that in the while loop it will be zero forever. After optimisation, the compiler will treat it as while(true) all the time, resulting in an infinite loop.
To avoid these kinds of scenarios, we declare the flag as volatile, we are telling to compiler that this value may be changed by an external interface or other module of program, i.e., please don't optimise it. That's the use case for volatile.
Comments
A marginal use for volatile is the following. Say you want to compute the numerical derivative of a function f :
double der_f(double x) { static const double h = 1e-3; return (f(x + h) - f(x)) / h; } The problem is that x+h-x is generally not equal to h due to roundoff errors. Think about it : when you substract very close numbers, you lose a lot of significant digits which can ruin the computation of the derivative (think 1.00001 - 1). A possible workaround could be
double der_f2(double x) { static const double h = 1e-3; double hh = x + h - x; return (f(x + hh) - f(x)) / hh; } but depending on your platform and compiler switches, the second line of that function may be wiped out by a aggressively optimizing compiler. So you write instead
volatile double hh = x + h; hh -= x; to force the compiler to read the memory location containing hh, forfeiting an eventual optimization opportunity.
5 Comments
h or hh in derivative formula? When hh is computed the last formula uses it like the first one, with no difference. Maybe it should be (f(x+h) - f(x))/hh?h and hh is that hh is truncated to some negative power of two by the operation x + h - x. In this case, x + hh and x differ exactly by hh. You can also take your formula, it will give the same result, since x + h and x + hh are equal (it is the denominator which is important here).x1=x+h; d = (f(x1)-f(x))/(x1-x)? without using the volatile.-ffast-math or equivalent.I'll mention another scenario where volatiles are important.
Suppose you memory-map a file for faster I/O and that file can change behind the scenes (e.g. the file is not on your local hard drive, but is instead served over the network by another computer).
If you access the memory-mapped file's data through pointers to non-volatile objects (at the source code level), then the code generated by the compiler can fetch the same data multiple times without you being aware of it.
If that data happens to change, your program may become using two or more different versions of the data and get into an inconsistent state. This can lead not only to logically incorrect behavior of the program but also to exploitable security holes in it if it processes untrusted files or files from untrusted locations.
If you care about security, and you should, this is an important scenario to consider.
Comments
There are two uses. These are specially used more often in embedded development.
The compiler will not optimise the functions that use variables that are defined with the volatile keyword
Volatile is used to access exact memory locations in RAM, ROM, etc... This is used more often to control memory-mapped devices, access CPU registers and locate specific memory locations.
See examples with assembly listing. Re: Usage of C "volatile" Keyword in Embedded Development
2 Comments
volatile can be used to force memory reads or writes to be a particular size. For example, there can be 8-bit only memory, or 16-bit only memory, and a read or write of the wrong type will have an undesirable effect. Without volatile, the compiler is free to combine four 8-bit writes into a 32-bit write, even if the memory does not support that kind of write. There are even cases where a for loop gets changed into memset which attempts to write bytes to 16-bit only memory, but this disappears when using volatile.In my opinion, you should not expect too much from volatile. To illustrate, look at the example in Nils Pipenbrinck's highly-voted answer.
I would say, his example is not suitable for volatile. volatile is only used to: prevent compiler optimizations that would normally be useful and desirable. It is nothing about thread safety, atomic access or even memory order.
In that example:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data) { // Wait while the gadget is busy: while (gadget->isbusy) { // Do nothing here. } // Set data first: gadget->data = data; // Writing the command starts the action: gadget->command = command; } The gadget->data = data before gadget->command = command only is only guaranteed in the compiled code by the compiler.
At run time, the processor may still reorder the data and command assignment, depending on the processor architecture. The hardware could get the wrong data (suppose a gadget is mapped to hardware I/O). A memory barrier is needed between data and command assignment.
Comments
In the language designed by Dennis Ritchie, every access to any object, other than automatic objects whose address had not been taken, would behave as though it computed the address of the object and then read or wrote the storage at that address. This made the language very powerful, but severely limited optimization opportunities.
While it might have been possible to forbid compilers from assuming that addressable objects would never be changed in weird ways, such an assumption would have been appropriate and useful for the vast majority of objects in C programs, and it would have been impractical to add a qualifier to all the objects for which such assumption would be appropriate. On the other hand, some programs need to use some objects for which such an assumption would not hold. To resolve this issue, the Standard says that compilers may assume that objects which are not declared volatile will not have their value observed or changed in ways that are outside the compiler's control, or would be outside a reasonable compiler's understanding.
Because various platforms may have different ways in which objects could be observed or modified outside a compiler's control, it is appropriate that quality compilers for those platforms should differ in their exact handling of volatile semantics. Unfortunately, because the Standard failed to suggest that quality compilers intended for low-level programming on any particular platform should handle volatile in a way that will recognize any and all relevant effects of a particular read/write operation on that platform, many compilers fall short of doing so in ways that make it harder to process things like background I/O in a way which is efficient but can't be broken by compiler "optimizations".
Comments
volatile means the storage is likely to change at any time and be changed by something outside the control of the user program.
This means that if you reference the variable, the program should always check the physical address (i.e., a mapped input FIFO), and not use it in a cached way.
1 Comment
In simple terms, it tells the compiler not to do any optimisation on a particular variable. Variables which are mapped to device register are modified indirectly by the device. In this case, volatile must be used.
1 Comment
Wikipedia says everything about volatile:
And the Linux kernel's documentation also makes a excellent note about volatile:
Comments
A volatile can be changed from outside the compiled code (for example, a program may map a volatile variable to a memory mapped register).
The compiler won't apply certain optimizations to code that handles a volatile variable. For example, it won't load it into a register without writing it to memory. This is important when dealing with hardware registers.
Comments
Volatile is often misunderstood as "disabling optimizations," synchronizing access to a variable, or generating memory fences. None are entirely the case. All it does is disable any compiler optimizations that assume the variable has not changed from an outside context.
What Volatile Does According to GCC
Eliminating Opimizations Effect
To understand volatile, we can simply look at the code generated by GCC. In the first example we will multiply the volatile variable with argc and store in i.
volatile int i = 0; int main(int argc, char **argv) { i *= argc; return 0; } See assembly here with O2: https://godbolt.org/z/cf4KGjxvP
main: mov eax, DWORD PTR i[rip] add eax, 1 mov DWORD PTR i[rip], eax xor eax, eax ret i: .zero 4 Volatile tells the compiler that the variable cannot be expected at compile time to have remained the same between accesses. It could change at any point during execution. The compiler is told to disable dead store elimination since doing so could lead to unexpected behavior.
Referencing the variable without doing anything to it also generates a load. https://godbolt.org/z/z9cffTsqb
Basically, the compiler disables any optimization that eliminates loads or stores.
Is It A Fence? On GCC, Yes.
On GCC, volatile appears to act as a compiler fence. This code:
volatile int i,j,k,l; int main(int argc, char **argv) { i += j + k + l; } When the order of the addition is changed, the corresponding assembly has a different load order. See: https://godbolt.org/z/z9KG3PnbE
So on GCC, volatile is a compiler memory fence, but no fence instructions are generated to serialize access.
On MSVC, the same sample was generated with the loads in reverse, but changing the variable order did also affect the load order. MSVC also regards it as a compiler fence. https://godbolt.org/z/snWz6fYa6
What Volatile Does Not Do
Volatile is not a memory fence. The compiler could treat it as one, but there is no guarantee. It is free to reorder memory accesses as long as it does not eliminate them. That means if you really need a memory access to happen between two function calls, it may not be generated that way.
Instead, a compiler or real memory fence must be explicitly used. Concurrent access between multiple processors may not work unless controlled with a lock or fence.
Volatile does not ensure atomicity. The loads and stores will happen with single instructions, but atomic operations may not be used. In the first example, the number was incremented with a single operation.
Conclusion
Volatile provides very few guarantees about the behavior of the variable. It should be considered non-portable for any situation outside its very narrow scope. Volatile variable access should be encapsulated inside helper functions and volatile function parameters or returns should be avoided.
The situations where volatile should be used are:
- Memory mapped IO registers
- Signal handler variables
- Interrupt service routine variables (for example, waiting in a loop until device gives "ready" signal)
- Testing the effectiveness of optimization on a single variable
Multithreaded programming should never include volatile since acquiring a critical section for a variable ensures that within it all optimizations are allowed because nothing else can access the variable.
1 Comment
volatile access will be act as a global barrier against compiler reordering, but so far as I can tell the only way to configure clang and gcc to reliably work that way without using special syntax is -O0.As rightly suggested by many here, the volatile keyword's popular use is to skip the optimisation of the volatile variable.
The best advantage that comes to mind, and worth mentioning after reading about volatile is -- to prevent rolling back of the variable in case of a longjmp. A non-local jump.
What does this mean?
It simply means that the last value will be retained after you do stack unwinding, to return to some previous stack frame; typically in case of some erroneous scenario.
Since it'd be out of scope of this question, I am not going into details of setjmp/longjmp here, but it's worth reading about it; and how the volatility feature can be used to retain the last value.