eyt*
May 24, 2004

Singletons and The Double-Checked Locking Pattern...

The Singleton design pattern is probably one of the more popular design patterns, and has been the focal point for a lot of literature, such as To Kill A Singleton which discusses some issues with the lifetime of a Singleton object, or Modern C++ Design offers a good discussion of the issues surrounding Singletons.

One of the issues that some developers are aware of is the issue with the instance acquisition in a multithreaded environment. Using C++ syntax, if you develop your getInstance() method as follows:

MySingleton * MySingleton::getInstance()
{
  static MySingleton * instance = 0;
  if ( instance == 0 ) {
    instance = new MySingleton();
  }
  return instance;
}

With the above situation, if two threads concurrently enter the above, you can actually get two instances in those threads.

The solution to this is to use a lock around the entire function, The acquisition of a lock is generally deemed expensive, especially since it is only required around the instantiation of the object. Placing the lock around the instantiation of the object, however, does not resolve the initial problem of having two instances. Pattern Oriented Software Architecture's Double-Check Locking Pattern purported to address this. The design pattern prescribes that when the instance is null, acquire the lock then, and then recheck if the instance is null. This transitions the above code to:

MySingleton * MySingleton::getInstance()
{
  static MySingleton * instance = 0;
  if ( instance == 0 ) {
    LockGuard lock( instanceLock );
    if ( instance == 0 ) {
      instance = new MySingleton();
    }
  }
  return instance;
}

It is a simple solution that appears to do the job.

But it does not work. The "Double-Checked Locking is Broken" Declaration clearly prescribes that this solution does not work in the presence of optimizing compilers or shared memory multiprocessors, where the code can be reordered in a fashion that circumvents the pattern. The article illustrates the problem using Java, but the problem also occurs in C++ and potentially other languages.

The declaration is worth a read as it looks at the problem from different angles. The solutions that seem to work best is a static variable initialization (static MySingleton instance; in C++) or, well, maybe the function-scope lock is not so expensive after all.

Filed In

Navigation

eyt*