Streaming Local Singletones

I would like to create a singleton class that is created once in each thread where it is used. I would like to store instance pointers in TLS slots. I came up with the following solution, but I'm not sure if there are any special considerations for multithreaded access to the singelton factory when local thread storage is involved. Perhaps there is a better solution for implementing streaming local singletons.

class ThreadLocalSingleton { static DWORD tlsIndex; public: static ThreadLocalSingleton *getInstance() { ThreadLocalSingleton *instance = static_cast<ThreadLocalSingleton*>(TlsGetValue(tlsIndex)); if (!instance) { instance = new ThreadLocalSingleton(); TlsSetValue(tlsIndex, instance); } return instance; } }; DWORD ThreadLocalSingleton::tlsIndex = TlsAlloc(); 

The Tls * functions, of course, have the value win32, but portability is not a major issue here. Your thoughts on other platforms will continue to be valuable.

Basic Editing . I originally asked about using double check locks in this scenario. However, as noted in DavidK , singletones must be created from each thread.

Two remaining questions:

  • Is it advisable to respond to TlsGetValue / TlsSetValue to ensure that each thread receives one instance and that an instance is created only once for each thread?

  • Is it possible to register a callback that allows me to clear the instance associated with a particular thread when that thread ends?

+4
source share
3 answers

Since your objects are local threads, why do you need to block to protect them at all? Each thread that getsInstance () calls does not depend on any other thread, so why not just check if a singleton exists and create one if necessary? Blocking will be needed only if several threads tried to access the same singleton, which is impossible in your design, since it is higher.

EDIT: Turning to two other questions ... I see no reason why using TlsAlloc / TlsGetValue, etc. will not work as you expected. Since the memory containing the pointer to your singleton is available only for the corresponding thread, there will be no problems with lazy initialization. However, there is no explicit callback interface to clear them.

An obvious solution for this would be to have a method that is called by all of your main stream functions, which clears the created singleton, if any.

If it is very likely that the thread will create a singleton, a simpler template might be to create a singleton at the beginning of the main function of the stream and delete it at the end. You can then use RAII either by creating a singleton on the stack, or by holding it in std :: auto_ptr <> so that it is deleted when the stream ends. (If the flow does not end abnormally, but if it does, all bets will be disconnected and the leaked object will be the least of your problems.) Then you can simply transfer the singleton or save it in TLS or save it in a class member, if most of the functionality flow is in the same class.

+11
source

Check out this document to see why double-check locking doesn't work at all (even if it might work in special cases).

+4
source

We use a class that stores a stream identifier map for data to implement our local stream store. This seems to work very well, then an instance of this class can be placed wherever local thread storage is required. Typically, clients use an instance as a static private field.

Here is a rough code diagram

 template <class T> struct ThreadLocal { T & value() { LockGuard<CriticalSection> lock(m_cs); std::map<int, T>::iterator itr = m_threadMap.find(Thread::getThreadID()); if(itr != m_threadMap.end()) return itr->second; return m_threadMap.insert( std::map<int, T>::value_type(BWThread::getThreadID(), T())) .first->second; } CriticalSection m_cs; std::map<int, T> m_threadMap; }; 

Then it is used as

 class A { // ... void doStuff(); private: static ThreadLocal<Foo> threadLocalFoo; }; ThreadLocal<Foo> A::threadLocalFoo; void A::doStuff() { // ... threadLocalFoo.value().bar(); // ... } 

It is simple and works on any platform where you can get the stream identifier. Please note that the critical section is only used to return / create a link, as soon as you have a link, all calls are outside the critical section.

+1
source

All Articles