Click here to Skip to main content
Click here to Skip to main content

Singleton Design Pattern and Thread Safety

By , 26 Jul 2010
 

Introduction

Okay, you're not a newbie, you know all about singletons (only one object instantiation during program execution), but do you really know everything there is to know about Singletons and thread safety?

Background

Who is this article for?

  1. This article is for people who know about Singletons and want to learn about thread safety for Singletons.
  2. This article is mainly for discussing design and for C++,  though you are okay knowing any other OOPS language. Do note, managed languages have additional solutions.

This article is not for people who do not know:

  1. What thread safety means.
  2. Basic understanding of Singleton – you might still be okay if you know about static methods.
  3. Design gurus and experts; this article is targeted for intermediate developers and designers in the making.

The Singleton

Let’s write a quick pseudo code for Singletons:

class MySingleton
{
public:
      static MySingleton * GetInstance()
      //static method that returns only instance of MySingletone
      {
            if (m_pOnlyOneInstance == NULL) // if not yet instantiated
            {
                  m_pOnlyOneInstance = new MySingleton();
                  //create one and only object
            }

            return m_pOnlyOneInstance;
      }

private:

      static MySingleton * m_pOnlyOneInstance;
      //holds one and only object of MySingleton

      MySingleton(); // private constructor

public:
      // MySingleton functionalities
      void foo();
      bool goo();
      int zoo();
};

Thread Safety Issue for Singletons

Remember, thread-safety issue for Singletons would occur only rarely, as follows (rarely, but still catastrophic! so you still need to design for it):

  1. No client code has called GetInstance() so far, and now two threads simultaneously call GetInstance(), and
  2. Context switch between the two calling threads happen on the exact line of code at:
  3. if (m_pOnlyOneInstance == NULL)

During further calls to GetInstance(), the MySingleton object is already created and would be returned. But it's still a serious issue, as we've instantiated MySingleton twice.

Have you made your Singleton Thread-Safe?

Note: Think about how you'll make your Singleton thread-safe, come up with solutions before proceeding further.

Solution 1

Easy, put a critical section to my Singleton method to make it thread-safe. Duh, is it gonna be that kinda article?

MySingleton * GetInstance() //usage of critcal section makes it thread safe
{
      EnterCriticalSection();
      //other client threads, if any, now have to wait till current
      // thread leaves critical section. Forget semantics and
      // compilation for Crit Section, and treat this as pseudo-code
      if (m_pOnlyOneInstance == NULL)
      {
          m_pOnlyOneInstance = new MySingleton();
      }

      LeaveCriticalSection();

      return m_pOnlyOneInstance;
}
Deep Dive into Solution 1

Yup, this solution works, but think about it: critical section is a costly operation, and you're using it each and every time a client accesses GetInstance().

You've devised a solution that works and handles the rare but serious thread safety issue for singletons, but at the cost of doing an expensive critical section operation for all GetInstance() calls, slowing down client access every time!!

This is clearly unacceptable. Isn't there a better solution?

Solution 2

Okay hot shot, if critical section is expensive, let's give it the boot.

Let's call MySingleton::GetInstance() during program start-up, like in main() in C++, or CWinApp:InitInstance() in MFC, and so on an so forth.

I know there is only one thread executing during program start-up, so thread-safety issue does not even arise.

Design Principle: This kind of instantiation is called Eager Instantiation. That means, creating objects up-front, even before they are required or might be used.

Yup, this works. No critical section involved, so no costly operation for the general use-case when clients call GetInstance() every time.

Wait a minute, trying to pull a fast one, eh? I know about the basic OOAD Design Principle of Lazy\Late Instantiation, which means create an object only when required, not upfront. Aren't we breaking this design principle?

Heck, yes, we are! Let's plunge into this. But let's quickly define Late Instantiation before that:

Design Principle: Late Instantiation means creating an object when it is required to be used, not up-front.

We’ve covered two design principles of Early and Late Instantiation already!

Deep Dive into Solution 2 (keep your beach towels ready!)
  1. What if no client calls MyInstance() during program execution? Maybe the client ran a use-case this time that did not need MySingleton's usage. You've created an unnecessary object that's floating around during the entire program life-cycle doing nothing.
  2. While Early or Lazy Instantiation might not sound like a big deal, what if MySingleton is a memory-hogging class? What if MySingleton represents data stored on a file, or detailed info about a server? You're occupying lot of precious memory that might never potentially be used!
  3. Eager Instantiation is not all bad. If your Singleton is a basic class that is heavily used all across your program, then by all means, go for Eager Instantiation.

Lazy Instantiation is a principle, not a rule, and not necessarily always the default choice. Be either Eager or Lazy, depending on your design and domain needs! There is no good or bad, you have to choose what's best based on your program needs. Solution 2 (Eager Instantiation) is a pretty good, easy, clean solution for many projects, and is widely used.

But isn’t there a solution to address these short-comings of Solution 2? Yup, there is! Enter Solution 3.

Solution 3

We can achieve good performance and lazy instantiation for Singletons (which were the short-comings of Solutions 1 and 2 for the short-term memory loss readers out there).

You can achieve this by moving around code in Solution 1.

Do go back to Solution 1 and think about how this can be done before proceeding further.

Deep Dive into Solution 3
MySingleton * GetInstance()
{
      if (m_pOnlyOneInstance == NULL)
      {
            EnterCriticalSection();
            if (m_pOnlyOneInstance == NULL)
            // Solution 1 and 2 gaps addressed by moving
            // critical section block and by re-doing this check!
            {
                  m_pOnlyOneInstance = new MySingleton();
            }
            LeaveCriticalSection();
      }
      return m_pOnlyOneInstance;
}

With Solution 3, you do not use a critical section every time a client calls GetInstance(), and we achieve Lazy Instantiation. The MySingleton object is created only when the client calls GetInstance().

Also, a Critical Section is used only during instantiation, and for handling the rare (but catastrophic!) thread-safety issue during instantiation and the race condition between two threads. We do not enter a critical section block every time the client calls GetInstance().

Congratulations! You’ve just learnt Double-Checked Locking, the formal name for Solution 3.

Summary

We’ve covered quite a bit of ground there with the simplest of Design Patterns: the Singleton. We’ve applied a mini-OO Analysis and Design on a small scale for our friend MySingleton. And, as a bonus, we learnt about Eager and Lazy Instantiation!

Remember, you can choose either Solution 2 or Solution 3 for your Singletons, based on your project need.

There is another pattern called Monostate with which you could achieve Singleton-like behavior, but it won’t please the purists out there.

The Monostate pattern is simply making all methods and data of your class static.

class MonoState
{

public:
      // MonoState functionalities
      static void foo();
      static bool goo();
      static int zoo();

      MonoState() {} // constructor NEITHER private NOR static

private:

      static int MyData;
};

Clients of MonoState access it by creating an object and calling the static method. Since all methods and variables are static, data is shared across objects and we get a Singleton like behavior.

MonoState newObject;
newObject.goo();

MonoState msObject;
msObject.zoo();

But note that what we get from MonoState is Singleton like behavior, because MonoState creates multiple objects but that which share the same data. Anyway, MonoState is theoretical, and for the sake of rounding up a complete article, you should always be going for the Singleton pattern, rather than MonoState.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ananth.tm
India India
Member
10 years of extensive experience in full life-cycle of S\W development inlcuding Requirements, Design, Prototyping, Implementation, Verification, Release, Sustenance.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalleaks memorymemberemilio_grv28 Jul '10 - 19:50 
Who and when (and how) will it be deleted, since it has been allocated on heap ?

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: leaks memorymemberAescleal30 Jul '10 - 0:58 
It's not just memory (which is fairly trivial anyway, as it's a singleton so it gets cleared up at the end of the process anyway) it's any other resource the singleton is using that leaks.
 
Cheers,
 
Ash
GeneralNot exception safe either...memberAescleal27 Jul '10 - 6:17 
Just noticed that the DCL version of GetInst() is not exception safe either.
 
What happens if the memory allocation fails or the constructor throws? The lock's been grabbed and never released.
GeneralRe: Not exception safe either...memberGalatei27 Jul '10 - 8:11 
Who said you should take all the code here to the letter? Add exception handling by yourself, and take the article as a guide.
GeneralRe: Not exception safe either...memberAescleal27 Jul '10 - 9:55 
I didn't say it didn't handle exceptions, I said it's not exception safe (to which I'll add it's not exception neutral either). To paraphrase your post to me below: "You sound like people in 1994 who were surprised by Tom Cargills stack challenge. It's because of people like you that games like Microsoft Combat Flight Simulator had appalling exception behaviour which led to Microsoft banning their contractors using C++ exception handling in games."
 
You see when someone tells me that they're going to tell me everything I need to know about DCL and how to write singletons I'd expect him (or her) to be able to write exception safe code. Now your response would imply that (if DCL worked) we'd write something like:
 
T *T::instance()
try
{
    if( !ptr_ )
    {
        lock_it();
        if( !ptr_ )
        {
            ptr_ = new T();
        }
        unlock_it();
     }
}
catch( ... )
{
    unlock_it();
    throw();
}
 
Which is a bit wordy. Had the author bothered copying Alexandrescu and Myers [1] he'd have seen a "better" (at least as far as exception safety goes, it still sucks as far as thread safety) implementation:
 
T *instance()
{
    if( !ptr_ )
    {
        lock l;
        if( !ptr_ )
        {
            ptr_ = new T();
        }
    }
}
 
Which is significantly smaller, does the same thing and is exception safe and neutral. What's more there's no explicit exception handling in sight. Admittedly it probably sucks if you're paid by the line...
 
So why did I bring it up? Well the main reason is there's a lot of misinformation out there about the best way to write exception safe code. When someone sticks their neck on the line as says "this is all you need to know..." they open themselves up to a lot more scrutiny.
 
[1] He'd have done far better by reading Alexandrescu and Myers articles in DDJ or the paper freely available on the web (Google their names and "Double checked locking") or even the earlier work done on Java (the original "Double Checked Locking is Broken" by Joshua Bloch et. al.) and understanding why DCL doesn't work in C++.
GeneralMy vote of 4memberMember 300849727 Jul '10 - 3:47 
This is very simple and excellent
GeneralRe: My vote of 4memberAescleal27 Jul '10 - 6:13 
For some definition of simple and excellent.
GeneralMy vote of 1memberAescleal27 Jul '10 - 1:16 
Double checked locking is completely and utterly broken.
 
And that's before starting on how broken using Singleton as a design pattern is.
GeneralRe: My vote of 1membersupercat927 Jul '10 - 5:24 
Aescleal wrote:
Double checked locking is completely and utterly broken.

 
Any discussion of double-checked locking (especially one calling itself "Everything there is to know") should discuss the issues of double-checked locking. Compilers which can resequence instructions out of an inlined constructor will yield code which may fail; I'm not clear why the solution is to always have everyone who uses the singleton require a lock, though, rather than simply adding whatever memory barriers are required to ensure that the singleton is in fact initialized prior to use.
 
As for the critical section, using a global one seems icky. What would you think of something like the following:
If main_singleton_variable is initialized
  Exit with main_singleton_variable.
If singleton_lock is initialized
  Wait on singleton_lock.
  Exit with main_singleton_variable.
Create a lock, acquire it, and CompareExchange it to singleton_lock.
If I lost the CompareExchange
  Release and dispose my lock
  Wait on the singleton_lock.
  Exit with main_singleton_variable.
Otherwise (I won the CompareExchange)
  Prepare new singleton
  Ensure it really is prepared (memory barrier, etc.)
  Set main_singleton_variable to the new singleton
  Release my lock
If the system can eager-initialize the lock, that will simplify the code somewhat (at least in .Net, a "lock" is the cheapest form of object there is; not sure about in unmanaged code; certainly a lock shouldn't be expensive).
GeneralRe: My vote of 1memberAescleal27 Jul '10 - 6:09 
"simply adding whatever memory barriers are required..." made me smile somewhat.
 
There's an article somewhere on the internet by Alexandrescu and Myers which discusses using memory fences/barriers as a way to optimise singleton usage. I can't comment on how it compares to your solution though. However seeing all your pseudo code for something as crufty as a singleton makes me wonder why bother? It's like optimising a swap operation to make a bubble sort quicker - we're looking at the wrong thing. Singletons (to me) aren't that valuable a pattern to worry about optimising that heavily and non-portably.
 
Cheers,
 
Ash

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 26 Jul 2010
Article Copyright 2010 by Ananth.tm
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid