Volatile (keyword): Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
No edit summary
 
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(18 intermediate revisions by 7 users not shown)
Line 1:
The <code>volatile</code> keyword gives an indication to the compiler/optimizer that it should always perform a read or write to a variable or memory without caching it locally. It also ensures that the order of changes is preserved and not altered by the optimizer, and that apparently redundant code is not optimized away. The keyword however doesn't guarantee that memory access isn't reordered by the processor on runtime. To fix possible issues due to this, make sure you use [[atomic operation]]s or [[memory barriers]] to manipulate memory.
== Definition ==
 
The volatile statement gives an indication to the compiler that it should always perform a read or write to a variable or memory without caching it locally. It also ensures that the order of changes is preserved and not altered by the optimizer, and that apparently redundant code is optimized away.
 
== The basics ==
 
The <code>volatile</code> keyword can be used in type definitions as well as function arguments and, variable definitions and typecasts. Similar to the '''<code>const'''</code> keyword, it either makes the variable itself or the data it points to ''<code>volatile''</code>, or both.
 
<syntaxhighlight lang="c">
/* Ensures the changes to/reads from x are always performed */
/* Ensures the changes to/reads from x are always performed */
volatile int x;
volatile int x;
 
/* Ensures that changes to/reads from the data that ptr points to
/* Ensures that changes to/reads from the data that ptr points to
(but not the value itself) are always performed */
(but not the value itself) are always performed */
volatile int *ptr;
volatile int *ptr;
int volatile *ptr;
/* Ensures that changes to/reads from the pointer ptr (but not the
 
data it points to) are always performed */
/* Ensures that changes to/reads from the pointer ptr (but not the
int * volatile ptr;
data it points to) are always performed */
int * volatile ptr;
/* Ensures that changes to/reads from the pointer ptr and also
 
the data it points to are always performed */
/* Ensures that changes to/reads from the pointer ptr and also
volatile int * volatile ptr;
the data it points to are always performed */
volatile int * volatile ptr;
int volatile * volatile ptr;
</syntaxhighlight>
 
== Examples ==
Line 28 ⟶ 30:
This is an example of a function that is supposed to create a very short delay, but might be optimized away by the compiler entirely because to the optimizer this code seems redundant:
 
<syntaxhighlight lang="c">
static void some_delay(void)
static void some_delay(void)
{
{
int i;
int i;
/* Should be declared as '''volatile int i;''' to prevent
/* Should be declared as '''volatile int i;''' to prevent
the loop or even the entire function from being optimized
the loop or even the entire function from being optimized
away */
away */
for (i = 0; i < 100; i++)
for (i = 0; i < 100; i++)
{
{
/* Do nothing, just wait a little while so that
/* Do nothing, just somewait hardwarea canlittle respond.while Usingso this techniquethat
some ishardware rarelycan neccessaryrespond. andUsing shouldthis be avoided! */technique
is rarely neccessary and should be avoided! */
}
}
}
}
</syntaxhighlight>
 
This example is supposed to poll a byte until it is <code>0</code> (e.g. waiting for a [[Spinlock]] to be released). Obviously another thread is supposed to change that memory, but since the compiler has no clue about this, we need to ensure that the code isn't optimized away in any case:
 
<syntaxhighlight lang="c">
typedef unsigned char * spin_lock;
/* Should be defined as '''typedef volatile unsigned char * spin_lock;'''
to prevent reading/writing to spin_lock variables being cached
in CPU registers! */
 
static void poll_spinlock(spin_lock lock)
{
while (*lock != 0)
{
/* Do nothing, just poll... */
}
}
 
/* Alternatively, if spin_lock wasn't defined as a pointer to volatile data
you can use it in a typecast instead: */
static void poll_spinlock(spin_lock lock)
{
while (*(volatile unsigned char *)lock != 0)
{
/* Do nothing, just poll... */
}
}
</syntaxhighlight>
 
=== Dereferencing memory ===
Line 47 ⟶ 78:
This is an example of code that is supposed to touch a piece of memory (e.g. to make it become resident or to check if it's valid and doesn't raise a page fault):
 
<syntaxhighlight lang="c">
static void touch_mem(void *ptr)
static void touch_mem(void *ptr)
{
{
char *data = (char *)ptr;
/* Should be declared as '''volatile char *data = (volatile char *)ptr;'''
/* Should be declared as '''volatile char *data = (volatile char *)ptr;'''
to prevent the code from being optimized away */
to prevent the code from being optimized away */
/* It is assumed that the memory is not used by other threads at the same time */
*data = *data;
}
</syntaxhighlight>
 
Also see the [[APIC#IO_APIC_Configuration|APIC example code]] where the <code>volatile</code> keyword is crucial (reading/writing to hardware registers).
 
=== Memory shared by multiple threads ===
 
Suppose you have one thread "A" that loops until another thread "B" wants thread "A" to terminate gracefully. They share a structure with a field "terminate" that thread "A" polls to see if another thread wants it to terminate. Unless that field or the entire structure is declared <code>volatile</code>, there's no guarantee that the code works as expected because the compiler/optimizer has no clue about that field being touched by someone else while looping. So it could generate code that reads the value once and caches it, resulting in an infinite loop. Use <code>volatile</code> to prevent wrong code being generated:
 
<syntaxhighlight lang="c">
typedef struct {
int terminate; /* Should be volatile int terminate; */
} shared_thread_data;
 
/* This is the code run by thread "B" to get thread "A" to terminate gracefully */
void stop_worker_thread(shared_thread_data *thread)
{
thread->terminate = 1;
/* Techni<cally incorrect, you should use a function that guarantees
an atomic operation (e.g. lock xchg)
}
 
/* This is the thread "A" loop */
void worker_thread_body(shared_thread_data *thread)
{
while (thread->terminate == 0)
{
/* Do some work here... */
}
}
</syntaxhighlight>
 
== External Links ==
/* It is assumed that the memory is not used by other threads at the same time */
[http://www.mjmwired.net/kernel/Documentation/volatile-considered-harmful.txt Volatile considered harmful]
*data = *data;
}
 
[[Category:C]]
Also see an example code where the volatile keyword is crucial (reading/writing to hardware registers): [[APIC#IO_APIC_Configuration|APIC]]
[[Category:C++]]