/** * @file llthreadlocalstorage.h * @author Richard * @brief Class wrappers for thread local storage * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLTHREADLOCALSTORAGE_H #define LL_LLTHREADLOCALSTORAGE_H #include "llinstancetracker.h" #include "llapr.h" class LLThreadLocalPointerBase : public LLInstanceTracker { public: LLThreadLocalPointerBase() : mThreadKey(NULL) { if (sInitialized) { initStorage(); } } LLThreadLocalPointerBase( const LLThreadLocalPointerBase& other) : mThreadKey(NULL) { if (sInitialized) { initStorage(); } } ~LLThreadLocalPointerBase() { destroyStorage(); } static void initAllThreadLocalStorage(); static void destroyAllThreadLocalStorage(); protected: void set(void* value); LL_FORCE_INLINE void* get() const { // llassert(sInitialized); void* ptr; apr_status_t result = apr_threadkey_private_get(&ptr, mThreadKey); if (result != APR_SUCCESS) { ll_apr_warn_status(result); llerrs << "Failed to get thread local data" << llendl; } return ptr; } void initStorage(); void destroyStorage(); protected: apr_threadkey_t* mThreadKey; static bool sInitialized; }; template class LLThreadLocalPointer : public LLThreadLocalPointerBase { public: LLThreadLocalPointer() {} explicit LLThreadLocalPointer(T* value) { set(value); } LLThreadLocalPointer(const LLThreadLocalPointer& other) : LLThreadLocalPointerBase(other) { set(other.get()); } LL_FORCE_INLINE T* get() const { return (T*)LLThreadLocalPointerBase::get(); } T* operator -> () const { return (T*)get(); } T& operator*() const { return *(T*)get(); } LLThreadLocalPointer& operator = (T* value) { set((void*)value); return *this; } bool operator ==(const T* other) const { if (!sInitialized) return false; return get() == other; } }; template class LLThreadLocalSingleton { typedef enum e_init_state { UNINITIALIZED = 0, CONSTRUCTING, INITIALIZING, INITIALIZED, DELETED } EInitState; public: LLThreadLocalSingleton() {} virtual ~LLThreadLocalSingleton() { #if LL_DARWIN pthread_setspecific(sInstanceKey, NULL); #else sInstance = NULL; #endif setInitState(DELETED); } static void deleteSingleton() { delete getIfExists(); } static DERIVED_TYPE* getInstance() { EInitState init_state = getInitState(); if (init_state == CONSTRUCTING) { llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; } if (init_state == DELETED) { llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; } #if LL_DARWIN createTLSInstance(); #endif if (!getIfExists()) { setInitState(CONSTRUCTING); DERIVED_TYPE* instancep = new DERIVED_TYPE(); #if LL_DARWIN S32 result = pthread_setspecific(sInstanceKey, (void*)instancep); if (result != 0) { llerrs << "Could not set thread local storage" << llendl; } #else sInstance = instancep; #endif setInitState(INITIALIZING); instancep->initSingleton(); setInitState(INITIALIZED); } return getIfExists(); } static DERIVED_TYPE* getIfExists() { #if LL_DARWIN return (DERIVED_TYPE*)pthread_getspecific(sInstanceKey); #else return sInstance; #endif } // Reference version of getInstance() // Preferred over getInstance() as it disallows checking for NULL static DERIVED_TYPE& instance() { return *getInstance(); } // Has this singleton been created uet? // Use this to avoid accessing singletons before the can safely be constructed static bool instanceExists() { return getInitState() == INITIALIZED; } // Has this singleton already been deleted? // Use this to avoid accessing singletons from a static object's destructor static bool destroyed() { return getInitState() == DELETED; } private: #if LL_DARWIN static void createTLSInitState() { static S32 key_created = pthread_key_create(&sInitStateKey, NULL); if (key_created != 0) { llerrs << "Could not create thread local storage" << llendl; } } static void createTLSInstance() { static S32 key_created = pthread_key_create(&sInstanceKey, NULL); if (key_created != 0) { llerrs << "Could not create thread local storage" << llendl; } } #endif static EInitState getInitState() { #if LL_DARWIN createTLSInitState(); return (EInitState)(int)pthread_getspecific(sInitStateKey); #else return sInitState; #endif } static void setInitState(EInitState state) { #if LL_DARWIN createTLSInitState(); pthread_setspecific(sInitStateKey, (void*)state); #else sInitState = state; #endif } LLThreadLocalSingleton(const LLThreadLocalSingleton& other); virtual void initSingleton() {} #ifdef LL_WINDOWS static __declspec(thread) DERIVED_TYPE* sInstance; static __declspec(thread) EInitState sInitState; #elif LL_LINUX static __thread DERIVED_TYPE* sInstance; static __thread EInitState sInitState; #elif LL_DARWIN static pthread_key_t sInstanceKey; static pthread_key_t sInitStateKey; #endif }; #if LL_WINDOWS template __declspec(thread) DERIVED_TYPE* LLThreadLocalSingleton::sInstance = NULL; template __declspec(thread) typename LLThreadLocalSingleton::EInitState LLThreadLocalSingleton::sInitState = LLThreadLocalSingleton::UNINITIALIZED; #elif LL_LINUX template __thread DERIVED_TYPE* LLThreadLocalSingleton::sInstance = NULL; template __thread typename LLThreadLocalSingleton::EInitState LLThreadLocalSingleton::sInitState = LLThreadLocalSingleton::UNINITIALIZED; #elif LL_DARWIN template pthread_key_t LLThreadLocalSingleton::sInstanceKey; template pthread_key_t LLThreadLocalSingleton::sInitStateKey; #endif template class LLThreadLocalSingletonPointer { public: void operator =(DERIVED_TYPE* value) { setInstance(value); } LL_FORCE_INLINE static DERIVED_TYPE* getInstance() { #if LL_DARWIN createTLSKey(); return (DERIVED_TYPE*)pthread_getspecific(sInstanceKey); #else return sInstance; #endif } LL_FORCE_INLINE static void setInstance(DERIVED_TYPE* instance) { #if LL_DARWIN createTLSKey(); pthread_setspecific(sInstanceKey, (void*)instance); #else sInstance = instance; #endif } private: #if LL_WINDOWS static __declspec(thread) DERIVED_TYPE* sInstance; #elif LL_LINUX static __thread DERIVED_TYPE* sInstance; #elif LL_DARWIN static void TLSError() { llerrs << "Could not create thread local storage" << llendl; } static void createTLSKey() { static S32 key_created = pthread_key_create(&sInstanceKey, NULL); if (key_created != 0) { llerrs << "Could not create thread local storage" << llendl; } } static pthread_key_t sInstanceKey; #endif }; #if LL_WINDOWS template __declspec(thread) DERIVED_TYPE* LLThreadLocalSingletonPointer::sInstance = NULL; #elif LL_LINUX template __thread DERIVED_TYPE* LLThreadLocalSingletonPointer::sInstance = NULL; #elif LL_DARWIN template pthread_key_t LLThreadLocalSingletonPointer::sInstanceKey; #endif #endif // LL_LLTHREADLOCALSTORAGE_H