volatile (C+)

The volatile keyword is a type qualifier used to declare that an object can be modified in the program by something such as the operating system, the hardware, or a concurrently executing thread.

volatile declarator ;

Remarks

The following example declares a volatile integer nVint whose value can be modified by external processes:

int volatile nVint;

Microsoft Specific

Objects declared as volatile are not used in certain optimizations because their values can change at any time. The system always reads the current value of a volatile object at the point it is requested, even if a previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.

Also, when optimizing, the compiler must maintain ordering among references to volatile objects as well as references to other global objects. In particular,

  • A write to a volatile object (volatile write) has Release semantics; a reference to a global or static object that occurs before a write to a volatile object in the instruction sequence will occur before that volatile write in the compiled binary.

  • A read of a volatile object (volatile read) has Acquire semantics; a reference to a global or static object that occurs after a read of volatile memory in the instruction sequence will occur after that volatile read in the compiled binary.

This allows volatile objects to be used for memory locks and releases in multithreaded applications.

Note

Although the processor will not reorder un-cacheable memory accesses, un-cacheable variables must be volatile to guarantee that the compiler will not change memory order.

Important noteImportant Note:

If a struct does not have a length that can be copied on the current architecture by using one instruction, volatile may be lost on that struct.

End Microsoft Specific

One use of the volatile qualifier is to provide access to memory locations used by asynchronous processes such as interrupt handlers.

Note

When used on a variable that also has the __restrict keyword, volatile will take precedence.

Example

This program uses two features of volatile memory.

  • It must honor all reads and writes from volatile memory; this allows it to be used for a mutex.

  • It cannot move a reference to global data down across a volatile write; this allows you to use it to guard a critical section.

  • It cannot move a reference to global data up across a volatile read; this ensures a critical section is exited before loading critical data.

// volatile.cpp
// compile with: /EHsc /O2
#include <iostream>
#include <windows.h>
using namespace std;

volatile bool Sentinel = true;
int CriticalData = 0;

unsigned ThreadFunc1( void* pArguments ) {
   while (Sentinel)
      Sleep(0);   // volatile spin lock

   // CriticalData load guaranteed after every load of Sentinel
   cout << "Critical Data = " << CriticalData << endl;
   return 0;
} 

unsigned  ThreadFunc2( void* pArguments ) {
   Sleep(2000);
   CriticalData++;   // guaranteed to occur before write to Sentinel
   Sentinel = false; // exit critical section
   return 0;
}

int main() {
   HANDLE hThread1, hThread2; 
   DWORD retCode;

   hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,
      NULL, 0, NULL);
   hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,
      NULL, 0, NULL);
   if (hThread1 == NULL || hThread2 == NULL) {
      if (hThread1 != NULL) CloseHandle(hThread1);
      if (hThread2 != NULL) CloseHandle(hThread2);
      cout << "CreateThread failed." << endl; 
      return 1;
   }

   retCode = WaitForSingleObject(hThread1,3000);

   CloseHandle(hThread1);
   CloseHandle(hThread2);

   if (retCode == WAIT_OBJECT_0 && CriticalData == 1 )
      cout << "Success" << endl;
   else
      cout << "Failure" << endl;
}

Critical Data = 1 Success

See Also

Reference

C++ Keywords

const (C+)

const and volatile Pointers