volatile
has semantics for memory visibility. Basically, the value of a volatile
field becomes visible to all readers (other threads in particular) after a write operation completes on it. Without volatile
, readers could see some non-updated value.
To answer your question: Yes, I use a volatile
variable to control whether some code continues a loop. The loop tests the volatile
value and continues if it is true
. The condition can be set to false
by calling a "stop" method. The loop sees false
and terminates when it tests the value after the stop method completes execution.
The book "Java Concurrency in Practice," which I highly recommend, gives a good explanation of volatile
. This book is written by the same person who wrote the IBM article that is referenced in the question (in fact, he cites his book at the bottom of that article). My use of volatile
is what his article calls the "pattern 1 status flag."
If you want to learn more about how volatile
works under the hood, read up on the Java memory model. If you want to go beyond that level, check out a good computer architecture book like Hennessy & Patterson and read about cache coherence and cache consistency.
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;
}
Best Answer
A pointer of the form
is a pointer to an
int
that the compiler will treat asvolatile
. This means that the compiler will assume that it is possible for the variable thatp
is pointing at to have changed even if there is nothing in the source code to suggest that this might occur. For example, if I setp
to point to a regular integer, then every time I read or write*p
, the compiler is aware that the value may have changed unexpectedly.There is one more use case for a
volatile int*
: If you declare anint
asvolatile
, then you should not point at it with a regularint*
. For example, this is a bad idea:The reason for this is that the C compiler no longer remembers that the variable pointed at by
ptr
isvolatile
, so it might cache the value of*ptr
in a register incorrectly. In fact, in C++, the above code is an error. Instead, you should write:Now, the compiler remembers that
ptr
points at avolatile int
, so it won't (or shouldn't!) try to optimize accesses through*ptr
.One last detail - the pointer you discussed is a pointer to a
volatile int
. You can also do this:This says that the pointer itself is
volatile
, which means that the compiler shouldn't try to cache the pointer in memory or try to optimize the pointer value because the pointer itself might get reassigned by something else (hardware, etc.) You can combine these together if you'd like to get this beast:This says that both the pointer and the pointee could get changed unexpectedly.The compiler can't optimize the pointer itself, and it can't optimize what's being pointed at.
Hope this helps!