volatile and MemoryBarrier()...
One thing I love about my job is that I learn something new all the time. Today I got a little bit smarter about volatile. One of the devs on the Indigo team was asking about the double check locking pattern. Today the Design Guidelines doc says:
public sealed class Singleton {
private Singleton() {}
private static volatile Singleton value;
private static object syncRoot = new Object();
public static Singleton Value {
get {
if (Singleton.value == null) {
lock (syncRoot) {
if (Singleton.value == null) {
Singleton.value = new Singleton();
}
}
}
return Singleton.value;
}
}
}
He wanted to know if “volatile” was really needed. Turns out the answer is “sorta”. Vance, a devlead on the CLR JIT team explained that the issue is around the CLR memory model… Essentially the memory model allows for non-volatile reads\writes to be reordered as long as that change can not be noticed from the point of view of a single thread. The issue is, of course, there is often more than one thread (like the finalizer thread, worker threads, threadpool threads, etc). volatile essentially prevents that optimization. As a side note. notice some other folks have a little problem in this space. A major mitigation here is that x86 chips don’t take advantage of this opportunity… but it will theoretically cause problems in IA64. As I was writing this I noticed that Vance has already done a very good write up a while ago…
That part I knew… what we news to me is there is a better way to do volatile, and that is with an explicitly memory barrier before accessing the data member.. We have a an API for that: System.Threading.Thread.MemoryBarrier(). This is more efficient than using volatile because a volatile field requires all accesses to be barriers and this effects some performance optimizations.
So, here is the “fixed” double check locking example..
public sealed class Singleton {
private Singleton() {}
private static Singleton value;
private static object syncRoot = new Object();
public static Singleton Value {
get {
if (Singleton.value == null) {
lock (syncRoot) {
if (Singleton.value == null) {
Singleton newVal = new Singleton();
// Insure all writes used to construct new value have been flushed.
System.Threading.Thread.MemoryBarrier();
Singleton.value = newVal; // publish the new value
}
}
}
return Singleton.value;
}
}
}
I have not completely internalized this yet, but my bet is it is still better to just make ” value” volatile to ensure code correctness at the cost of (possibly) minor perf costs.
Thoughts?
Update: Vance some new information about how this works in 2.0..
https://msdn.microsoft.com/msdnmag/issues/05/10/MemoryModels/default.aspx
Update 2: Even more great information from Joe
https://www.bluebytesoftware.com/blog/2007/06/09/ALazyInitializationPrimitiveForNET.aspx
Update 3: This gets even better with 3.5, again from Joe!
https://www.bluebytesoftware.com/blog/PermaLink,guid,a2787ef6-ade6-4818-846a-2b2fd8bb752b.aspx