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.
Nota
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.
End Microsoft Specific
One use of the volatile qualifier is to provide access to memory locations used by asynchronous processes such as interrupt handlers.
Nota
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