In C#, volatile
keyword ensures that reads and writes have acquire and release semantics, respectively. However, does it say anything about introduced reads or writes?
For instance:
volatile Thing something;
volatile int aNumber;
void Method()
{
// Are these lines...
var local = something;
if (local != null)
local.DoThings();
// ...guaranteed not to be transformed into these by compiler, jitter or processor?
if (something != null)
something.DoThings(); // <-- Second read!
// Are these lines...
if (aNumber == 0)
aNumber = 1;
// ...guaranteed not to be transformed into these by compiler, jitter or processor?
var temp = aNumber;
if (temp == 0)
temp = 1;
aNumber = temp; // <-- An out-of-thin-air write!
}
The volatile keyword is intended to prevent the compiler from applying any optimizations on objects that can change in ways that cannot be determined by the compiler. Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time.
Basically, volatile announces that a value might change behind your program's back. That prevents compilers from caching the value (in a CPU register) and from optimizing away accesses to that value when they seem unnecessary from the POV of your program.
The volatile qualifier is applied to a variable when we declare it. It is used to tell the compiler, that the value may change at any time. These are some properties of volatile. The volatile keyword cannot remove the memory assignment.
The field to read. The reference to T that was read. This reference is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. For more information, see the Volatile class. Returns a 64-bit unsigned value, loaded as an atomic operation.
The Thread.VolatileRead method forces the value in the field to be read from at the point of the call. In addition, any earlier program-order loads and stores must occur before the call to VolatileRead and any later program-order loads and stores must occur after the call.
Thus, the usage of volatile keyword as a portable synchronization mechanism is discouraged by many C/C++ groups. In this example, the code sets the value stored in foo to 0. It then starts to poll that value repeatedly until it changes to 255 :
For more information, see the Volatile class. Reads the value of the specified field. On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: If a read or write appears after this method in the code, the processor cannot move it before this method. The field to read.
Here's what the C# spec1 has to say about Execution Order:
Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field ...
The execution environment is free to change the order of execution of a C# program, subject to the following constraints:
...
The ordering of side effects is preserved with respect to volatile reads and writes ...
I would certainly consider introducing new side effects to be changing the order of side effects, but it's not explicitly stated like that here.
Link in answer is to the C# 6 spec which is listed as Draft. C# 5 spec isn't a draft but is not available on-line, only as a download. Identical wording, so far as I can see in this section.
This wording from the C# spec:
The ordering of side effects is preserved with respect to volatile reads and writes...
may be interpreted as implying that read and write introductions on volatile
variables are not allowed, but it is really ambiguous and it depends on the meaning of "ordering." If it is referring to relative ordering of existing accesses, then introducing new reads or writes does not change that and so it would not violate this part of the spec. If it is referring to the exact position of all memory accesses in program order, then introducing new accesses would violate the spec.
This article says that reads on non-volatile
variables might be introduced but does not say explicitly whether this is not allowed on volatile
variables.
This Q/A discusses how to prevent read introduction (but no discussion on write introduction).
In the comments under this article, two Microsoft employees (at the least at the time the comments were written) explicitly state that read and write introductions on volatile
variables are not allowed.
Stephen Toub
"read introduction" is one mechanism by which a memory reordering might be introduced.
Igor Ostrovsky
Elsewhere in the C# specification, a volatile read is defined to be a "side effect". As a result, repeating the read of m_paused would be equivalent to adding another side effect, which is not allowed.
I think we can conclude from these comments that introducing a side effect out-of-thin-air in C#, any kind of side effect, anywhere in the code is not allowed.
A related quote from the CLI standard states the following in Section I.12.6.7:
An optimizing compiler that converts CIL to native code shall not remove any volatile operation, nor shall it coalesce multiple volatile operations into a single operation.
As far as I know, the CLI does not explicitly talk about introducing new side effects.
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