As known, std::atomic
and volatile
are different things.
There are 2 main differences:
Two optimizations can be for std::atomic<int> a;
, but can't be for volatile int a;
:
a = 1; a = 2;
can be replaced by the compiler on a = 2;
a = 1; local = a;
can be replaced by the compiler ona = 1; local = 1;
Reordering of ordinary reads/writes across atomic/volatile operations:
volatile int a;
any volatile-read/write-operations can't be reordered. But nearby ordinary reads/writes can still be reordered around volatile reads/writes.std::atomic a;
reordering of nearby ordinary reads/writes restricted based on the memory barrier used for atomic operation a.load(std::memory_order_...);
I.e. volatile
don't introduce a memory fences, but std::atomic
can do it.
As is well described in the article:
For example, std::atomic
should be used for concurrent multi-thread programs (CPU-Core <-> CPU-Core), but volatile
should be used for access to Mamory Mapped Regions on devices (CPU-Core <-> Device).
But if required, both have unusual semantics and has any or all of the atomicity and/or ordering guarantees needed for lock-free coding, i.e. if required volatile std::atomic<>
, require for several reasons:
For example:
char cpu_ram_data_written_by_device[1024];
device_dma_will_write_here( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *device_ready = get_pointer_device_ready_flag();
//... somewhere much later
while(!device_ready); // spin-lock (here should be memory fence!!!)
for(auto &i : cpu_ram_data_written_by_device) std::cout << i;
example:
char cpu_ram_data_will_read_by_device[1024];
device_dma_will_read_it( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *data_ready = get_pointer_data_ready_flag();
//... somewhere much later
for(auto &i : cpu_ram_data_will_read_by_device) i = 10;
data_ready=true; //spilling cpu_ram_data_will_read_by_device to RAM, should be memory fence
For this, Herb Sutter said about volatile atomic<T>
, January 08, 2009: http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
Finally, to express a variable that both has unusual semantics and has any or all of the atomicity and/or ordering guarantees needed for lock-free coding, only the ISO C++0x draft Standard provides a direct way to spell it: volatile atomic.
But do modern standards C++11 (not C++0x draft), C++14, and C++17 guarantee that volatile atomic<T>
has both semantics (volatile + atomic)?
Does volatile atomic<T>
guarantee the most stringent guarantees from both volatile and atomic?
volatile
: Avoids fused-operations and constant-propagation as described in the beginning of the questionstd::atomic
: Introduces memory fences to provide ordering, spilling, and being atomic.And can we do reinterpret_cast
from volatile int *ptr;
to volatile std::atomic<int>*
?
In C and C++Operations on volatile variables are not atomic, nor do they establish a proper happens-before relationship for threading.
So no, don't use volatile , use std::memory_order_acquire and std::memory_order_release .
Yes, it does.
Section 29.6.5, "Requirements for operations on atomic types"
Many operations are volatile-qualified. The “volatile as device register” semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects.
I checked working drafts 2008 through 2016, and the same text is in all of them. Therefore it should apply C++11, C++14, and C++17.
And can we do reinterpret_cast from
volatile int *ptr;
tovolatile std::atomic<int>*
?
You can do such casts if and only if the ABI says that both types (here int
and std::atomic<int>
) have the same representation and restrictions: same size, alignement and possible bit patterns; same meaning for same bit patterns.
Everything that is volatile is directly connected with the ABI: variables that are volatile qualified must have the canonical ABI representation at sequence points and operations on volatile objects only assume they follow their ABI requirements and nothing else. So whenever volatile is used in C or C++, you can rely alternatively on the language standard or the platform ABI.
(I hope this answer is not deleted because some people despise volatile semantic and depending on the ABI and platform specific notions.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With