Click here to Skip to main content
15,868,164 members
Articles / Programming Languages / C++

Singleton Pattern: A Review and Analysis of Existing C++ Implementations

Rate me:
Please Sign up or sign in to vote.
4.85/5 (42 votes)
20 Aug 200318 min read 239.3K   1.5K   107   37
Review existing Singleton Pattern C++ implemenations, and how to come up with an extensible design that has the best of all worlds

Introduction

As you design your application framework, you may come across certain framework objects that should enforce only a single instance of itself throughout the application lifetime. These objects can range from a file system, a graphic system, to even a simple application logging utility. The design of the framework object is thus known as applying the Singleton Pattern, and is described in [GOF] as:

Ensure a class only has one instance, and provide a global point of access to it.

The definition of a singleton is simple, yet has resulted in a wide variety of implementations. Each of this implementation varies from the simplest of issues like storage of instance, to complex multi-threading.

In this article, I will describe the few singleton implementations encountered, and put together a singleton object that resolves all likely issues.

Implications of Using The Singleton Pattern

Before we begin, it is best to understand the implications of using The Singleton Pattern; that is, the restrictions and rules we have to follow, as well as the benefits. Describe in [GOF], an object implementing The Singleton Pattern must:

  • Ensure only one instance of a class, and it must be accessible to clients from a well-known access point. Note that however, the limitations did not imply if it is to be a language imposed limitation, or simply a programmer self-imposed limitation.
  • Allow clients to use an extended instance of the class without modifying their code. That is, the sole instance should be extensible by subclassing.

Additional, a Singleton should own itself. The client is not required to perform additional steps to create the Singleton. From a client side of view, he is, and always is, a client, and takes no part in creation or destruction of the Singleton object.

With the Singleton Pattern, the object can:

  • Enforce controlled access to the sole instance.
  • Avoid polluting the global namespace with global variables that store sole instances.
  • Easily configure the current object to be used in the application, as long as it is extended from the base object.

Static Data and Functions, a First Encounter of a Self-Proclaimed Singleton

Our first review of a ‘Singleton’ implementation is as follows:

C++
class PrintJob {...};

class PrintQueue {
public : 
    static void AddJob(PrintJob&);

private : 
    static std::queue<printjob> queue_;

private : 
    inline explicit PrintQueue () {}
    inline ~ PrintQueue () {}
    inline explicit PrintQueue (PrintQueue const&) {}
    inline PrintQueue& operator=( PrintQueue const&) { return *this; }
};    //    end of class PrintQueue
</printjob>

At first glance, it fulfills the basic requirement of The Singleton Pattern, that is, only a single instance of PrintQueue can exist. Additionally, it performs the automatic destruction of itself, since static data are destroyed upon program termination. However, it falls short on the second requirement, the inability to subclass the application, and yet provides the client the new extended version without making changes to the code.

Another area the implementation falls short of is the inability to control the initialization and destruction of the singleton. If, during the application lifespan, that PrintQueue was never used, you have paid for the cost of initializing all of PrintQueue’s static data without using it at all. We should always try to obey the same rule that C++ has always obeyed:

You shouldn’t pay for what you don’t use.

Another reason why we would want to control the destruction of the singleton would be, for example, in the case of a Singleton Logging Utility. Imagine that we have one other static object, that upon destruction when program exits, is required to log certain debugging information. However, the static data required to perform logging, at that point of time, might have already been destroyed. This could crash the application immediately.

So now, let’s move on to a non-static data/function approach and have a look at the another Singleton Pattern implementation, this time proposed by Scott Meyer [Meyers 1996b].

Meyer’s Singleton

C++
class PrintQueue {
public : 
    void AddJob(PrintJob&);

    inline static PrintQueue& Instance() {
        static PrintQueue instance;
        return instance;
    }

private : 
    std::queue<printjob> queue_;

private : 
    inline explicit PrintQueue() {}
    inline ~PrintQueue() {}
    inline explicit PrintQueue(PrintQueue const&) {}
    inline PrintQueue& operator=(PrintQueue const&) { return *this; }
};    //    end of class PrintQueue

As before, this approach has only managed to fulfill the basic requirement, a single instance, as well as automatic Singleton destruction. However, it does manage to solve the problem of not paying for what we don’t use. Notice that with this implementation, the only instance of PrintQueue is a static instance in Instance(). Meyers explained [Meyers 1996b],

…First, it's important that the single Printer object be static in a function and not in a class. An object that's static in a class is, for all intents and purposes, always constructed (and destructed), even if it's never used. In contrast, an object that's static in a function is created the first time through the function, so if the function is never called, the object is never created. (You do, however, pay for a check each time the function is called to see whether the object needs to be created.)…… We know exactly when a function static is initialized: the first time through the function at the point where the static is defined. The situation with a class static (or, for that matter, a global static, should you be so gauche as to use one) is less well defined. C++ offers certain guarantees regarding the order of initialization of statics within a particular translation unit (i.e., a body of source code that yields a single object file), but it says nothing about the initialization order of static objects in different translation units…

Allowing Subclass, a Quick Hack

A quick hack can be applied to the class above actually, so that we can fulfill the second requirement, subclass of the Singleton and yet not required to change any client code.

C++
class PrintQueue {
public : 
    void AddJob(PrintJob&);

    static PrintQueue& Instance();

protected : 
    inline explicit PrintQueue() {}
    inline ~PrintQueue() {}

private : 
    std::queue<printjob> queue_;

private : 
    inline explicit PrintQueue(PrintQueue const&) {}
    inline PrintQueue& operator=(PrintQueue const&) { return *this; }
};    //    end of class PrintQueue 

class DerivedPrintQueue : public PrintQueue {...};

PrintQueue& PrintQueue::Instance() {
    static std::auto_ptr<DerivedPrintQueue> instance(new DerivedPrintQueue);
    return *instance;
}

By having a pointer to an object that has subclassed PrintQueue, we have managed to allow clients to use an extended version of PrintQueue, yet shield them from the knowledge.

Notice the usage of std::auto_ptr. Since instance is now a pointer, there must be a mean to destroy the object. std::auto_ptr helps us accomplish this, since upon the destruction of std::auto_ptr, the destructor calls delete on the held object pointer if valid. And since instance is a static data, the time of destruction is during program termination.

Although this version of fulfills the basic requirement of The Singleton Pattern, this is far from being complete. This being, the destruction order is still undefined.

Removing std::auto_ptr, using atexit

The C Standard library provides a function atexit, which MSDN describes:

The atexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to atexit create a register of functions that are executed in last-in, first-out (LIFO) order. The functions passed to atexit cannot take parameters. atexit and _onexit use the heap to hold the register of functions. Thus, the number of functions that can be registered is limited only by heap memory.

Thus, using atexit, we can actually do away with std::auto_ptr totally. The above codes can be rewritten as follows:

C++
class PrintQueue {
public : 
    void AddJob(PrintJob&);

    static PrintQueue& Instance();

protected : 
    inline explicit PrintQueue() {}
    inline ~PrintQueue() {}

private : 
    static void Destroy();

private : 
    std::queue<printjob> queue_;

    static PrintQueue* instance_;

private : 
    inline explicit PrintQueue(PrintQueue const&) {}
    inline PrintQueue& operator=(PrintQueue const&) { return *this; }
};    //    end of class PrintQueue 

PrintQueue* PrintQueue::instance_ = 0;

PrintQueue& PrintQueue::Instance() {
    if ( instance_ == 0 ) {
        instance_ = new DerivedPrintQueue();
        std::atexit(Destroy);
    }
    return *instance_;
}

Note that this doesn’t make a difference now, since a static std::auto_ptr would generate similar codes. However, using atexit actually paths the way to a better Singleton implementation later.

Singleton with a Defined Lifespan

In an attempt to control destruction order, another implementation has been introduced.

C++
class PrintQueue {
public : 
    void AddJob(PrintJob&);

    inline static PrintQueue& Instance() { 
        assert(instance_); 
        return *instance_;
    }

protected : 
    inline explicit PrintQueue() {
        assert(instance_ == 0); 
        instance_ = this;
    }
    inline ~PrintQueue() {
        instance_ = 0;
    }

private : 
    static PrintQueue* instance_;

private : 
    inline explicit PrintQueue(PrintQueue const&) {}
    inline PrintQueue& operator=(PrintQueue const&) { return *this; }
};    //    end of class PrintQueue

class DerivedPrintQueue : public PrintQueue {...};

int main() {
    DerivedPrintQueue printer;
    ...
}

With this approach, the destruction time of DerivedPrintQueue is actually well-defined (before it goes out of scope). At any point of time, if the program needs to use the PrintQueue, he only needs to call PrintQueue::Instance(), which will return the extended DerivedPrintQueue instance.

The drawbacks of this approach, however, are that in the case that PrintQueue was never used, we have paid for the price of initialization, as well as PrintQueue::Instance() will still return an invalid object if we attempted to call it in one of our global object’s destructor.

Putting It All Together, a Basic Singleton Template Class

After reviewing the previous implementations, we should arrive at some conclusions.

  • In order to allow extending of the singleton class, we should use a pointer to the singleton class instead of a static instance. This also gives us the benefit of not paying the cost of initialization if we’re not using it.
  • An option should be provided to control the destruction order, or simply destroy a singleton.
  • At times, we would also like to give simply mark certain objects as singleton, and take the last reviewed approach.

And the result of these conclusions is a template class shown below:

C++
template<typename T>
class Singleton {
public : 
    static T& Instance();
    static void Destroy();

protected : 
    inline explicit Singleton() { 
        assert(Singleton::instance_ == 0); 
        Singleton::instance_ = static_cast<T*>(this); 
    }
    inline ~Singleton() { 
        Singleton::instance_ = 0; 
    }

private : 
    static T* CreateInstance();
    static void ScheduleForDestruction(void (*)());
    static void DestroyInstance(T*);

private : 
    static T* instance_;

private : 
    inline explicit Singleton(Singleton const&) {}
    inline Singleton& operator=(Singleton const&) { return *this; }
};    //    end of class Singleton

template<typename T>
typename T& Singleton<T>::Instance() {
    if ( Singleton::instance_ == 0 ) {
        Singleton::instance_ = CreateInstance();
        ScheduleForDestruction(Singleton::Destroy);
    }
    return *(Singleton::instance_);
}

template<typename T>
void Singleton<T>::Destroy() {
    if ( Singleton::instance_ != 0 ) {
        DestroyInstance(Singleton::instance_);
        Singleton::instance_ = 0;
    }
}

template<typename T>
inline typename T* Singleton<T>::CreateInstance() {
    return new T();
}

template<typename T>
inline void Singleton<T>::ScheduleForDestruction(void (*pFun)()) {
    std::atexit(pFun);
}

template<typename T>
inline void Singleton<T>::DestroyInstance(T* p) {
    delete p;
}

template<typename T>
typename T* Singleton<T>::instance_ = 0;

Note that the Destroy function is actually made public. This allows us to destroy the Singleton object explicitly.

However, be warned that that if the Destroy function is public, it holds several implications. Being public, it imposes a restriction that client cannot actually maintain a local reference of the Singleton object, and all usage of Singleton must goes through the Singleton::Instance. Because otherwise, the client might actually hold a reference to a destroyed Singleton.

There is, of course, a second issue, which I will bring up in the next section.

This seems to be the best Singleton Pattern implementation we can come up with, or is it?

Resolving the Multi-Threading Issue

In an ideal world, everything would have worked. However, we don’t live in an ideal world. Thus, Murphy’s law, “Everything that can go wrong, will go wrong” should be applied whenever possible. Imagine the following code:

C++
template<typename T>
typename Singleton<T>::RefType Singleton<T>::Instance() {
    if ( Singleton::instance_ == 0 ) {              //    1
        Singleton::instance_ = CreateInstance();    //    2
        ScheduleForDestruction(Singleton::Destroy);
    }
    return *(Singleton::instance_);                 //    3
}

In an ideal world, the first thread would reach // 1 first, then hit // 2, then // 3 before another thread calls Instance.

But remember, we don’t live in an ideal world. What would most likely happen would thus be: first thread calls Instance, reach // 1 first. The if statement would return true, and the first thread enters // 2. At the same time, the OS might have interrupted the execution and pass control to the second thread. The second thread calls Instance, and at this time, the if statement would still return true, and the second thread enters // 2 as well. Then the second thread executes // 2, and proceed on to // 3, and the execution was interrupted again and control returned to the first thread. Then the first thread executes // 2, overwriting the instance the second thread created (memory leakage), and proceeds on to // 3.

Of course, whenever multithreading is introduced, we should always also introduce synchronization measures to ensure the ‘correctness’ state of an application. In this case, a mutex lock should be introduced. (Please refer to your favorite OS programming reference on thread synchronization.)

A quick solution would produce codes somewhat akin to the one that follows:

C++
template<typename T>
typename Singleton<T>::RefType Singleton<T>::Instance() {
    LockThread lock;                               //    1
    if ( Singleton::instance_ == 0 ) {             //    2
        Singleton::instance_ = CreateInstance();   //    3
        ScheduleForDestruction(Singleton::Destroy);
    }
    return *(Singleton::instance_);                //    4
}

This implementation falls short of performance as a lock is imposed even when there apparently is no danger (when if fails). A better implementation would have been:

C++
template<typename T>
typename Singleton<T>::RefType Singleton<T>::Instance() {
    if ( Singleton::instance_ == 0 ) {              //    1
        LockThread lock;                            //    2
        Singleton::instance_ = CreateInstance();    //    3
        ScheduleForDestruction(Singleton::Destroy);
    }
    return *(Singleton::instance_);                 //    4
}

Though, the same synchronization issue comes back. For example, the first thread hits // 1, and before it is about to execute // 2, control is passed to the second thread….

Surprisingly though, a better solution can be derived by merging the above two codes.

C++
template<typename T>
typename Singleton<T>::RefType Singleton<T>::Instance() {
    if ( Singleton::instance_ == 0 ) {
        LockThread lock;
        if ( Singleton::instance_ == 0 ) {
            Singleton::instance_ = CreateInstance();    
            ScheduleForDestruction(Singleton::Destroy);
        }
    }
    return *(Singleton::instance_);
}

The new solution would result in quick access for threads that accessed Instance when instance_ is valid, and slower access (due to the locks) when instance_ is invalid, but those are really rare and one-time cases.

Remember, earlier I mentioned that a public Destroy has another implication? Ok, imagine this process. Assuming the Singleton has been created, Thread A calls Singleton::Instance. It tests the instance and finds it valid, then proceed to the return instance statement. Then Thread B comes into the scene. It checks that instance is valid, and proceeds to destroy it (regardless of whether it's double-checked pattern with a lock or otherwise). After this, control is returned to Thread A again, which attempts to dereference an invalid Singleton instance, and return to the client. From this, we can deduce that exposing Destroy for explicit destruction is actually not a viable design decision in a multi-threading environment.

More on Multi-Threading

The Double-Checked Locking mechanism applied seems to be a perfect solution, except that, in some (or maybe even most) cases, it still does not work3 as expected. The main reason is because on some systems, the writes to memory can be rearranged, and not be in chronological order. Due to this rearranging of writes, different threads might, at times, have a different 'view' of the current memory. A thread might have made the lock, created the Singleton, unlock, and carried on, while another thread goes in, and see that the instance is still not created, because the system had rearranged the writes and is performing the writes, and has yet to write the new value of the instance_ to the memory yet. Aside from this, some processors actually maintain locally cached copies of memory, creating even more problems when you need instant and up to date data.

Other possible solutions are employing up to a Triple-Checked Locking mechanism, or issue an explicit memory barrier instruction (which forces writes to the memory and retrieves new copies).

Resolving the Dead Reference Issue

Remember earlier when an example was given with the Singleton Logging Utility? The incident where after the destruction of a Singleton, another class object would still wish to get an instance of the Logging Utility. In such a scenario, what should be the possible response of a singleton? We can identify the following responses, either recreate the instance, or throw an exception.

First, however, support has to be built into the Singleton class so that it can know if a null instance means a dead reference situation, or simply an uninitialized instance. An additional boolean variable is introduced to fill the role. The resulting Instance code might look as follows (remember to add the relevant boolean variable setting to Destroy as well):

C++
template<typename T>
typename Singleton<T>::RefType Singleton<T>::Instance() {
    if ( Singleton::instance_ == 0 ) {
        LockThread lock;
        if ( Singleton::instance_ == 0 ) {
            if ( Singleton::destroyed_ ) {
                OnDeadReference();
                Singleton::destroyed_ = false;
            }
            Singleton::instance_ = CreateInstance();
            ScheduleForDestruction(Singleton::Destroy);
        }
    }
    return *(Singleton::instance_);
}

The implementation of OnDeadReference could contain codes to throw an exception, or simply an empty function so we can recreate the Singleton for usage.

Redesign the Template Singleton for Extensibility, a Loki1 Design

Given the above implementations, we could actually identify certain policies2 to define the behavior of a Singleton. They are namely:

  1. Creation. Could be either a normal invocation of new operator, a static object, or even an empty policy.
  2. Lifetime. Could be the normal LIFO sequence with atexit, recurring, or an empty policy (for use with creation of Singleton on the stack in main, for example)
  3. ThreadingModel. Could be single-threaded, where no locks are implemented, or multi-threaded.

A single-threaded model policy might look like the following:

C++
template<typename T>
class SingleThreaded {
public : 
    typedef T VolatileType;

protected : 
    inline explicit SingleThreaded() {}
    inline ~SingleThreaded() {}

protected : 
    class LockThread {
    public : 
        inline explicit LockThread() {
            SingleThreaded::Lock();
        }
        inline ~LockThread() { 
            SingleThreaded::Unlock(); 
        }

    private : 
        inline explicit LockThread(LockThread const&) {}
        inline LockThread& operator=(LockThread const&) { return *this; }
    };    //    end of class LockThread

private : 
    friend LockThread;

    inline static void Lock() {}
    inline static void Unlock() {}

private : 
    inline explicit SingleThreaded(SingleThreaded const&) {}
    inline SingleThreaded& operator=(SingleThreaded const&) { return *this; }
};    //    end of class SingleThreaded

followed by a default life time (FILO) policy:

C++
template<typename T>
class DefaultLifetime {
protected : 
    inline explicit DefaultLifetime() {}
    inline ~DefaultLifetime() {}

    inline static void OnDeadReference() { 
        throw std::logic_error("Dead Reference Detected"); 
    }
    inline static void ScheduleForDestruction(void (*pFun)()) {
        std::atexit(pFun); 
    }

private : 
    inline explicit DefaultLifetime(DefaultLifetime const&) {}
    inline DefaultLifetime& operator=(DefaultLifetime const&) { return *this; }
};    //    end of class DefaultLifetime

And the creation/destruction policy:

C++
template<typename T>
class CreateUsingNew {
protected : 
    inline explicit CreateUsingNew() {}
    inline ~CreateUsingNew() {}

    inline static T* CreateInstance() { return new T(); }
    inline static void DestroyInstance(T* t) { delete t; }

private : 
    inline explicit CreateUsingNew(CreateUsingNew const&) {}
    inline CreateUsingNew& operator=(CreateUsingNew const&) { return *this; }
};    //    end of class CreateUsingNew

The Singleton class template that uses the above mentioned policies would thus look as follows:

C++
template<typename T, typename CreationPolicy = CreateUsingNew<T>, 
template <typename> class LifetimePolicy = DefaultLifetime, 
template <typename> class ThreadingModel = SingleThreaded>
class Singleton : public CreationPolicy, public LifetimePolicy<T>, public ThreadingModel<T> {
public : 
    static T& Instance();
    static void Destroy();

protected : 
    inline explicit Singleton() { 
        assert(Singleton::instance_ == 0); 
        Singleton::instance_ = static_cast<T*>(this); 
        Singleton::destroyed_ = false; 
    }
    inline ~Singleton() { 
        Singleton::instance_ = 0; 
        Singleton::destroyed_ = true; 
    }

private : 
    static VolatileType* instance_;
    static bool destroyed_;

private : 
    inline explicit Singleton(Singleton const&) {}
    inline Singleton& operator=(Singleton const&) { return *this; }
};    //    end of class Singleton

template<typename T, typename C, template <typename> class L, template <typename> class T>
typename T& Singleton<T, C, L, T>::Instance() {
    if ( Singleton::instance_ == 0 ) {
        LockThread lock;
        if ( Singleton::instance_ == 0 ) {
            if ( Singleton::destroyed_ ) {
                OnDeadReference();
                Singleton::destroyed_ = false;
            }
            Singleton::instance_ = CreateInstance();
            try {
                ScheduleForDestruction(Singleton::Destroy);
            } catch(...) {
                DestroyInstance(Singleton::instance_);
            }        
        }
    }
    return *(Singleton::instance_);
}

template<typename T, typename C, template <typename> class L, template <typename> class T>
void Singleton<T, C, L, T>::Destroy() {
    if ( Singleton::instance_ != 0 ) {
        LockThread lock;
        if ( Singleton::instance_ != 0 ) {
            DestroyInstance(Singleton::instance_);
            Singleton::instance_ = 0;
            Singleton::destroyed_ = true;
        }
    }
}

template<typename T, typename C, template <typename> class L, template <typename> class T>
typename Singleton<T, C, L, T>:: VolatileType* Singleton<T, C, L, T>::instance_ = 0;
template<typename T, typename C, template <typename> class L, template <typename> class T>
bool Singleton<T, C, L, T>::destroyed_ = false;

This time, the implementation of the Singleton allows it to be easily extensible. With different policies, the same codes can be tailored to create a new Singleton with different behavior.

Sample Usage

The Singleton template class can be used in two forms, namely:

C++
class A : public Singleton<A> {...};
A::Instance().DoSomething();

as well as:

C++
class A {...};
Singleton<A>::Instance().DoSomething();

Additional Implementations

The current implementation of Singleton can only call the default constructor of the Singleton class, and at times, we would actually like to specific additional arguments and call an overloaded constructor. Actually, this can be easily achieved, by simply providing a newly defined CreationPolicy. The example CreationPolicy is as follows:

C++
class Test {
public : 
    explicit Test(char const*);
    ...
};    //    end of class Test

class TestCreator {
public : 
    inline static void SetString(char const* str) { 
        str_ = str;
    }

protected : 
    inline explicit TestCreator() {}
    inline ~TestCreator() {}

    //    creation/destruction/storage issues
    inline static Test* CreateInstance() { return new Test(str_); }
    inline static void DestroyInstance(Test* t) { delete t; }

private : 
    static std::string str_;

private : 
    inline explicit TestCreator(TestCreator const&) {}
    inline TestCreator& operator=(TestCreator const&) { return *this; }
};    //    end of class TestCreator

By providing TestCreator as the CreationPolicy template parameter of Singleton, we can simply call Singleton<Test>::SetString(someValue), and then Singleton<Test>::Instance will invoke the constructor Test(someValue).

And that is why the CreationPolicy template parameter for Singleton was not declared as a template class; to facilitate custom constructor calling.

Runtime Polymorphism

At times, during the application runtime, the application might not be satisfied with simply the same Singleton instances. The application might actually wish to replace the existing Singleton instance with its extended type, to be used in future operations.

Since the Singleton stores a pointer to the instance, it already supports this functionality. The pointer can not only point to the parent object, but derived types as well. Below is the initial implementation of the function that exposes such functionality.

C++
template<typename T, typename C, template <typename> class L, template <typename> class T>
inline void Singleton<T, C, L, T>::Reset(T* p) {
LockThread lock;
    if ( Singleton::instance_ != 0 ) {
        DestroyInstance(Singleton::instance_);
        Singleton::instance_ = 0;
    }
    Singleton::instance_ = p;
    ScheduleForDestruction(Singleton::Destroy);
}

At first glance, it seems to fulfill all our needs. It performs a lock, destroys the existing instance if any, and sets the instance to point to the new instance.

In this case, what happens when the application ends, and the scheduled program terminates? Since the behavior is determined by the Lifetime policy, as well as the Creation policy, we will not know what would happen. A default behavior of deleting the instance might be wrong, since the instance could be allocated on the stack. Hence, if the user should choose to reset the Singleton, it would therefore be logical for him to be responsible for the object cleaning up. A simple solution would be for the user to simply pass in a clean up function as well, so that it can be called upon Destroy. The improved version of Reset and Destroy is shown below:

C++
template<typename T, typename C, template <typename> class L, template <typename> class T>
void Singleton<T, C, L, T>::Reset(T* p, void (*pFun)(T*)) {
    LockThread lock;
    if ( Singleton::instance_ != 0 ) {
        DestroyInstance(Singleton::instance_);
    } else if ( p != 0 ) {
        ScheduleForDestruction(Singleton::Destroy);
    }
    Singleton::instance_ = p;
    Singleton::pFun_ = pFun;
}

template<typename T, typename C, template <typename> class L, template <typename> class T>
void Singleton<T, C, L, T>::Destroy() {
    if ( Singleton::instance_ != 0 ) {
        LockThread lock;
        if ( Singleton::instance_ != 0 ) {
            if (  Singleton::pFun_ != 0 ) {
                Singleton::pFun_(Singleton::instance_);
            } else {
                DestroyInstance(Singleton::instance_);
            }
            Singleton::instance_ = 0;
            Singleton::destroyed_ = true;
        }
    }
}

where pFun_ is of a member of Singleton of the below form:

C++
static void (*pFun_)(T*);

It seems to work well, and perfect, or is it? Except with this new feature, we have to impose a limitation on ourselves, to not use the form:

C++
class A : public Singleton<A> {...};
class B : public A {...};

A::Instance().DoSomething();

Recall that in the Singleton’s constructor, instance_ is verified to be null, and later instance_ is set to this. This is actually the cause of our new problem (pun intended).

Imagine the following program execution sequence A is constructed, via Instance using new. Application decides to change the A’s instance to B instead, and calls Reset(new B, …). At the instantiation of B, instance is no longer null, and assertion fails. Program terminates. The world no longer looks as bright.

There are no quick workarounds. We could impose a limitation on ourselves to use:

C++
Singleton<A>::Instance().DoSomething();
Singleton<A>::Reset(new B, ...);

Or we could also remove the assertion checks and assignment in the constructor, or we could simply introduce a new policy to determine the behavior (left as an exercise for the reader).

Implications of Runtime Polymorphism

So we've added runtime polymorphism to the implementations. Next, we have to understand the implications.

As with Destroy, Reset would result in unsafe multi-threading codes. For one, the client can no longer be sure that their locally kept copies of Singleton object to be valid. A second point brought up was that, in the case of multithreading, imagine a thread calls Instance, and another calls Reset, when instance is already valid. The first thread tested instance, and since is valid, proceeds to the return instance code. Just as the function returns the instance to the client, and before the client performs the actual Singleton operation, execution is interrupted, and control transferred to the second thread. The second thread perform a lock, destroys the current instance, and assigned a new instance. Control is then returned to the first thread, which is operating on an invalid instance.

Obviously, Reset might be a handy and interesting function to have, but as with Destroy, it actually results in code that is not multithreading safe. Of course, we could actually do some template metaprogramming which hides Reset and Destroy if the threading policy is Multi-threading, and review them if otherwise. But this is a design decision which you must decide if you really need to use Reset and Destroy, since they seem to exploit the undefined rules of a Singleton Pattern.

Conclusion

If you had to declare global variables, chances are you would be better off with a Singleton Pattern implementation. And reviewing the various implementations out there, Loki’s implementation seems to be the most robust, extensible Singleton Pattern implementation. However, my proposed implementation builds upon Loki’s pattern, and addresses the following additional issues:

  1. Allows stack creation of a Singleton object and be used any/every where in an application
  2. Notice the try catch block in my implementation of Instance, which Loki lacks. Being an unknown policy, we cannot make assumptions that ScheduleForDestruction will never throw, since we would not know what exactly happens in it. If ScheduleForDestruction do throw, there would be no proper way to clean up the allocated instance_, short of a manual call of Destroy.
  3. Allows runtime changes of the Singleton’s internal instance, enabling further polymorphism.

However, one should not take the code explained and given as-it-is. The code, although addresses multi-threading, is not complete in its design. If you are using the Singleton class in a multi-threading environment, you should check up on the best available locking mechanism you have, and modify the codes. To use the Singleton 'correctly', you would need to come up with a set of restrictions and limits to impose yourself, and the way to use it.

Decomposing a problem into its most generic form, and applying policies on the generic form, can yield extremely surprising amount of code reuse, as well as code usability.

With the creation policy pattern, we can even further customize the construction of the Singleton objects, a feature often overlooked by most implementation. (Note, you can even further decompose the Creation Policy into a Storage Policy, which takes care of memory allocation and deallocation, and a Construction Policy)

Footnotes

1 Loki is the library described in Modern C++ Design, Alexandrescu

2 A policy defines a class interface or class template interface. Policies are similar to traits, but differ in that they place more emphasis on behavior than type. Policy can also be described as the Strategy pattern [GOF], except that policies are compile time bound. [Alexandrescu 2001]

3 The "Double-Checked Locking is Broken" Declaration provided by Julian Brown

References

  • [GOF]: Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Pub Co, 1995
  • [Meyers 1996b]: Scott Meyers, More Effective C++: 35 New Ways to Improve Your Programs and Designs. Addison-Wesley Pub Co, 1995
  • [Alexandrescu 2001]: Andrei Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley Pub Co, 2001

History

  • 21st August, 2003: More on multi-threading, as well as updated conclusion
  • 13th August, 2003: Risks of having Destroy and Reset in a multi-threading environment, as well as definition of Singleton updated
  • 12th August, 2003: Initial version uploaded

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Web Developer
Singapore Singapore
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThanks for the feedback, keep them coming in Pin
Lai Shiaw San Kent12-Aug-03 17:01
Lai Shiaw San Kent12-Aug-03 17:01 
GeneralI have some issues. Pin
WREY12-Aug-03 8:25
WREY12-Aug-03 8:25 
GeneralRe: I have some issues. Pin
Lai Shiaw San Kent12-Aug-03 15:56
Lai Shiaw San Kent12-Aug-03 15:56 
QuestionIs this correct? Pin
e_invalidarg12-Aug-03 6:32
e_invalidarg12-Aug-03 6:32 
AnswerRe: Is this correct? Pin
Julian Brown12-Aug-03 13:35
Julian Brown12-Aug-03 13:35 
GeneralRe: Is this correct? Pin
Lai Shiaw San Kent12-Aug-03 16:59
Lai Shiaw San Kent12-Aug-03 16:59 
AnswerRe: Is this correct? Pin
Lai Shiaw San Kent12-Aug-03 16:52
Lai Shiaw San Kent12-Aug-03 16:52 
GeneralRe: Is this correct? Pin
Julian Brown12-Aug-03 22:19
Julian Brown12-Aug-03 22:19 
There is no solution!

This Test -> Lock -> Test pattern cannot work unless the Create is atomic. The Create can never be atomic ( new T() is no maore atomic than calling a static Create method).

The only solution is to abandon this pattern and to Lock BEFORE the test.

I would also take issue with the whole concept of the Reset method. That a singleton can change during the lifetime of the program seesm to me to break the whole concept of a singleton.
Additionally it is fragile:
There is nothing to prevent assignment of a Singleton reference - if this is done by one thread, and another thread calls Reset the reference will become invalid (which is counter intuitive in a singleton) and the prgram will break;


Julian
GeneralRe: Is this correct? Pin
Lai Shiaw San Kent13-Aug-03 3:55
Lai Shiaw San Kent13-Aug-03 3:55 
GeneralRe: Is this correct? Pin
Julian Brown13-Aug-03 5:26
Julian Brown13-Aug-03 5:26 
GeneralRe: Is this correct? Pin
Lai Shiaw San Kent19-Aug-03 2:20
Lai Shiaw San Kent19-Aug-03 2:20 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.