Per Thread Singleton

Introduction

Sometimes we want our singleton to be per thread instance, to avoid contention in using the objects in threads. Per thread singleton is usually implemented, using a Thread Local Storage(TLS). Per thread singleton too uses a static member to store its information, like normal singleton. Per thread singleton which is implemented, using a Thread Local Storage(TLS), like this one(code listing is shown below) from Dr Dobbs Journal, has a serious bug because it uses the static integer member to store the TLS slot index. And for any thread, the TLS slot index is not always the same for all threads if the thread has code or classes which also make use of the TLS to store information before the singleton instance allocates a TLS slot.

Code Listing from Dr Dobbs Journal

#include <windows.h>
#include <iostream.h>

// This class ensures that the thread specific instance gets
// deleted as each thread exits.
class ThreadSingletonDestroyer
{
public:
    ~ThreadSingletonDestroyer();
};

// This class implements the functionality needed for a thread
// specific Singleton class.
class ThreadSingleton
{
public:
    static ThreadSingleton* GetObject();
    DWORD GetThreadID()
    {
        return m_ThreadID;
    }

protected:
    ThreadSingleton(DWORD threadID)
    {
        m_ThreadID = threadID;
    }

private:
    static DWORD m_ThreadIndex; // static TLS slot index
    DWORD  m_ThreadID;
    static void DestroyObject();


    friend ThreadSingletonDestroyer;
};


DWORD ThreadSingleton :: m_ThreadIndex = -1;

ThreadSingletonDestroyer :: ~ThreadSingletonDestroyer()
{
    // Just call the function DestroyObject.
    ThreadSingleton :: DestroyObject();
}

void ThreadSingleton :: DestroyObject()
{
    // If the thread Index is not even initialized yet, it
    // means there have been no object created for this class
    // yet, just return.
    if(-1 == m_ThreadIndex)
    {
        return;
    }

    ThreadSingleton *obj = NULL;

    // If there is an object at the TLS index, delete it,
    // otherwise just return.
    obj = (ThreadSingleton*)TlsGetValue(m_ThreadIndex);

    if(NULL != obj)
    {
        delete obj;
    }
}

ThreadSingleton* ThreadSingleton :: GetObject()
{
    ThreadSingleton *retVal = NULL;

    // This "if" block needs to be protected by a
    // CRITICAL_SECTION, left out for sake of clarity.
    if(-1 == m_ThreadIndex)
    {
        m_ThreadIndex = TlsAlloc();
        if(-1 == m_ThreadIndex)
        {
            cout << "Error while calling TlsAllocn";
            return NULL;
        }
    }

    // Try to get an object at the TLS index, but if we can't
    // then create one and put in the TLS location.
    retVal = (ThreadSingleton*)TlsGetValue(m_ThreadIndex);

    if(NULL == retVal)
    {
        retVal = new ThreadSingleton(GetCurrentThreadId());

        TlsSetValue(m_ThreadIndex, retVal);
    }

    return retVal;
}


// This is the thread function, this function is called to
// execute the thread created in "main".
DWORD WINAPI ThreadMain(void*)
{
    // This object's destructor will destroy the thread
    // specific singleton instance, upon the thread's exit.
    ThreadSingletonDestroyer tsDestroyer;

    ThreadSingleton *obj = ThreadSingleton :: GetObject();

    cout <<"The thread ID is = " << obj->GetThreadID() << endl;

    return 0;
}

int main()
{
    ThreadSingletonDestroyer tsDestroyer;
    DWORD dwThreadID;

    ThreadSingleton *obj = ThreadSingleton :: GetObject();

    // Print the thread ID.
    cout <<"The thread ID is = " << obj->GetThreadID() << endl;

    HANDLE handle = CreateThread(NULL, 0, ThreadMain, NULL, 0,
                                 &dwThreadID);

    if(NULL == handle)
    {
        cout << "Error while creating a thread!n";
        return -1;
    }

    (void)WaitForSingleObject(handle, INFINITE);

    return 0;
}

I have chosen to make my per thread singleton, using a dictionary as a static member instead. The dictionary has the thread ID as the key and the templatized/generic object as value.

During accessing the singleton for the first time in the thread, the singleton will check that the thread ID does not exist in the dictionary and will attempt to instantiate the object and store it in the dictionary for future retrieval before returning object. For subsequent retrieval, it just fetches the object from the dictionary using the thread ID.

C++ Per Thread Singleton

template< typename T >
class PerThreadSingleton
{
private:
    PerThreadSingleton() {} // hide ctor
    ~PerThreadSingleton() {} // hide dtor
    PerThreadSingleton(PerThreadSingleton& that) {} // hide copy ctor
    void operator=(PerThreadSingleton& that) {} // hide asst optor
public:
    static T* Instance()
    {
        CritSectLock lock(m_cs.GetInnerCritSect());
        DWORD nThreadID = GetCurrentThreadId();
        T* retVal;
        if(m_map.find(nThreadID)!=m_map.end())
        {
            retVal = m_map[nThreadID];
        }
        else
        {
            retVal = new T();
            m_map[nThreadID] = retVal;
        }
        return retVal;
    }
    //! Deletes the static instance immediately
    static void DeleteInstance()
    {
        CritSectLock lock(m_cs.GetInnerCritSect());
        DWORD nThreadID = GetCurrentThreadId();
        T* retVal;
        if(m_map.find(nThreadID)!=m_map.end())
        {
            retVal = m_map[nThreadID];
            m_map.erase(nThreadID);
            delete retVal;
        }
    }
private:
    static CritSect m_cs;
    static std::map<DWORD, T*> m_map;
};
template<typename T> CritSect PerThreadSingleton<T>::m_cs;
template<typename T> std::map<DWORD, T*> PerThreadSingleton<T>::m_map;

Example on how to use the C++ Per Thread Singleton

int main()
{
    PerThreadSingleton<classA>::Instance()->DoWork;

    // delete the instance after use.
    PerThreadSingleton<classA>::DeleteInstance();
    return 0;
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read