Subterranean IL: Volatile

This time, we’ll be having a look at the volatile. prefix instruction, and one of the differences between volatile in IL and C#.

The volatile. prefix

volatile is a tricky one, as there’s varying levels of documentation on it. From what I can see, it has two effects:

  1. It prevents caching of the load or store value; rather than reading or writing to a cached version of the memory location (say, the processor register or cache), it forces the value to be loaded or stored at the ‘actual’ memory location, so it is then immediately visible to other threads.
  2. It forces a memory barrier at the prefixed instruction. This ensures instructions don’t get re-ordered around the volatile instruction. This is slightly more complicated than it first seems, and only seems to matter on certain architectures. For more details, Joe Duffy has a blog post going into the details.

For this post, I’ll be concentrating on the first aspect of volatile.

Caching field accesses

To demonstrate this, I created a simple multithreaded IL program. It boils down to the following code:

If you compile and run this code, you’ll find that the call to Thread.Join() never returns – the DoWork spinlock is reading a cached version of Holder.stop, which is never being updated with the new value set by the Main method. Adding volatile to the ldfld fixes this:

The volatile ldfld forces the field access to read direct from heap memory, which is then updated by the main thread, rather than using a cached copy.

volatile in C#

This highlights one of the differences between IL and C#. In IL, volatile only applies to the prefixed instruction, whereas in C#, volatile is specified on a field to indicate that all accesses to that field should be volatile (interestingly, there’s no mention of the ‘no caching’ aspect of volatile in the C# spec; it only focuses on the memory barrier aspect). Furthermore, this information needs to be stored within the assembly somehow, as such a field might be accessed directly from outside the assembly, but there’s no concept of a ‘volatile field’ in IL! How this information is stored with the field will be the subject of my next post.