Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is volatile needed in C?

People also ask

What is the use of volatile?

volatile is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time-without any action being taken by the code the compiler finds nearby.

Where volatile variables are stored in C?

Volatile values primarily arise in hardware access (memory-mapped I/O), where reading from or writing to memory is used to communicate with peripheral devices, and in threading, where a different thread may have modified a value.

What is volatile and non volatile in C?

Volatile Memory is used to store computer programs and data that CPU needs in real time and is erased once computer is switched off. RAM and Cache memory are volatile memory. Where as Non-volatile memory is static and remains in the computer even if computer is switched off. ROM and HDD are non-volatile memory.

What is difference between static and volatile in C?

A static variable refers to a class variable that's shared among all instances. volatile: Volatile variables are those which are read and written to main memory. They aren't stored in local cache and are always fetched from main memory.


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; or 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;
}

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.


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.


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.


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.