-
>> Yes it does. That's the whole point. If memory changes due to a device, volatile is what ensures that change is seen.
When I say "memory visibility", I'm not talking about compiler optimizations. Volatile only affects compiler behavior - it does not affect anything at the hardware level.
Memory barrier - Wikipedia, the free encyclopedia
gg
-
In modern CPUs, You don't always need memory barriers when sharing data between threads or processes. It depends on how the data is used. You do need the basic guarantee that a thread's view of memory does not revert from to an earlier state; so you don't un-see a state, but this is guaranteed by the same hardware that ensure that memory writes from the one thread don't revert. And you need proper cache invalidation rules. But as it happens, cache invalidation rules generally don't need special instructions.
Also, the fact that volatile prevents a particular optimization that the compiler is responsible for is besides the point. The language standard does not differentiate between the compiler and the hardware, they're both part of the implementation.
-
[Edit]TL;DR: a `volatile' variable simply doesn't pay for itself when it comes to concurrency issues.[/Edit]
O_o
You seem to be confused with regards to what Codeplug has said.
If you are using your concurrency primitives carefully and correctly you are protected against undesirable effects of concurrency by definition.
If you aren't using concurrency primitives without care or incorrectly `volatile' will almost never save you.
An example would be like what you might write for a partial (read only) barrier with multiple phases against `volatile' on "x86" architecture. (The example would see a `volatile' variable potentially cached, at least theoretically, during one phase and read again at a later phase where both phases use the same partial protection mechanism.)
The problem is, reality trumps the thought process involved in using `volatile' as a kind of "concurrency support". In my test, a lot of them, only three cases of such usage resulted in correct code. (I analyzed the assembler produced. I did not run the code. Yes, the examples were intentionally that simple.) To be clear here, there was not just three cases where it would have been useful for the assumption to work out. Furthermore, altering the code to use either more elaborate primitives, isolating the relevant bits behind atomic operations, or changing the implementation to a pattern with a single phase resulted in correct code for all cases regardless whether or not `volatile' was used.
Here then then the assumption that `volatile' will do anything more, as in over what the concurrency primitives already guarantee, to prevent caching is not carefully considered use of concurrency primitives.
The point here isn't even tangentially related to any standard. This is simply reality showing up to say that it is almost always wrong to reach for `volatile' for concurrent code.
Because it depends on flaky factors, like arguments to the compiler, `volatile' just isn't worth buying for "concurrency support".
With that in mind, the thought that `volatile' will "help" your compiler produce correct code in the face of things like caching only really serves to prevent perfectly valid optimization, such as those it might perform between acquiring and releasing a lock, without buying you anything because you may very well find it necessary to restructure or use more expensive primitives in any event.
Soma
-
tl;dr;
'volatile' tells the compiler what to do. it doesn't tell the processor.
modern processors can reorder memory reads and writes at the hardware level, regardless of the order of instructions in the assembly code.
http://en.wikipedia.org/wiki/Memory_...rative_example
-
Yes instruction reordering happens. It happens when volatile is not used, it happens when volatile is used, and it happens when relaxed atomic access is used.
My point is that a volatile read is functionally identical to a relaxed atomic load, and both are different from a normal variable read.
-
>> functionally identical to a relaxed atomic load
volatile doesn't provide atomicity - you're at the mercy of your compiler and hardware there.
gg
-
True, but atomicity for primitive types is not hard to come by.
-
When programming microcontrollers, "volatile" is very important.
A problem when writing interrupts is that some variables are only modified by an interrupt routine, and then read in the program (such as a flag).
The optimiser will look at the flag in the code and see that the (main) code doesn't change the flag from its initial value -> So the initial value is substituted and shortcuts are made
i.e.
Code:
int flag=0;
...
/* This section will be removed from the code */
if (flag == 1)
{
/*do something*/
}
When programming, you need to indicate to the compiler that the value of the variable may change elsewhere; so don't do any optimisations with that variable. This is done by using the "volatile" keyword.
This is such a common mistake when programming microcontrollers, that it appears at question 1 on the FAQ for the AVR Frequently Asked Questions
It is also explained here in a PIC FAQ Frequently Asked Questions (FAQ) about C on the Microchip PIC - This example also brings up the issue of delays. You would not have an accurate delay using this technique, but if you were just looking for the program to stop for a second or two...
-
That's demonstrative, because hardware interrupts are effectively other processes. A micro-controller is not going to provide atomic primitives. Nor is it going to provide any way to guarantee atomicity for large types, as C11 would require of all memory accesses if the two threads of execution were synchronized (which they can't be, because of a lack of atomics). So volatile is the way to go.