Introduction
Singleton
is a pattern which provides a design strategy which helps to control the instance creation of a
class. It helps to maintain only a single instance (or variable instance numbers) of a
class by making the constructor private and providing a instance creation
function.
Singleton
is a very widely used pattern. Well, at the same time it can be implemented in
different ways. The pattern specification says only a single instance or a controlled
number of instances can be created. If we consider a C++ version of singleton
there are multiple ways we can implement the pattern. Another consideration is
multithreading. We will consider the different design strategies and see how
we can implement a thread safe singleton object.
The simplest design
See the below code snippet to
implement a singleton.
class XMLReader
{
public:
XMLReader& GetInstance()
{
static XMLReader XMLReaderObj;
return XMLReaderObj;
}
private:
XMLReader(){}
};
This is the most simplest form of
singleton implementation in C++. A static local object is created and its reference
is returned. Since the static objects lives in the global memory, it will live
till the process ends. Here we cannot control the lifetime of the object. It
gets destructed when the application main returns. Suppose we need to do some
cleanup before the main returns. For example we needs to save some information
to a XML file. We can use an XML library like MSXML for managing XML files.
This library is COM based. We initialize COM using CoInitialize or
CoInitializeEx one time per thread and calls CoUninitialize before the thread
returns. See the full source code for XMLWriter below.
class XMLWriter
{
public:
static XMLWriter& GetInstance()
{
static XMLWriter SingletonObj;
return SingletonObj;
}
bool UpdateXML(const DataHolder& DataHolderObj)
{
}
~XMLWriter()
{
_bstr_t bstrFileName(_T("E:\\Test.xml"));
m_pXMLDom->save( bstrFileName );
}
private:
XMLWriter()
{
InitDOM();
}
void InitDOM()
{
HRESULT hr = m_pXMLDom.CreateInstance(__uuidof(MSXML2::DOMDocument60));
if (SUCCEEDED(hr))
{
m_pXMLDom->loadXML(
_bstr_t(_T("<?xml version='1.0'?>\n")
_T("<doc title='test'>\n")
_T(" <page num='1'> \n")
_T(" <para title='Saved at last'> \n")
_T(" This XML data is finally saved.\n")
_T(" </para>\n")
_T(" </page>\n")
_T(" <page num='2'>\n")
_T( " <para>\n")
_T(" This page is intentionally left blank.\n")
_T(" </para>\n")
_T(" </page>\n")
_T("</doc>\n")));
}
}
private:
MSXML2::IXMLDOMDocumentPtr m_pXMLDom;
};
void main()
{
CoInitialize(0);
XMLWriter& WriterObj = XMLWriter::GetInstance();
CoUninitialize();
}
Certainly, this code will break
the application and it will crash on exit. If we look at the destructor of
XMLWriter, it is trying to save the loaded XML in to a file. But before the
main returns we have uninitialized COM and the saving code needs COM
environment. When the main returns the destructor gets kicked and code to save
file executes. Bang!!!
We can make the call to
CoInitialize in the constructor and CoUninitialize in the destructor. But in a
multi threaded application, we can’t guarentee in which thread's context the
object is created and so CoInitialize is called. So the CoInitialize may get
called in one worker thread and CoUninitialize in the main thread(inside the
destructor) as the application exits. Infact, the mainthread have not called a
matching CoInitialize. This can cause undefined results.
This is simple example to show
that we need control on the destruction process. When there is nothing to be
done while the object is destroyed, it is OK to use the local static object.
Infact it is a threadsafe implementation. The compiler will do the necessary
that a single instance of the static object is created when used in a
multithreaded environment.
Now, we will look in to other
strategies in singleton implementation.
Creator and Destroyer methods for finer control
See the below code snippet.
class XMLReader
{
public:
static XMLReader& CreateInstance()
{
if(!m_pXMLReader)
{
m_pXMLReader = new XMLReader;
}
return (*m_pXMLReader);
}
static XMLReader& GetInstance()
{
return (*m_pXMLReader);
}
static void DestroyInstance()
{
delete m_pXMLReader;
}
~XMLReader()
{
}
private:
XMLReader()
{
}
private:
static XMLReader* m_pXMLReader;
};
This code seems OK. But what
happens if memory allocation failure happens in CreateInstance. It will be
returning a NULL reference if new returns NULL. If the allocation failure is
returned as std::bad_alloc, CreateInstance can throw an exception, which the
singleton object creation code should be aware. In the case of new returning
NULL, GetInstance also return a NULL reference, which can lead to a runtime
error. A modified version of this implementation is shown below.
class XMLReader
{
public:
static XMLReader& CreateInstance()
{
try
{
if(!m_pXMLReader)
{
m_pXMLReader = new XMLReader;
}
return (*m_pXMLReader);
}
catch(std::bad_alloc& baException)
{
throw new ObjectCreationFailed(_T("Memory allocation failed"));
}
catch(...)
{
throw new ObjectCreationFailed(_T("Unknown reason"));
}
}
static XMLReader& GetInstance()
{
if(!m_pXMLReader)
{
throw new ObjectDoesNotExist;
}
return (*m_pXMLReader);
}
static void DestroyInstance()
{
delete m_pXMLReader;
}
~XMLReader()
{
}
private:
XMLReader()
{
}
private:
static XMLReader* m_pXMLReader;
};
The multi threading aspects
In above version if any error
occurs in the object creation, the object itself will not be created. This is a
much better one. Now, what about thread safety aspects. What happens if
CreateInstance is called simultaneously by two threads? Certainly, there is a possibility
of multiple instances created. This violates the assumption of singleton of
only a sole instance. How can we solve this?
The solution is very common in
the multithreaded environment, we need to synchronize the object creation using
a critical section or mutex. We shall discuss a complete solution in the coming
sections.
The usage the XMLReader class
will be as shown below.
XMLReader& XMLRdrObj =
XMLReader::CreateInstance();
XMLReader::DestroyInstance();
Suppose we have multiple child
threads and both needs the same singleton class. Probably, we need to call
CreateInstance() from thread1 and 2 (say). Similarly, DestroyInstance before
the threads end. But, how will we ensure that both threads see a valid instance
always. What if thread1 ends first and thread2 continues to run? It will be
catastrophic if thread1 invokes DestroyInstance and thread2 will be using an
already deleted instance. This problem suggests that we need a reference
counting mechanism, so that the object will be destroyed only when the final
DestroyInstance is invoked. See the following implementation.
class XMLReader
{
public:
static XMLReader& CreateInstance()
{
try
{
if(!m_pXMLReader)
{
m_pXMLReader = new XMLReader;
}
++m_nRefCount;
return (*m_pXMLReader);
}
catch(std::bad_alloc& baException)
{
throw new ObjectCreationFailed(_T("Memory allocation failed"));
}
catch(...)
{
throw new ObjectCreationFailed(_T("Unknown reason"));
}
}
static XMLReader& GetInstance()
{
if(!m_pXMLReader)
{
throw new ObjectDoesNotExist;
}
return (*m_pXMLReader);
}
static void DestroyInstance()
{
--m_nRefCount;
if(0 == m_nRefCount)
delete m_pXMLReader;
}
~XMLReader()
{
}
private:
XMLReader()
{
}
private:
static int m_nRefCount;
static XMLReader* m_pXMLReader;
};
This seems much better. We have
one more pending issue to be solved, a thread safe instance creation. Here I am
introducing a complete solution which is a template class, that can be reused
to make singleton classes.
The solution
See the code below.
#pragma once
#include <exception>
class CriticalSectionObject
{
public:
CriticalSectionObject()
{
InitializeCriticalSection(&m_stCriticalSection);
}
void Lock()
{
EnterCriticalSection(&m_stCriticalSection);
}
void UnLock()
{
LeaveCriticalSection(&m_stCriticalSection);
}
~CriticalSectionObject()
{
DeleteCriticalSection(&m_stCriticalSection);
}
private:
CRITICAL_SECTION m_stCriticalSection;
};
class AutoCriticalSection
{
public:
AutoCriticalSection(CriticalSectionObject* pCSObj)
{
m_pCSObj = pCSObj;
if( m_pCSObj )
{
m_pCSObj->Lock();
}
}
~AutoCriticalSection()
{
if( m_pCSObj )
{
m_pCSObj->UnLock();
}
}
private:
CriticalSectionObject* m_pCSObj;
};
extern CriticalSectionObject g_CSObj;
class GeneralException
{
public:
GeneralException(LPCTSTR lpctszDesciption)
{
m_ptcDescription = StrDup(lpctszDesciption);
}
~GeneralException()
{
LocalFree(m_ptcDescription);
}
TCHAR* GetDescription()
{
return m_ptcDescription;
}
private:
TCHAR* m_ptcDescription;
};
class ObjectCreationFailedException:public GeneralException
{
public:
ObjectCreationFailedException(LPCTSTR lpctszDesciption):GeneralException(lpctszDesciption)
{
}
};
class ObjectDoesNotExistException:public GeneralException
{
public:
ObjectDoesNotExistException():GeneralException(_T("Object does not exist"))
{
}
};
template <class T> class SingletonTemplate
{
public:
static T& CreateInstance()
{
try
{
AutoCriticalSection ACSObj(&g_CSObj);
if( 0 == m_pTSingleInstance )
{
m_pTSingleInstance = new T;
}
++m_nRefCount;
}
catch(std::bad_alloc)
{
throw new ObjectCreationFailedException(_T("Memory allocation failed"));
}
catch(...)
{
throw new ObjectCreationFailedException(_T("Unknown reason"));
}
return *m_pTSingleInstance;
}
static T& GetInstance()
{
if( 0 == m_pTSingleInstance )
{
throw new ObjectDoesNotExistException;
}
return *m_pTSingleInstance;
}
static void DestroyInstance()
{
AutoCriticalSection ACSObj(&g_CSObj);
(m_nRefCount > 0) ? --m_nRefCount:m_nRefCount;
if( 0 == m_nRefCount )
{
delete m_pTSingleInstance;
m_pTSingleInstance = 0;
}
}
private:
static T* m_pTSingleInstance;
static int m_nRefCount;
};
template <class T> T* SingletonTemplate<T>::m_pTSingleInstance = 0;
template <class T> int SingletonTemplate<T>::m_nRefCount = 0;
The following code shows how to use SingletonTemplate class.
#pragma once
#include "SingletonTemplate.h"
class SequenceGenSingleton
{
friend class SingletonTemplate<SequenceGenSingleton>;
public:
~SequenceGenSingleton(void);
long GetNextSeqNum();
private:
SequenceGenSingleton(void);
SequenceGenSingleton(int nInitVal);
private:
long m_lSeqNum;
};
The following code shows the usage of this class.
SingletonTemplate<SequenceGenSingleton>::CreateInstance();
SequenceGenSingleton& SingleObj = SingletonTemplate<SequenceGenSingleton>::GetInstance();
SingleObj.GetNextSeqNum();
SingletonTemplate<SequenceGenSingleton>::DestroyInstance();
The SingletonTemplate should be made the friend of the target singleton class (this gives access to the private constructor).
DLLs and singleton
In the case of usage from a DLL, the code can be as shown below.
BOOL WINAPI DllMain( IN HINSTANCE hDllHandle,
IN DWORD dwReason,
IN LPVOID lpvReserved )
{
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
SingletonTemplate<SequenceGenSingleton>::CreateInstance();
break;
case DLL_PROCESS_DETACH:
SingletonTemplate<SequenceGenSingleton>::DestroyInstance();
break;
}
}
Usage from a thread
In the case of usage from a child thread, the code can be as shown below.
DWORD WINAPI MyWorkerThread(LPVOID)
{
SingletonTemplate<SequenceGenSingleton>::CreateInstance();
SingletonTemplate<SequenceGenSingleton>::DestroyInstance();
return 0;
}
The thread function can have multiple return paths. So we need to call
DestroyInstance in all return paths. This gives the possibilty to define an
automatic class that can automate the release process. It can be as shown below.
template <class T> class AutoSingleton
{
public:
AutoSingleton()
{
SingletonTemplate<T>::CreateInstance();
}
~AutoSingleton()
{
SingletonTemplate<T>::DestroyInstance();
}
};
AutoSingleton<SequenceGenSingleton> AutoSingletonObj;
SequenceGenSingleton& SingleObj = SingletonTemplate<SequenceGenSingleton>::GetInstance();
The wrapup
This is all what I have to offer now. I hope the template class provided will be useful as it takes away the need for thread synchronization and reference counting. We can make a singleton
class which is thread safe just by parametrizing the SingletonTemplate with the required class and making it a friend of your class.
Working in an MNC at Trivandrum. I can be reached at sudheesh_perumbilli@yahoo.com.
My personal page http://mytechcraze.wordpress.com/