views:

3728

answers:

7

I've been reading about thread-safe singleton patterns here:

http://en.wikipedia.org/wiki/Singleton_pattern#C.2B.2B_.28using_pthreads.29

And it says at the bottom that the only safe way is to use pthread_once - which isn't available on Windows.

Is that the only way of guaranteeing thread safe initialisation?

I've read this thread on SO:

http://stackoverflow.com/questions/6915/thread-safe-lazy-contruction-of-a-singleton-in-c

And seems to hint at an atomic OS level swap and compare function, which I assume on Windows is:

http://msdn.microsoft.com/en-us/library/ms683568.aspx

Can this do what I want?

Edit: I would like lazy initialisation and for there to only ever be one instance of the class.

Someone on another site mentioned using a global inside a namespace (and he described a singleton as an anti-pattern) - how can it be an "anti-pattern"?

Accepted Answer:
I've accepted Josh's answer as I'm using Visual Studio 2008 - NB: For future readers, if you aren't using this compiler (or 2005) - Don't use the accepted answer!!

Edit: The code works fine except the return statement - I get an error: error C2440: 'return' : cannot convert from 'volatile Singleton *' to 'Singleton *'. Should I modify the return value to be volatile Singleton *?

Edit: Apparently const_cast<> will remove the volatile qualifier. Thanks again to Josh.

A: 

There are many ways to do thread safe Singleton* initialization on windows. In fact some of them are even cross-platform. In the SO thread that you linked to, they were looking for a Singleton that is lazily constructed in C, which is a bit more specific, and can be a bit trickier to do right, given the intricacies of the memory model you are working under.

  • which you should never use
1800 INFORMATION
+9  A: 

A simple way to guarantee cross-platform thread safe initialization of a singleton is to perform it explicitly (via a call to a static member function on the singleton) in the main thread of your application before your application starts any other threads (or at least any other threads that will access the singleton).

Ensuring thread safe access to the singleton is then achieved in the usual way with mutexes/critical sections.

Lazy initialization can also be achieved using a similar mechanism. The usual problem encountered with this is that the mutex required to provide thread-safety is often initialized in the singleton itself which just pushes the thread-safety issue to initialization of the mutex/critical section. One way to overcome this issue is to create and initialize a mutex/critical section in the main thread of your application then pass it to the singleton via a call to a static member function. The heavyweight initialization of the singleton can then occur in a thread-safe manner using this pre-initialized mutex/critical section. For example:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};

However, there are good reasons to avoid the use of singletons altogether (and why they are sometimes referred to as an anti-pattern):

  • They are essentially glorified global variables
  • They can lead to high coupling between disparate parts of an application
  • They can make unit testing more complicated or impossible (due to the difficultly in swapping real singletons with fake implementations)

An alternative is to make use of a 'logical singleton' whereby you create and initialise a single instance of a class in the main thread and pass it to the objects which require it. This approach can become unwieldy where there are many objects which you want to create as singletons. In this case the disparate objects can be bundled into a single 'Context' object which is then passed around where necessary.

Matthew Murdoch
A: 

You can use an OS primitive such as mutex or critical section to ensure thread safe initialization however this will incur an overhead each time your singleton pointer is accessed (due to acquiring a lock). It's also non portable.

Henk
+1  A: 

There is one clarifying point you need to consider for this question. Do you require ...

  1. That one and only one instance of a class is ever actually created
  2. Many instances of a class can be created but there should only be one true definitive instance of the class

There are many samples on the web to implement these patterns in C++. Here's a Code Project Sample

JaredPar
The first one (just one class can be created)
Mark Ingram
That link doesn't provide a thread safe version.
Mark Ingram
A: 

The following explains how to do it in C#, but the exact same concept applies to any programming language that would support the singleton pattern

http://www.yoda.arachsys.com/csharp/singleton.html

What you need to decide is wheter you want lazy initialization or not. Lazy initialization means that the object contained inside the singleton is created on the first call to it ex :

MySingleton::getInstance()->doWork();

if that call isnt made until later on, there is a danger of a race condition between the threads as explained in the article. However, if you put

MySingleton::getInstance()->initSingleton();

at the very beginning of your code where you assume it would be thread safe, then you are no longer lazy initializing, you will require "some" more processing power when your application starts. However it will solve a lot of headaches about race conditions if you do so.

Eric
+2  A: 

If you are are using Visual C++ 2005/2008 you can use the double checked locking pattern, since "volatile variables behave as fences". This is the most efficient way to implement a lazy-initialized singleton.

From MSDN Magazine:

Singleton* GetSingleton()
{
    volatile static Singleton* pSingleton = 0;

    if (pSingleton == NULL)
    {
        EnterCriticalSection(&cs);

        if (pSingleton == NULL)
        {
            try
            {
                pSingleton = new Singleton();
            }
            catch (...)
            {
                // Something went wrong.
            }
        }

        LeaveCriticalSection(&cs);
    }

    return const_cast<Singleton*>(pSingleton);
}

Whenever you need access to the singleton, just call GetSingleton(). The first time it is called, the static pointer will be initialized. After it's initialized, the NULL check will prevent locking for just reading the pointer.

DO NOT use this on just any compiler, as it's not portable. The standard makes no guarantees on how this will work. Visual C++ 2005 explicitly adds to the semantics of volatile to make this possible.

You'll have to declare and initialize the CRITICAL SECTION elsewhere in code. But that initialization is cheap, so lazy initialization is usually not important.

Eclipse
This does not work: Read this article http://erdani.org/publications/DDJ_Jul_Aug_2004_revised.pdf
Martin York
This code is not exception safe: LeaveCriticalSection() will not be called if exceptions start flying.
Martin York
Why won't it be called?
Mark Ingram
Also, that article you provided was from 2004, whereas the article provided by Josh is from 2007.
Mark Ingram
The only exceptions that will fly will be caught by try / catch block, but you're right that wrapping Enter/Leave CriticalSection should be done by an RAII wrapper class.
Eclipse
Martin: Visual C++ 2005 changed the semantics of volatile specifically to address the issues brought up in the article you referenced. If you try this in 6.0, you eventually run into issues when your processor reorders the instructions, or you have a multi-core system.
Eclipse
Josh is correct. It's important to note that the use of 'volatile' here is Visual C++ specific and will not ensure thread-safety if the code is compiled with a different Windows compiler (Intel, gcc etc.) or ported to another OS.
Matthew Murdoch
Your code works fine except the return statement - I get an error:error C2440: 'return' : cannot convert from 'volatile Singleton *' to 'Singleton *'.Should I modify the return value to be volatile Singleton*?
Mark Ingram
You'd be safe with a const_cast<Singleton *>(pSingleton) since the volatile semantics are only needed inside the critical section.
Eclipse
const_cast<> removes volatile qualifier? Crazy stuff. Worked fine though. Thanks!
Mark Ingram
How is the CRITICAL_SECTION 'cs' being initialized?
Matthew Murdoch
Small nit: you assign 0, but test against NULL. Either use 0 in the test, or use !pSingleton
camh
How can I guarantee that CRITICAL_SECTION is initialized before GetSingleton is called?
caspin
A: 

If you are looking for a more portable, and easier solution, you could turn to boost.

boost::call_once can be used for thread safe initialization.

Its pretty simple to use, and will be part of the next C++0x standard.

michalmocny