A Reproducible Example of Volatile Usage

A reproducible example of volatile usage

The exact semantics of volatile is a jitter implementation detail. The compiler emits the Opcodes.Volatile IL instruction where ever you access a variable that's declared volatile. It does some checking to verify that the variable type is legal, you can't declare value types larger than 4 bytes volatile but that's where the buck stops.

The C# language specification defines the behavior of volatile, quoted here by Eric Lippert. The 'release' and 'acquire' semantics is something that only makes sense on a processor core with a weak memory model. Those kind of processors have not done well in the market, probably because they are such an enormous pain to program. The odds that your code will ever run on a Titanium are slim to none.

What's especially bad about the C# language specification definition is that it doesn't mention at all what really happens. Declaring a variable volatile prevents the jitter optimizer from optimizing the code to store the variable in a cpu register. Which is why the code that Marc linked is hanging. This will only happen with the current x86 jitter, another strong hint that volatile is really a jitter implementation detail.

The poor semantics of volatile has a rich history, it comes from the C language. Whose code generators have lots of trouble getting it right as well. Here's a interesting report about it (pdf). It dates from 2008, a good 30+ years of opportunity to get it right. Or wrong, this goes belly-up when the code optimizer is forgetting about a variable being volatile. Unoptimized code never has a problem with it. Notable is that the jitter in the 'open source' version of .NET (SSLI20) completely ignores the IL instruction. It can also be argued that the current behavior of the x86 jitter is a bug. I think it is, it is not easy to bump it into the failure mode. But nobody can argue that it actually is a bug.

The writing is on the wall, only ever declare a variable volatile if it is stored in a memory mapped register. The original intention of the keyword. The odds that you'll run into such a usage in the C# language should be vanishingly small, code like that belongs in a device driver. And above all, never assume that it is useful in a multi-threading scenario.

Illustrating usage of the volatile keyword in C#

I've achieved a working example!

The main idea received from wiki, but with some changes for C#. The wiki article demonstrates this for static field of C++, it is looks like C# always carefully compile requests to static fields... and i make example with non static one:

If you run this example in Release mode and without debugger (i.e. using Ctrl+F5) then the line while (test.foo != 255) will be optimized to 'while(true)' and this program never returns.
But after adding volatile keyword, you always get 'OK'.

class Test
{
/*volatile*/ int foo;

static void Main()
{
var test = new Test();

new Thread(delegate() { Thread.Sleep(500); test.foo = 255; }).Start();

while (test.foo != 255) ;
Console.WriteLine("OK");
}
}

What is the purpose of 'volatile' keyword in C#

I refer you to section 10.5.3 of the specification, which states:

For non-volatile fields, optimization
techniques that reorder instructions
can lead to unexpected and
unpredictable results in
multi-threaded programs that access
fields without synchronization such as
that provided by the lock-statement
(§8.12). These optimizations can be
performed by the compiler, by the
run-time system, or by hardware. For
volatile fields, such reordering
optimizations are restricted:

A
read of a volatile field is called a
volatile read. A volatile read has
“acquire semantics”; that is, it is
guaranteed to occur prior to any
references to memory that occur after
it in the instruction sequence.

A
write of a volatile field is called a
volatile write. A volatile write has
“release semantics”; that is, it is
guaranteed to happen after any memory
references prior to the write
instruction in the instruction
sequence.

These restrictions ensure
that all threads will observe volatile
writes performed by any other thread
in the order in which they were
performed. A conforming implementation
is not required to provide a single
total ordering of volatile writes as
seen from all threads of execution.

Read that extremely carefully if you have any intention of ever making a volatile field. If you do not completely and thoroughly understand all the implications of volatile semantics then do not attempt to use them. It is usually far better to use a lock, which automatically gives you sufficient memory barriers to ensure the necessary acquire and release semantics. Remember, locks are only really expensive when they are contended.

Volatile fields: How can I actually get the latest written value to a field?

Your understanding is correct, and it is true that you cannot ensure that the program will always print 1 using these techniques. To ensure your program will print 1, assuming thread 2 runs after thread one, you need two fences on each thread.

The easiest way to achieve that is using the lock keyword:

private int sharedState = 0;
private readonly object locker = new object();

private void FirstThread()
{
lock (locker)
{
sharedState = 1;
}
}

private void SecondThread()
{
int sharedStateSnapshot;
lock (locker)
{
sharedStateSnapshot = sharedState;
}
Console.WriteLine(sharedStateSnapshot);
}

I'd like to quote Eric Lippert:

Frankly, I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy: you're attempting to read and write the same value on two different threads without putting a lock in place.

The same applies to calling Volatile.Read and Volatile.Write. In fact, they are even worse than volatile fields, since they require you to do manually what the volatile modifier does automatically.

How fields of reference type can be nonvolatile?

This state implies that fields of reference type are not volatile by
default.

Sure. No field is treated as volatile by default because volatile comes together with the possibility of a sizable performance cost.

I think it's ok to treat reference-type field as a field of value type
containing the address of the object. Then it becomes similar to int
type.

Assume this can be done without problems. So what? Fields of scalar types such as int or bool are also not treated as volatile by default.

Unlike usual value types GC moves objects into memory when it compacts
the heap and changes the references accordingly. Hence 'the most
up-to-date value' must always be there. And if so how does the concept
of volatility apply to reference types?

You are slightly confused about the utility of volatile. The problem it is meant to address is not only that (A) the most up to date value is not there (although volatile semantics do guarantee that any writes to the value will be observable by any reads that follow them in an abstract timeline¹).

Apart from that, it is also meant to address the situation (B) where the compiler assumes that the code it generates is the only party modifying that value (or the value is not being modified at all), which means that it will not choose to read the value from the field and instead use a "cached copy" it already has at hand. If the value is being modified by a third party at the same time this is obviously going to result in the program using the wrong data for its calculations.

For more information you can refer to this excellent analysis by Igor Ostrovsky, wherein (A) is referred to as "processor optimizations" and (B) as "compiler optimizations".


¹ Please note that this is not a rigorous definition but rather just a crude approximation.

Do I need this field to be volatile?

Yes, that's a hard requirement. The just-in-time compiler is allowed to store the value of m_cur in a processor register without refreshing it from memory. The x86 jitter in fact does, the x64 jitter doesn't (at least the last time I looked at it).

The volatile keyword is required to suppress this optimization.

Volatile means something entirely different on Itanium cores, a processor with a weak memory model. Unfortunately that's what made it into the MSDN library and C# Language Specification. What it is going to to mean on an ARM core remains to be seen.



Related Topics



Leave a reply



Submit