OdeToCode IC Logo

Double Check Locking In The News Again

Thursday, May 13, 2004

Once upon a time, Chris Brumme posted about shortcomings in the memory model of the ECMA specification for the CLR. Not necessarily shortcomings from a runtime performance point of view, but shortcomings from a programmer productivity point of view. In the post he discussed why double check locking requires some attention to detail. Specifically, the following code snippet may not be as thread safe as it first appears.

if (a == null)
{
  lock(obj)
  {
    if (a == null) a = new A();
  }
}

There are interesting comments in response to the post, and eventually Jon Skeet devoted a page to singleton construction. Jon avoids the double check locking issue altogether by using a static field initializer in a nested type. The approach Jon promotes works very well except in cases where you do not know the singleton type to construct at compile time. For example, the type of singleton to construct may be an object derived from an abstract base class in a provider / pluggable architecture and the application reads the type to construct from a config file.

If you can’t use a static field initializer, but still want safe, lazy instantiation, then it seems to me that Brad Adam’s post about using the static MemoryBarrier method of the System.Threading.Thread class is the direction to go, for a couple reasons.

To me, the volatile keyword carries specific overtones. I still think of programming with memory mapped IO when I see the volatile keyword. Volatile variables are completely unsafe for caching. Imagine having a byte in memory hooked up to a thermometer laying on your desk. Not even a single CPU machine knows when the memory location may update with a new temperature value – you have to read it from main memory every time. Volatile has an unfortunate connotation for a singleton reference, which after construction isn’t going to change.

Secondly, the use of Thread.MemoryBarrier explicitly calls out what needs to happen for the code to be thread safe. For people who stumble across the code in the future, they will not need to think of the side effects of a volatile variable when Thread.MemoryBarrier is in place.

Not only do we have maintainable code showing programmer intent, there is a performance bonus too. That being said, if this code was not part of a singleton, and other methods were involved, I'd prefer volatile.