Reputation: 5239
I understand what volatile does and what it doesn't do, taking the example from this question
void waitForSemaphore()
{
volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}
my question is: in the presence of cpu cache, volatile can not guarantee the above works, because it only forces cpu to read smePer from memory but cpu doesn't know if the memory is in RAM or one of the caches. Therefore if another device changed content of WELL_KNOWN_SEM_ADDR, waitForSemaphore won't necessarily know. So there must be something else make it work.
I have read this and this, it seems volatile itself is not enough to guarantee such program works, there must be some platform dependent magic that either by passes L1/2/3 cache or force flush them, am I right? If so are such support available on all popular platforms, for example x86?
Upvotes: 0
Views: 272
Reputation: 12262
volatile
forbids the compiler to optimize-out accesses to such variables or re-order such accesses with regard to all volatile variables (only!). Actual semantics might be implementation defined (but have to be specified by the compiler).
For the hardware: yes, caches and bus-buffers (e.g. "write buffers") may still reorder, not to speak of the CPU(s). volatile
does not imply fences/barriers. So the underlying hardware has to tag such memory areas as "(strongly) ordered" at least. If external hardware is involved, the area must also be tagged "uncached", so every access will directly go to the hardware. That might also apply for other CPUs unless there is some kind of "snooping" hardware in the system (each CPU gets notified on cache-changes in another CPU).
C11 provides stdatomic.h
(optional). That would be better suited for thread/interrupt synchronization. If multiple CPUs, volatile
is close to useless for that anyway. However, it still has applications for hardware (possible requiring additional mutex or implicit exclusive access to a peripheral device).
Upvotes: 4
Reputation: 1638
No, volatile as such doesn't need hardware support to work. See answer by gnaster729 for details.
Cache synchronization issues are related to so-called "Memory model" (https://en.wikipedia.org/wiki/Memory_ordering ); if describing it very short - there is no such thing as "if another device has changed variable" (it is not observable from reading side), there is only a thing "if another device changed variable a earlier than variable b" - this IS observable. To deal with it, so-called "memory fences" a.k.a. "memory barriers" are used (https://en.wikipedia.org/wiki/Memory_barrier and https://www.kernel.org/doc/Documentation/memory-barriers.txt ), which, if I'm not mistaken, have special support in C++11. On x86, memory fences are implicit on CAS instructions; x86 is widely believed to comply with so-called TSO (total store ordering) memory model, which is quite strict and one of the easiest to deal with (though, as described in kernel.org ref above, it is perfectly feasible to deal with correctness under any memory model).
Upvotes: 0
Reputation: 52530
volatile on its own only tells the compiler "assume that when you store to a volatile object, someone might take notice, so you can't optimise the store away" and "assume that when you read a volatile object, the result might not be the same as what you stored, and also someone might take notice that you read the volatile object, so you can't optimise the read away, and you must use the value read and not what you think should be stored there".
For example, if you write
int x = 0 * (*p);
the compiler can't say "I'll set x to 0 whatever *p is, so I don' need to bother reading *p". It's wrong if *p is volatile.
In C11 there are new features to help you, or you might want to use proper OS functions.
Upvotes: 1