Volatile (keyword): Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
Line 5: Line 5:
== The basics ==
== The basics ==


The volatile keyword can be used in type definitions as well as function arguments and variable definitions. Similar to the '''const''' keyword, it either makes the variable itself or the data it points to ''volatile'', or both.
The volatile keyword can be used in type definitions as well as function arguments, variable definitions and typecasts. Similar to the '''const''' keyword, it either makes the variable itself or the data it points to ''volatile'', or both.


/* Ensures the changes to/reads from x are always performed */
/* Ensures the changes to/reads from x are always performed */

Revision as of 19:16, 9 May 2007

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 volatile keyword can be used in type definitions as well as function arguments, variable definitions and typecasts. Similar to the const keyword, it either makes the variable itself or the data it points to volatile, or both.

/* Ensures the changes to/reads from x are always performed */
volatile int x;

/* Ensures that changes to/reads from the data that ptr points to
   (but not the value itself) are always performed */
volatile int *ptr;

/* Ensures that changes to/reads from the pointer ptr (but not the
   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 */
volatile int * volatile ptr;

Examples

Loops

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:

static void some_delay(void)
{
    int i;
    /* Should be declared as volatile int i; to prevent
       the loop or even the entire function from being optimized
       away */
    
    for (i = 0; i < 100; i++)
    {
        /* Do nothing, just wait a little while so that
           some hardware can respond. Using this technique
           is rarely neccessary and should be avoided! */
    }
}

This example is supposed to poll a byte until it is 0 (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:

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... */
    }
}

Dereferencing memory

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):

static void touch_mem(void *ptr)
{
    char *data = (char *)ptr;
    /* Should be declared as volatile char *data = (volatile char *)ptr;
       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;
}

Also see an example code where the volatile keyword is crucial (reading/writing to hardware registers): APIC