Friday, 11 April 2014

Design pattern - Singleton pattern

Singleton patter is one of the creation patterns and it tries to guarantee there is only one instance in the whole program and in its entire life span. It provides a certain service for the entire program, for instance logging system, and it guarantees that all legitimate users are retrieving the same instance and works for everyone unanimously.

1. Concepts and its simple implementation
This concept is to provide a unique instance of this class for everyone and does not allow the programmers/users to instantiate instance by themselves. So based on this requirements, here are the key points in implementation
- The constructor has not to be accessible to programmers/users. So it can't be declared as public.
- Its unique instance is not copy-able or assignable in outside of the class itself. So it's copy constructor and assignment operator can't be declared as public either.
- Provide a function to return this unique instance.
- Guarantee this instance is unique and not null as long as initialized.

Here is the simple implementation of Singleton in C++
*********************************************************************************
class LicenseManager{
public:
    static LicenseManager* GetInstance() {
         if (m_instance == nullptr) {
             m_instance = new LicenseManager();
         }

         return m_instance;
    }

private:
    LicenseManager() { // implementation}

    // suppress these two default implementation
    // use new syntax in C++11, read this blog,
    // http://cpluspluslearning-petert.blogspot.co.uk/2014/03/c11-features-definitely-worth-learning_17.html
    LicenseManager(const LicenseManager& other);
    LicenseManager& operator=(const LicenseManager& other);
    static LicenseManager* m_instance; 
};
*********************************************************************************

The constructor is declared as "private" in this simple example. It can be declared as "protected", if it has derived class. (For instance, in the implementation of singleton for license manager. This license manager should be implemented as singleton. But it may have different implementation) Simply expand the function GetInstance() with some option as a factory method and declare LicenseManager() as "protected" in order that its derived class can call it.

2. Thread safety issue with this simple implementation
The thread safety issue arises when multiple threads call function LicenseManager::GetInstance() all at onece, when m_instance is not instantiated. For instance all hit "m_instance == nullptr" and this line code evaluated as true, then each thread will create an instance. This obviously breaks the requirement that this instance should be unique.

3. Solutions
There are a few solutions for this issues based on different techniques. I will present them all and discuss their strength and weakness.
Instantiate this unique copy before threads are spooled:
In order to overcome the thread safety issue caused when multiple threads call GetInstace() at the same time, this solution is to eliminate the threat at the starting point of the program. Call GetInstance() before the threads are spooled at the beginning of main() function.

*********************************************************************************
int main(int argc, char* argv[])
{
    LicenseManager::GetInstance();

    // spool the threads here
}
*********************************************************************************

Certainly this solution will remove the threat before it can happen. The drawback is that we initialize this object well before it needed. This is against of "good practice" rules, lazy initialization. Especially when the object is considerable large or this object is not required on service for certain application setting and it will be complete waste. Another drawback is that this singleton implementation can only be used in single thread applications.

Use mutual exclusion
Mutual exclusion is one of most used facilities that are used to sync data/operations between threads. It guarantees that only one thread can acquire the guarded code/data at one time. When one thread acquires the control other threads have to wait until it releases the control.

Here is the improved solution:
*********************************************************************************
    static LicenseManager* GetInstance() {
         EnterCriticalSection();
         if (m_instance == nullptr) {
             m_instance = new LicenseManager();
         }
         LeaveCriticalSection();

         return m_instance;
    }
*********************************************************************************

Here we use critical section (under Windows), which is a cheap version of mutex. It simply runs faster. (Mutex is a kernel call but critical section is an application call). Now the important bits are guarded. But the biggest problem with this solution is the performance. It means all the calls (even after the m_instance is instantiated and this function is simply returning the instance. But this piece of code is still been guarded) are guarded and hence bottleneck could hit here.

Here is the better improved solution
*********************************************************************************
    static LicenseManager* GetInstance()
   {
         if (m_instance == nullptr) {
             EnterCriticalSection();
             m_instance = new LicenseManager();
             LeaveCriticalSection();
         }
         return m_instance;
    }
*********************************************************************************

This solution certainly remove the bottleneck problem to guard this comparison when m_instance is safely initialized as unique copy. But is this really thread safe now? Not yet. Imagine 2 threads call this function at the same time. One function acquires the critical section and the other is waiting at the body of if-clause. Immediately the first thread creates an instance for m_instance and release the critical section, the second thread goes into the critical section again and create another instance for m_instance. Certainly m_instance is not unique any more and besides there is memory leak.

Now we come to the real solution:
*********************************************************************************
class LicenseManager{
public:
    static LicenseManager* GetInstance() {
         EnterCriticalSection();
         if (m_instance == nullptr) {
             EnterCriticalSection();
             if (m_instance == nullptr) {
                 m_instance = new LicenseManager();
             }
             LeaveCriticalSection();
         }
         return m_instance;
    }

private:
    LicenseManager() { // implementation}

    // suppress these two default implementation
    // use new syntax in C++11, read this blog,
    // http://cpluspluslearning-petert.blogspot.co.uk/2014/03/c11-features-definitely-worth-learning_17.html
    LicenseManager(const LicenseManager& other);
    LicenseManager& operator=(const LicenseManager& other);
    static volatile LicenseManager* m_instance; 
};
*********************************************************************************

Now it's thread safe and no bottleneck to call this function after m_instance is safely initialized. Critical section is to make sure only one thread goes into the guarded code to initialized the m_instance at a time. And m_instance is declared as "volatile" to make sure the thread to reload this value when evaluating if it is a nullptr. (Remove the sticky memory problem)

This is a potential risk that the instance could be nullptr all the time for this solution. It comes from how the critical section/mutex is used. What happens if exception is thrown when call "m_instance = new LicenseManager()". Then the bottom part "LeaveCriticalSection" is never been called and this can cause m_instance never to be instantiated properly and always stay nullptr. Because as long as the critical section/mutex is not unlocked, any threads comes in, finds m_instance is nullptr and try to initialize it. But surely it can't any more because the critical section locked forever (as it isn't unlocked at the first failure attempt). The solution for this potential risk is to use RAII (resource acquisition is initialization). See my other blog entry, http://cpluspluslearning-petert.blogspot.co.uk/2014/04/raii.html, if interested.

Use RAII technique to make sure critical section is unlocked automatically no matter if exception is thrown.
*********************************************************************************
class MyCriticalSection{
public:
    MyCriticalSection() { EnterCriticalSection();}
    ~MyCriticalSection() {LeaveCriticalSection();}
};

    static LicenseManager* GetInstance() {
         EnterCriticalSection();
         if (m_instance == nullptr) {
             MyCriticalSection();
             if (m_instance == nullptr) {
                 m_instance = new LicenseManager();
             }
         }
         return m_instance;
    }
*********************************************************************************

With this solution all the property will be guarantee for Singleton:
- Unique instance of m_instance
- m_instance will be re-initialized if all previous attempts fail due to exception
- No bottleneck to get the pointer/reference after m_instance is successfully initialized

Meyer's version:
Meyer Soctts shows his version of singleton pattern implementation in his "Effective C++" book, which I think is the best implementation of all.

*********************************************************************************
class LicenseManager{
public:
    static LicenseManager* GetInstance() {
         static LicenseManager instance;
         return &instance;
    }

private:
    LicenseManager() { // implementation}

    // suppress these two default implementation
    // use new syntax in C++11, read this blog,
    // http://cpluspluslearning-petert.blogspot.co.uk/2014/03/c11-features-definitely-worth-learning_17.html
    LicenseManager(const LicenseManager& other);
    LicenseManager& operator=(const LicenseManager& other); 
};
*********************************************************************************

Fundamentally it has the same idea like the solution with mutex. By just examining the C++ code you may not be able to find the tricks. If you examine the machine code of Meyer's version, it will be clear that there is global guard generated by compiler for this "static" and local variable in GetInstance(). As this "static" in this local function has the persistent storage (in static/global section) but local linkage. As long as it is initialized, it will has a life span lasting until the application ends, unless its destructor is called explicitly

4. Summary
All there 3 solutions are valid ones. As for the portability, the first one has the worse portability as it is not thread safe and therefor the first time calling GetInstance() has to happen before the threads are spooled. The second and third solution are equally good. But the third solution has better portability as it does not depend on any mutex classes.

Biobiliograogy:
[1] GOF, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, "Design Patterns - Elements of Reusable Object-Oriented Software", 1994

No comments:

Post a Comment