/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Defines thread-local storage and related concepts in a platform-independent // and thread-safe manner. // // As of this writing (10/2003), documentation concerning thread-local // storage implementations under GCC, pthreads, and MSVC/Windows can be found at: // http://gcc.gnu.org/onlinedocs/gcc-3.3.2/gcc/Thread-Local.html#Thread-Local // http://java.icmc.sc.usp.br/library/books/ibm_pthreads/users-33.htm#324811 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_Thread_Local_Storage_.28.TLS.29.asp ///////////////////////////////////////////////////////////////////////////// #ifndef EATHREAD_EATHREAD_STORAGE_H #define EATHREAD_EATHREAD_STORAGE_H #include EA_DISABLE_VC_WARNING(4574) #include EA_RESTORE_VC_WARNING() #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif namespace EA { namespace Thread { ///////////////////////////////////////////////////////////////////////// /// EA_THREAD_LOCAL /// /// Documentation (partially culled from online information): /// Thread Local Storage (a.k.a. TLS and Thread Specific Storage) is a /// mechanism by which each thread in a multithreaded process allocates /// storage for thread-specific data. In standard multithreaded programs, /// data is shared among all threads of a given process, whereas thread /// local storage is the mechanism for allocating per-thread data. /// /// The EA_THREAD_LOCAL specifier may be used alone, with the extern or /// static specifiers, but with no other storage class specifier. /// When used with extern or static, EA_THREAD_LOCAL must appear /// immediately after the other storage class specifier. /// /// The EA_THREAD_LOCAL specifier may be applied to any global, file-scoped /// static, function-scoped static, or static data member of a class. /// It may not be applied to block-scoped automatic or non-static data member. /// /// When the address-of operator is applied to a thread-local variable, /// it is evaluated at run-time and returns the address of the current /// thread's instance of that variable. An address so obtained may be used /// by any thread. When a thread terminates, any pointers to thread-local /// variables in that thread become invalid. /// /// No static initialization may refer to the address of a thread-local variable. /// In C++, if an initializer is present for a thread-local variable, /// it must be a constant-expression, as defined in 5.19.2 of the ANSI/ISO C++ standard. /// /// Windows has special considerations for using thread local storage in a DLL. /// /// Example usage: /// #if defined(EA_THREAD_LOCAL) /// EA_THREAD_LOCAL int n = 0; // OK /// extern EA_THREAD_LOCAL struct Data s; // OK /// static EA_THREAD_LOCAL char* p; // OK /// EA_THREAD_LOCAL int i = sizeof(i); // OK. /// EA_THREAD_LOCAL std::string s("hello"); // Bad -- Can't be used for initialized objects. /// EA_THREAD_LOCAL int Function(); // Bad -- Can't be used as return value. /// void Function(){ EA_THREAD_LOCAL int i = 0; } // Bad -- Can't be used in function. /// void Function(EA_THREAD_LOCAL int i){ } // Bad -- can't be used as argument. /// extern int i; EA_THREAD_LOCAL int i; // Bad -- Declarations differ. /// int EA_THREAD_LOCAL i; // Bad -- Can't be used as a type modifier. /// EA_THREAD_LOCAL int i = i; // Bad -- Can't reference self before initialization. /// #else /// Need to use EA::Thread::ThreadLocalStorage. /// #endif #if !EA_THREADS_AVAILABLE #define EA_THREAD_LOCAL #elif !defined(EA_COMPILER_NO_THREAD_LOCAL) || EA_USE_CPP11_CONCURRENCY #define EA_THREAD_LOCAL thread_local #elif defined(EA_COMPILER_MSVC) // This appears to be supported by VC++, Borland C++. // And it is supported by all compilers for the Windows platform. #define EA_THREAD_LOCAL __declspec(thread) #else // Else don't define it as anything. This will result in a compilation // error reporting the problem. We cannot simply #define away the // EA_THEAD_LOCAL term, as doing so would defeat the purpose of the // specifier. Dynamic thread local storage is a more flexible and // portable solution to the problem. // #define EA_THREAD_LOCAL #endif ///////////////////////////////////////////////////////////////////////// } // namespace Thread } // namespace EA ///////////////////////////////////////////////////////////////////////// /// EAThreadLocalStorageData /// /// This is used internally by class ThreadLocalStorage. /// Todo: Consider moving this declaration into a platform-specific /// header file. /// #if defined(EA_PLATFORM_SONY) #include struct EAThreadLocalStorageData{ ScePthreadKey mKey; // This is usually a pointer. int mResult; // Result of call to scePthreadKeyCreate, so we can know if mKey is valid. }; #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && !defined(EA_PLATFORM_NX) // In this case we will be using pthread_key_create, pthread_key_delete, pthread_getspecific, pthread_setspecific. #include struct EAThreadLocalStorageData{ pthread_key_t mKey; // This is usually a pointer. int mResult; // Result of call to pthread_key_create, so we can know if mKey is valid. }; #elif defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_WINDOWS_PHONE) && !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) // In this case we will be using TlsAlloc, TlsFree, TlsGetValue, TlsSetValue. typedef uint32_t EAThreadLocalStorageData; #elif (!EA_THREADS_AVAILABLE || defined(EA_PLATFORM_CONSOLE)) && !defined(EA_PLATFORM_NX) #include struct EAThreadLocalStorageData { struct ThreadToDataPair { EA::Thread::ThreadUniqueId mThreadID; const void* mpData; }; #ifndef EA_TLS_MAX_COUNT #define EA_TLS_MAX_COUNT 16 // This is the max number of threads that might want to use the given thread-local-storage item. #endif ThreadToDataPair* GetTLSEntry(bool bCreateIfNotFound); ThreadToDataPair mDataArray[EA_TLS_MAX_COUNT]; int mDataArrayCount; }; #else // STL version which uses less memory but uses heap memory. // If you use this version, then you want to make sure your STL is using new/delete // by default and then make sure you are globally mapping new/delete to your // custom allocation system. STLPort, for example, tends to want to use its // own internal allocator which is non-optimal for serious uses. EA_DISABLE_VC_WARNING(4574 4350) #include // Note that this dependency on STL map is only present if you use this pathway, which is disabled by default. EA_RESTORE_VC_WARNING() #include #include struct EAThreadLocalStorageData { EAThreadLocalStorageData() : mThreadToDataMap(NULL) {} ~EAThreadLocalStorageData() { delete mThreadToDataMap; mThreadToDataMap = NULL; } void** GetTLSEntry(bool bCreateIfNotFound); // We allocate this map only when needed // This prevents too early allocations before our allocator initialization std::map *mThreadToDataMap; EA::Thread::Futex mFutex; private: // Disable copy and assignment EAThreadLocalStorageData(const EAThreadLocalStorageData&); EAThreadLocalStorageData operator=(const EAThreadLocalStorageData&); }; #endif ///////////////////////////////////////////////////////////////////////// namespace EA { namespace Thread { ///////////////////////////////////////////////////////////////////////// /// class ThreadLocalStorage /// /// This is a class that lets you store a pointer to data uniquely for /// each thread. It thus allows access to a pointer as if it were local /// but each thread gets its own copy. /// /// The implementation behind this class maps to the PThreads API under /// Unix-like systems, maps to the Windows TLS SPI under Windows, and /// maps to a custom implementation otherwise. The PThreads API has a /// mechanism whereby you can set a callback to execute when a thread /// exits; the callback will call the callback once for each pointer /// that was stored in all thread local storage objects. Due to the /// general weaknesses of the PThread mechanism and due to our interest /// in being as lean as possible, we don't support automatic callbacks /// such as with PThreads. The same effect can be achieved manually /// when needed. /// /// Example usage: /// ThreadLocalStorage tls; /// void* pValue; /// bool bResult; /// /// pValue = tls.GetValue(); // Return value will be NULL. /// bResult = tls.SetValue(NULL); // This is fine and bResult should be true. /// pValue = tls.GetValue(); // Return value will be NULL. /// bResult = tls.SetValue(pSomeObject); // Set thread-specific value to pSomeObject. /// bResult = tls.SetValue(pOtherObject); // Set thread-specific value to pOtherObject. /// pValue = tls.GetValue(); // Return value will be pOtherObject. /// bResult = tls.SetValue(NULL); // This is fine and bResult should be true. /// class EATHREADLIB_API ThreadLocalStorage { public: ThreadLocalStorage(); ~ThreadLocalStorage(); /// GetValue /// Returns the pointer previous stored via GetValue or returns NULL if there /// is not stored value or if the user stored NULL. void* GetValue(); /// SetValue /// Stores a pointer, returns true if the storage was possible. In general, /// the only reason that false would ever be returned is if there wasn't /// sufficient memory remaining for the operation. When a thread exits, /// it should call SetValue(NULL), as there is currently no mechanism to /// automatically detect thread exits on some platforms and thus there is /// no way to automatically clear these values. bool SetValue(const void* pData); /// GetPlatformData /// Returns the platform-specific thread local storage handle for debugging /// uses or other cases whereby special (and non-portable) uses are required. void* GetPlatformData() { return &mTLSData; } protected: EAThreadLocalStorageData mTLSData; private: // Disable copy and assignment ThreadLocalStorage(const ThreadLocalStorage&); ThreadLocalStorage operator=(const ThreadLocalStorage&); }; ///////////////////////////////////////////////////////////////////////// /// ThreadLocalStorageFactory /// /// Implements a factory-based creation and destruction mechanism for class ThreadLocalStorage. /// A primary use of this would be to allow the ThreadLocalStorage implementation to reside in /// a private library while users of the class interact only with the interface /// header and the factory. The factory provides conventional create/destroy /// semantics which use global operator new, but also provides manual construction/ /// destruction semantics so that the user can provide for memory allocation /// and deallocation. class EATHREADLIB_API ThreadLocalStorageFactory { public: static ThreadLocalStorage* CreateThreadLocalStorage(); // Internally implemented as: return new ThreadLocalStorage; static void DestroyThreadLocalStorage(ThreadLocalStorage* pTLS); // Internally implemented as: delete pTLS; static size_t GetThreadLocalStorageSize(); // Internally implemented as: return sizeof(ThreadLocalStorage); static ThreadLocalStorage* ConstructThreadLocalStorage(void* pMemory); // Internally implemented as: return new(pMemory) ThreadLocalStorage; static void DestructThreadLocalStorage(ThreadLocalStorage* pTLS); // Internally implemented as: pTLS->~ThreadLocalStorage(); }; // ThreadLocalPointer // This is a class that adds pointer type awareness to ThreadLocalStorage. // The interface is designed to look like the standard auto_ptr class. // // The following is disabled until we provide a way to enumerate and delete // the pointers when the object goes out of scope or delete the thread-specific // pointer when the thread ends. Both are require before this class fully acts // as one would expect. // //template //class ThreadLocalPointer //{ //public: // T* get() const { return static_cast(mTLS.GetValue()); } // T* operator->() const { return static_cast(mTLS.GetValue()); } // T& operator*() const { return *static_cast(mTLS.GetValue()); } // void reset(T* pNew = 0){ // T* const pTemp = get(); // if(pNew != pTemp){ // delete pTemp; // mTLS.SetValue(pTemp); // } // } // //protected: // ThreadLocalStorage mTLS; // //private: // ThreadLocalPointer(const ThreadLocalPointer&); // const ThreadLocalPointer& operator=(const ThreadLocalPointer&); //}; ///////////////////////////////////////////////////////////////////////// } // namespace Thread } // namespace EA #endif // #ifdef EATHREAD_EATHREAD_STORAGE_H