diff options
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 4 | ||||
-rw-r--r-- | indra/llcommon/llmemory.h | 5 | ||||
-rw-r--r-- | indra/llcommon/llmutex.cpp | 229 | ||||
-rw-r--r-- | indra/llcommon/llmutex.h | 168 | ||||
-rw-r--r-- | indra/llcommon/llthread.cpp | 214 | ||||
-rw-r--r-- | indra/llcommon/llthread.h | 171 | ||||
-rw-r--r-- | indra/llcommon/llthreadlocalptr.h | 141 | ||||
-rw-r--r-- | indra/llcommon/lltrace.cpp | 95 | ||||
-rw-r--r-- | indra/llcommon/lltrace.h | 265 |
9 files changed, 826 insertions, 466 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1812e39b36..eec2695dde 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -74,6 +74,7 @@ set(llcommon_SOURCE_FILES llmetrics.cpp llmetricperformancetester.cpp llmortician.cpp + llmutex.cpp lloptioninterface.cpp llptrto.cpp llprocess.cpp @@ -99,6 +100,7 @@ set(llcommon_SOURCE_FILES llthread.cpp llthreadsafequeue.cpp lltimer.cpp + lltrace.cpp lluri.cpp lluuid.cpp llworkerthread.cpp @@ -197,6 +199,7 @@ set(llcommon_HEADER_FILES llmetrics.h llmetricperformancetester.h llmortician.h + llmutex.h llnametable.h lloptioninterface.h llpointer.h @@ -237,6 +240,7 @@ set(llcommon_HEADER_FILES llstringtable.h llsys.h llthread.h + llthreadlocalptr.h llthreadsafequeue.h lltimer.h lltrace.h diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 6a2323e7d8..4480e381e8 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -30,6 +30,11 @@ class LLMutex ; +#ifdef LL_WINDOWS +#define LL_ALIGNED(x) __declspec(align(x)) +#else +#define LL_ALIGNED(x) __attribute__ ((aligned (16))) +#endif inline void* ll_aligned_malloc( size_t size, int align ) { void* mem = malloc( size + (align - 1) + sizeof(void*) ); diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp new file mode 100644 index 0000000000..2ce14b3a2e --- /dev/null +++ b/indra/llcommon/llmutex.cpp @@ -0,0 +1,229 @@ +/** + * @file llmutex.cpp + * + * $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$ + */ + +#include "linden_common.h" +#include "llapr.h" + +#include "apr_portable.h" + +#include "llmutex.h" +#include "llthread.h" + +//============================================================================ + +LLMutex::LLMutex(apr_pool_t *poolp) : + mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD) +{ + //if (poolp) + //{ + // mIsLocalPool = FALSE; + // mAPRPoolp = poolp; + //} + //else + { + mIsLocalPool = TRUE; + apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread + } + apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp); +} + + +LLMutex::~LLMutex() +{ +#if MUTEX_DEBUG + //bad assertion, the subclass LLSignal might be "locked", and that's OK + //llassert_always(!isLocked()); // better not be locked! +#endif + apr_thread_mutex_destroy(mAPRMutexp); + mAPRMutexp = NULL; + if (mIsLocalPool) + { + apr_pool_destroy(mAPRPoolp); + } +} + + +void LLMutex::lock() +{ + if(isSelfLocked()) + { //redundant lock + mCount++; + return; + } + + apr_thread_mutex_lock(mAPRMutexp); + +#if MUTEX_DEBUG + // Have to have the lock before we can access the debug info + U32 id = LLThread::currentID(); + if (mIsLocked[id] != FALSE) + llerrs << "Already locked in Thread: " << id << llendl; + mIsLocked[id] = TRUE; +#endif + +#if LL_DARWIN + mLockingThread = LLThread::currentID(); +#else + mLockingThread = LLThread::sThreadIndex; +#endif +} + +void LLMutex::unlock() +{ + if (mCount > 0) + { //not the root unlock + mCount--; + return; + } + +#if MUTEX_DEBUG + // Access the debug info while we have the lock + U32 id = LLThread::currentID(); + if (mIsLocked[id] != TRUE) + llerrs << "Not locked in Thread: " << id << llendl; + mIsLocked[id] = FALSE; +#endif + + mLockingThread = NO_THREAD; + apr_thread_mutex_unlock(mAPRMutexp); +} + +bool LLMutex::isLocked() +{ + apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp); + if (APR_STATUS_IS_EBUSY(status)) + { + return true; + } + else + { + apr_thread_mutex_unlock(mAPRMutexp); + return false; + } +} + +bool LLMutex::isSelfLocked() +{ +#if LL_DARWIN + return mLockingThread == LLThread::currentID(); +#else + return mLockingThread == LLThread::sThreadIndex; +#endif +} + +U32 LLMutex::lockingThread() const +{ + return mLockingThread; +} + +//============================================================================ + +LLCondition::LLCondition(apr_pool_t *poolp) : + LLMutex(poolp) +{ + // base class (LLMutex) has already ensured that mAPRPoolp is set up. + + apr_thread_cond_create(&mAPRCondp, mAPRPoolp); +} + + +LLCondition::~LLCondition() +{ + apr_thread_cond_destroy(mAPRCondp); + mAPRCondp = NULL; +} + + +void LLCondition::wait() +{ + if (!isLocked()) + { //mAPRMutexp MUST be locked before calling apr_thread_cond_wait + apr_thread_mutex_lock(mAPRMutexp); +#if MUTEX_DEBUG + // avoid asserts on destruction in non-release builds + U32 id = LLThread::currentID(); + mIsLocked[id] = TRUE; +#endif + } + apr_thread_cond_wait(mAPRCondp, mAPRMutexp); +} + +void LLCondition::signal() +{ + apr_thread_cond_signal(mAPRCondp); +} + +void LLCondition::broadcast() +{ + apr_thread_cond_broadcast(mAPRCondp); +} + + +//============================================================================ + +//---------------------------------------------------------------------------- + +//static +LLMutex* LLThreadSafeRefCount::sMutex = 0; + +//static +void LLThreadSafeRefCount::initThreadSafeRefCount() +{ + if (!sMutex) + { + sMutex = new LLMutex(0); + } +} + +//static +void LLThreadSafeRefCount::cleanupThreadSafeRefCount() +{ + delete sMutex; + sMutex = NULL; +} + + +//---------------------------------------------------------------------------- + +LLThreadSafeRefCount::LLThreadSafeRefCount() : +mRef(0) +{ +} + +LLThreadSafeRefCount::~LLThreadSafeRefCount() +{ + if (mRef != 0) + { + llerrs << "deleting non-zero reference" << llendl; + } +} + +//============================================================================ + +LLResponder::~LLResponder() +{ +} + +//============================================================================ diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h new file mode 100644 index 0000000000..bd0a59b577 --- /dev/null +++ b/indra/llcommon/llmutex.h @@ -0,0 +1,168 @@ +/** + * @file llmutex.h + * @brief Base classes for mutex and condition handling. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLMUTEX_H +#define LL_LLMUTEX_H + +#include "llapr.h" +#include "apr_thread_cond.h" + +//============================================================================ + +#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) + +class LL_COMMON_API LLMutex +{ +public: + typedef enum + { + NO_THREAD = 0xFFFFFFFF + } e_locking_thread; + + LLMutex(apr_pool_t *apr_poolp = NULL); // NULL pool constructs a new pool for the mutex + virtual ~LLMutex(); + + void lock(); // blocks + void unlock(); + bool isLocked(); // non-blocking, but does do a lock/unlock so not free + bool isSelfLocked(); //return true if locked in a same thread + U32 lockingThread() const; //get ID of locking thread + +protected: + apr_thread_mutex_t *mAPRMutexp; + mutable U32 mCount; + mutable U32 mLockingThread; + + apr_pool_t *mAPRPoolp; + BOOL mIsLocalPool; + +#if MUTEX_DEBUG + std::map<U32, BOOL> mIsLocked; +#endif +}; + +// Actually a condition/mutex pair (since each condition needs to be associated with a mutex). +class LL_COMMON_API LLCondition : public LLMutex +{ +public: + LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. + ~LLCondition(); + + void wait(); // blocks + void signal(); + void broadcast(); + +protected: + apr_thread_cond_t *mAPRCondp; +}; + +class LLMutexLock +{ +public: + LLMutexLock(LLMutex* mutex) + { + mMutex = mutex; + + if(mMutex) + mMutex->lock(); + } + ~LLMutexLock() + { + if(mMutex) + mMutex->unlock(); + } +private: + LLMutex* mMutex; +}; + + +//============================================================================ + +// see llmemory.h for LLPointer<> definition + +class LL_COMMON_API LLThreadSafeRefCount +{ +public: + static void initThreadSafeRefCount(); // creates sMutex + static void cleanupThreadSafeRefCount(); // destroys sMutex + +private: + static LLMutex* sMutex; + +private: + LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented + LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented + +protected: + virtual ~LLThreadSafeRefCount(); // use unref() + +public: + LLThreadSafeRefCount(); + + void ref() + { + if (sMutex) sMutex->lock(); + mRef++; + if (sMutex) sMutex->unlock(); + } + + S32 unref() + { + llassert(mRef >= 1); + if (sMutex) sMutex->lock(); + S32 res = --mRef; + if (sMutex) sMutex->unlock(); + if (0 == res) + { + delete this; + return 0; + } + return res; + } + S32 getNumRefs() const + { + return mRef; + } + +private: + S32 mRef; +}; + + +//============================================================================ + +// Simple responder for self destructing callbacks +// Pure virtual class +class LL_COMMON_API LLResponder : public LLThreadSafeRefCount +{ +protected: + virtual ~LLResponder(); +public: + virtual void completed(bool success) = 0; +}; + + +#endif // LL_LLTHREAD_H diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index a6ad6b125c..f3ab8aa40c 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -29,6 +29,7 @@ #include "apr_portable.h" #include "llthread.h" +#include "llmutex.h" #include "lltimer.h" @@ -56,12 +57,20 @@ // //---------------------------------------------------------------------------- -#if !LL_DARWIN -U32 ll_thread_local sThreadID = 0; -#endif +#if LL_DARWIN +// statically allocated thread local storage not supported in Darwin executable formats +#elif LL_WINDOWS +U32 __declspec(thread) LLThread::sThreadIndex = 0; +#elif LL_LINUX +U32 __thread LLThread::sThreadID = 0; +#endif U32 LLThread::sIDIter = 0; +LLTrace::MasterThreadTrace gMasterThreadTrace; +LLThreadLocalPtr<LLTrace::ThreadTraceData> LLThread::sTraceData(&gMasterThreadTrace); + + LL_COMMON_API void assert_main_thread() { static U32 s_thread_id = LLThread::currentID(); @@ -78,8 +87,10 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap { LLThread *threadp = (LLThread *)datap; + sTraceData = new LLTrace::SlaveThreadTrace(gMasterThreadTrace); + #if !LL_DARWIN - sThreadID = threadp->mID; + sThreadIndex = threadp->mID; #endif // Run the user supplied function @@ -93,7 +104,6 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap return NULL; } - LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : mPaused(FALSE), mName(name), @@ -301,198 +311,12 @@ void LLThread::wakeLocked() } } -//============================================================================ - -LLMutex::LLMutex(apr_pool_t *poolp) : - mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD) -{ - //if (poolp) - //{ - // mIsLocalPool = FALSE; - // mAPRPoolp = poolp; - //} - //else - { - mIsLocalPool = TRUE; - apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread - } - apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp); -} - - -LLMutex::~LLMutex() -{ -#if MUTEX_DEBUG - //bad assertion, the subclass LLSignal might be "locked", and that's OK - //llassert_always(!isLocked()); // better not be locked! -#endif - apr_thread_mutex_destroy(mAPRMutexp); - mAPRMutexp = NULL; - if (mIsLocalPool) - { - apr_pool_destroy(mAPRPoolp); - } -} - - -void LLMutex::lock() -{ - if(isSelfLocked()) - { //redundant lock - mCount++; - return; - } - - apr_thread_mutex_lock(mAPRMutexp); - -#if MUTEX_DEBUG - // Have to have the lock before we can access the debug info - U32 id = LLThread::currentID(); - if (mIsLocked[id] != FALSE) - llerrs << "Already locked in Thread: " << id << llendl; - mIsLocked[id] = TRUE; -#endif - -#if LL_DARWIN - mLockingThread = LLThread::currentID(); -#else - mLockingThread = sThreadID; -#endif -} - -void LLMutex::unlock() -{ - if (mCount > 0) - { //not the root unlock - mCount--; - return; - } - -#if MUTEX_DEBUG - // Access the debug info while we have the lock - U32 id = LLThread::currentID(); - if (mIsLocked[id] != TRUE) - llerrs << "Not locked in Thread: " << id << llendl; - mIsLocked[id] = FALSE; -#endif - - mLockingThread = NO_THREAD; - apr_thread_mutex_unlock(mAPRMutexp); -} - -bool LLMutex::isLocked() -{ - apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp); - if (APR_STATUS_IS_EBUSY(status)) - { - return true; - } - else - { - apr_thread_mutex_unlock(mAPRMutexp); - return false; - } -} - -bool LLMutex::isSelfLocked() -{ -#if LL_DARWIN - return mLockingThread == LLThread::currentID(); -#else - return mLockingThread == sThreadID; -#endif -} - -U32 LLMutex::lockingThread() const -{ - return mLockingThread; -} - -//============================================================================ - -LLCondition::LLCondition(apr_pool_t *poolp) : - LLMutex(poolp) -{ - // base class (LLMutex) has already ensured that mAPRPoolp is set up. - - apr_thread_cond_create(&mAPRCondp, mAPRPoolp); -} - - -LLCondition::~LLCondition() -{ - apr_thread_cond_destroy(mAPRCondp); - mAPRCondp = NULL; -} - - -void LLCondition::wait() -{ - if (!isLocked()) - { //mAPRMutexp MUST be locked before calling apr_thread_cond_wait - apr_thread_mutex_lock(mAPRMutexp); -#if MUTEX_DEBUG - // avoid asserts on destruction in non-release builds - U32 id = LLThread::currentID(); - mIsLocked[id] = TRUE; -#endif - } - apr_thread_cond_wait(mAPRCondp, mAPRMutexp); -} - -void LLCondition::signal() -{ - apr_thread_cond_signal(mAPRCondp); -} - -void LLCondition::broadcast() -{ - apr_thread_cond_broadcast(mAPRCondp); -} - -//============================================================================ - -//---------------------------------------------------------------------------- - -//static -LLMutex* LLThreadSafeRefCount::sMutex = 0; - -//static -void LLThreadSafeRefCount::initThreadSafeRefCount() -{ - if (!sMutex) - { - sMutex = new LLMutex(0); - } -} - -//static -void LLThreadSafeRefCount::cleanupThreadSafeRefCount() +void LLThread::lockData() { - delete sMutex; - sMutex = NULL; -} - - -//---------------------------------------------------------------------------- - -LLThreadSafeRefCount::LLThreadSafeRefCount() : - mRef(0) -{ -} - -LLThreadSafeRefCount::~LLThreadSafeRefCount() -{ - if (mRef != 0) - { - llerrs << "deleting non-zero reference" << llendl; - } + mRunCondition->lock(); } -//============================================================================ - -LLResponder::~LLResponder() +void LLThread::unlockData() { + mRunCondition->unlock(); } - -//============================================================================ diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b52e70ab2e..e2de4c8b85 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -30,21 +30,21 @@ #include "llapp.h" #include "llapr.h" #include "apr_thread_cond.h" - -class LLThread; -class LLMutex; -class LLCondition; - -#if LL_WINDOWS -#define ll_thread_local __declspec(thread) -#else -#define ll_thread_local __thread -#endif +#include "lltrace.h" +#include "llthreadlocalptr.h" class LL_COMMON_API LLThread { private: + friend class LLMutex; static U32 sIDIter; +#if LL_DARWIN + // statically allocated thread local storage not supported in Darwin executable formats +#elif LL_WINDOWS + static U32 __declspec(thread) LLThread::sThreadIndex; +#elif LL_LINUX + static U32 __thread LLThread::sThreadID ; +#endif public: typedef enum e_thread_status @@ -88,6 +88,8 @@ public: U32 getID() const { return mID; } + static LLTrace::ThreadTraceData* getTraceData() { return sTraceData.get(); } + private: BOOL mPaused; @@ -96,7 +98,7 @@ private: protected: std::string mName; - LLCondition* mRunCondition; + class LLCondition* mRunCondition; apr_thread_t *mAPRThreadp; apr_pool_t *mAPRPoolp; @@ -104,6 +106,8 @@ protected: EThreadStatus mStatus; U32 mID; + static LLThreadLocalPtr<LLTrace::ThreadTraceData> sTraceData; + //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. //Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes. // otherwise it will cause severe memory leaking!!! --bao @@ -135,149 +139,4 @@ protected: //============================================================================ -#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) - -class LL_COMMON_API LLMutex -{ -public: - typedef enum - { - NO_THREAD = 0xFFFFFFFF - } e_locking_thread; - - LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex - virtual ~LLMutex(); - - void lock(); // blocks - void unlock(); - bool isLocked(); // non-blocking, but does do a lock/unlock so not free - bool isSelfLocked(); //return true if locked in a same thread - U32 lockingThread() const; //get ID of locking thread - -protected: - apr_thread_mutex_t *mAPRMutexp; - mutable U32 mCount; - mutable U32 mLockingThread; - - apr_pool_t *mAPRPoolp; - BOOL mIsLocalPool; - -#if MUTEX_DEBUG - std::map<U32, BOOL> mIsLocked; -#endif -}; - -// Actually a condition/mutex pair (since each condition needs to be associated with a mutex). -class LL_COMMON_API LLCondition : public LLMutex -{ -public: - LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. - ~LLCondition(); - - void wait(); // blocks - void signal(); - void broadcast(); - -protected: - apr_thread_cond_t *mAPRCondp; -}; - -class LLMutexLock -{ -public: - LLMutexLock(LLMutex* mutex) - { - mMutex = mutex; - - if(mMutex) - mMutex->lock(); - } - ~LLMutexLock() - { - if(mMutex) - mMutex->unlock(); - } -private: - LLMutex* mMutex; -}; - -//============================================================================ - -void LLThread::lockData() -{ - mRunCondition->lock(); -} - -void LLThread::unlockData() -{ - mRunCondition->unlock(); -} - - -//============================================================================ - -// see llmemory.h for LLPointer<> definition - -class LL_COMMON_API LLThreadSafeRefCount -{ -public: - static void initThreadSafeRefCount(); // creates sMutex - static void cleanupThreadSafeRefCount(); // destroys sMutex - -private: - static LLMutex* sMutex; - -private: - LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented - LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented - -protected: - virtual ~LLThreadSafeRefCount(); // use unref() - -public: - LLThreadSafeRefCount(); - - void ref() - { - if (sMutex) sMutex->lock(); - mRef++; - if (sMutex) sMutex->unlock(); - } - - S32 unref() - { - llassert(mRef >= 1); - if (sMutex) sMutex->lock(); - S32 res = --mRef; - if (sMutex) sMutex->unlock(); - if (0 == res) - { - delete this; - return 0; - } - return res; - } - S32 getNumRefs() const - { - return mRef; - } - -private: - S32 mRef; -}; - -//============================================================================ - -// Simple responder for self destructing callbacks -// Pure virtual class -class LL_COMMON_API LLResponder : public LLThreadSafeRefCount -{ -protected: - virtual ~LLResponder(); -public: - virtual void completed(bool success) = 0; -}; - -//============================================================================ - #endif // LL_LLTHREAD_H diff --git a/indra/llcommon/llthreadlocalptr.h b/indra/llcommon/llthreadlocalptr.h new file mode 100644 index 0000000000..f02f4849ca --- /dev/null +++ b/indra/llcommon/llthreadlocalptr.h @@ -0,0 +1,141 @@ +/** + * @file llthreadlocalptr.h + * @brief manage thread local storage through non-copyable pointer + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLTHREAD_LOCAL_PTR_H +#define LL_LLTHREAD_LOCAL_PTR_H + +#include "llapr.h" + +template <typename T> +class LLThreadLocalPtr +{ +public: + LLThreadLocalPtr(T* value = NULL, apr_pool_t* pool = NULL) + { + apr_status_t result = apr_threadkey_private_create(&mThreadKey, cleanup, pool); + if (result != APR_SUCCESS) + { + ll_apr_warn_status(result); + llerrs << "Failed to allocate thread local data" << llendl; + } + set(value); + } + + + ~LLThreadLocalPtr() + { + apr_status_t result = apr_threadkey_private_delete(mThreadKey); + if (result != APR_SUCCESS) + { + ll_apr_warn_status(result); + llerrs << "Failed to delete thread local data" << llendl; + } + } + + T* operator -> () + { + return get(); + } + + const T* operator -> () const + { + return get(); + } + + T& operator*() + { + return *get(); + } + + const T& operator*() const + { + return *get(); + } + + LLThreadLocalPtr<T>& operator = (T* value) + { + set(value); + return *this; + } + + void copyFrom(const LLThreadLocalPtr<T>& other) + { + set(other.get()); + } + + LL_FORCE_INLINE void set(T* value) + { + apr_status_t result = apr_threadkey_private_set((void*)value, mThreadKey); + if (result != APR_SUCCESS) + { + ll_apr_warn_status(result); + llerrs << "Failed to set thread local data" << llendl; + } + } + + LL_FORCE_INLINE T* get() + { + T* ptr; + //apr_status_t result = + apr_threadkey_private_get((void**)&ptr, mThreadKey); + //if (result != APR_SUCCESS) + //{ + // ll_apr_warn_status(s); + // llerrs << "Failed to get thread local data" << llendl; + //} + return ptr; + } + + LL_FORCE_INLINE const T* get() const + { + T* ptr; + //apr_status_t result = + apr_threadkey_private_get((void**)&ptr, mThreadKey); + //if (result != APR_SUCCESS) + //{ + // ll_apr_warn_status(s); + // llerrs << "Failed to get thread local data" << llendl; + //} + return ptr; + } + + +private: + static void cleanup(void* ptr) + { + delete reinterpret_cast<T*>(ptr); + } + + LLThreadLocalPtr(const LLThreadLocalPtr<T>& other) + { + // do not copy construct + llassert(false); + } + + apr_threadkey_t* mThreadKey; +}; + +#endif // LL_LLTHREAD_LOCAL_PTR_H diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp new file mode 100644 index 0000000000..037c52f8c1 --- /dev/null +++ b/indra/llcommon/lltrace.cpp @@ -0,0 +1,95 @@ +/** + * @file lltrace.cpp + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#include "linden_common.h" + +#include "lltrace.h" +#include "llthread.h" + +namespace LLTrace +{ + +BlockTimer::Recorder::StackEntry BlockTimer::sCurRecorder; +LLThreadLocalPtr<ThreadTraceData> ThreadTraceData::sCurThreadTrace; + +/////////////////////////////////////////////////////////////////////// +// Sampler +/////////////////////////////////////////////////////////////////////// + +void Sampler::stop() +{ + getThreadTrace()->deactivate(this); +} + +void Sampler::resume() +{ + getThreadTrace()->activate(this); +} + +class ThreadTraceData* Sampler::getThreadTrace() +{ + return LLThread::getTraceData(); +} + +/////////////////////////////////////////////////////////////////////// +// MasterThreadTrace +/////////////////////////////////////////////////////////////////////// + +void MasterThreadTrace::pullFromWorkerThreads() +{ + LLMutexLock lock(&mSlaveListMutex); + + for (worker_thread_trace_list_t::iterator it = mSlaveThreadTraces.begin(), end_it = mSlaveThreadTraces.end(); + it != end_it; + ++it) + { + it->mWorkerTrace->mSharedData.copyTo(it->mSamplerStorage); + } +} + +void MasterThreadTrace::addSlaveThread( class SlaveThreadTrace* child ) +{ + LLMutexLock lock(&mSlaveListMutex); + + mSlaveThreadTraces.push_back(WorkerThreadTraceProxy(child)); +} + +void MasterThreadTrace::removeSlaveThread( class SlaveThreadTrace* child ) +{ + LLMutexLock lock(&mSlaveListMutex); + + for (worker_thread_trace_list_t::iterator it = mSlaveThreadTraces.begin(), end_it = mSlaveThreadTraces.end(); + it != end_it; + ++it) + { + if (it->mWorkerTrace == child) + { + mSlaveThreadTraces.erase(it); + break; + } + } +} + +} diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h index 7da182df1e..401ddfd6f3 100644 --- a/indra/llcommon/lltrace.h +++ b/indra/llcommon/lltrace.h @@ -30,25 +30,27 @@ #include "stdtypes.h" #include "llpreprocessor.h" -#include "llthread.h" +#include "llmutex.h" +#include "llmemory.h" +#include "llthreadlocalptr.h" #include <list> +#define TOKEN_PASTE_ACTUAL(x, y) x##y +#define TOKEN_PASTE(x, y) TOKEN_PASTE_ACTUAL(x, y) +#define RECORD_BLOCK_TIME(block_timer) LLTrace::BlockTimer::Recorder TOKEN_PASTE(block_time_recorder, __COUNTER__)(block_timer); + namespace LLTrace { - // one per thread per type template<typename ACCUMULATOR> - struct AccumulatorBuffer : public AccumulatorBufferBase + class AccumulatorBuffer { - ACCUMULATOR* mStorage; - size_t mStorageSize; - size_t mNextStorageSlot; - static S32 sStorageKey; // key used to access thread local storage pointer to accumulator values - + static const U32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 64; + public: AccumulatorBuffer() : mStorageSize(64), - mStorage(new ACCUMULATOR[64]), + mStorage(new ACCUMULATOR[DEFAULT_ACCUMULATOR_BUFFER_SIZE]), mNextStorageSlot(0) {} @@ -56,12 +58,13 @@ namespace LLTrace : mStorageSize(other.mStorageSize), mStorage(new ACCUMULATOR[other.mStorageSize]), mNextStorageSlot(other.mNextStorageSlot) - { + {} + LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index) + { + return mStorage[index]; } - LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index) { return (*mStorage)[index]; } - void mergeFrom(const AccumulatorBuffer<ACCUMULATOR>& other) { llassert(mNextStorageSlot == other.mNextStorageSlot); @@ -72,7 +75,7 @@ namespace LLTrace } } - void copyFrom(const AccumulatorBuffer<Accumulator>& other) + void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other) { for (size_t i = 0; i < mNextStorageSlot; i++) { @@ -90,7 +93,12 @@ namespace LLTrace void makePrimary() { - //TODO: use sStorageKey to set mStorage as active buffer + sPrimaryStorage = mStorage; + } + + LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage() + { + return sPrimaryStorage.get(); } // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned @@ -101,14 +109,29 @@ namespace LLTrace { size_t new_size = mStorageSize + (mStorageSize >> 2); delete [] mStorage; - mStorage = new mStorage(new_size); + mStorage = new ACCUMULATOR[new_size]; mStorageSize = new_size; } llassert(next_slot < mStorageSize); return next_slot; } + + private: + ACCUMULATOR* mStorage; + size_t mStorageSize; + size_t mNextStorageSlot; + static LLThreadLocalPtr<ACCUMULATOR> sPrimaryStorage; + }; + template<typename ACCUMULATOR> LLThreadLocalPtr<ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>::sPrimaryStorage; + + template<typename ACCUMULATOR> + class PrimaryAccumulatorBuffer : public AccumulatorBuffer<ACCUMULATOR + { + PrimaryAccumulatorBuffer() + { + makePrimary(); + } }; - template<typename ACCUMULATOR> S32 AccumulatorBuffer<ACCUMULATOR>::sStorageKey; template<typename ACCUMULATOR> class Trace @@ -117,32 +140,34 @@ namespace LLTrace Trace(const std::string& name) : mName(name) { - mAccumulatorIndex = sAccumulatorBuffer.reserveSlot(); + mAccumulatorIndex = getPrimaryBuffer().reserveSlot(); } LL_FORCE_INLINE ACCUMULATOR& getAccumulator() { - return sAccumulatorBuffer[mAccumulatorIndex]; + return AccumulatorBuffer<ACCUMULATOR>::getPrimaryStorage()[mAccumulatorIndex]; + } + + static PrimaryAccumulatorBuffer& getPrimaryBuffer() + { + static PrimaryAccumulatorBuffer sBuffer; + return sBuffer; } private: std::string mName; size_t mAccumulatorIndex; - - // this needs to be thread local - static AccumulatorBuffer<ACCUMULATOR> sAccumulatorBuffer; }; - template<typename ACCUMULATOR> std::vector<ACCUMULATOR> Trace<ACCUMULATOR>::sAccumulatorBuffer; template<typename T> class StatAccumulator { public: StatAccumulator() - : mSum(), - mMin(), - mMax(), + : mSum(0), + mMin(0), + mMax(0), mNumSamples(0) {} @@ -160,7 +185,7 @@ namespace LLTrace } } - void mergeFrom(const Stat<T>& other) + void mergeFrom(const StatAccumulator<T>& other) { mSum += other.mSum; if (other.mMin < mMin) @@ -318,21 +343,13 @@ namespace LLTrace static Recorder::StackEntry sCurRecorder; }; - BlockTimer::Recorder::StackEntry BlockTimer::sCurRecorder; - class Sampler { public: - Sampler(const Sampler& other) - : mF32Stats(other.mF32Stats), - mS32Stats(other.mS32Stats), - mTimers(other.mTimers) - {} + Sampler() {} + Sampler(const Sampler& other); - ~Sampler() - { - stop(); - } + ~Sampler(); void makePrimary() { @@ -347,17 +364,8 @@ namespace LLTrace resume(); } - void stop() - { - getThreadTracer()->deactivate(this); - } - - void resume() - { - ThreadTracer* thread_data = getThreadTracer(); - thread_data->flushData(); - thread_data->activate(this); - } + void stop(); + void resume(); void mergeFrom(const Sampler& other) { @@ -375,7 +383,7 @@ namespace LLTrace private: // returns data for current thread - struct ThreadTracer* getThreadTracer() { return NULL; } + class ThreadTraceData* getThreadTrace(); AccumulatorBuffer<StatAccumulator<F32> > mF32Stats; AccumulatorBuffer<StatAccumulator<S32> > mS32Stats; @@ -383,39 +391,39 @@ namespace LLTrace AccumulatorBuffer<TimerAccumulator> mTimers; }; - struct ThreadTracer + class ThreadTraceData { - ThreadTracer(LLThread& this_thread, ThreadTracer& parent_data) - : mPrimarySampler(parent_data.mPrimarySampler), - mSharedSampler(parent_data.mSharedSampler), - mSharedSamplerMutex(this_thread.getAPRPool()), - mParent(parent_data) + public: + ThreadTraceData() { mPrimarySampler.makePrimary(); - parent_data.addChildThread(this); } - ~ThreadTracer() + ThreadTraceData(const ThreadTraceData& other) + : mPrimarySampler(other.mPrimarySampler) { - mParent.removeChildThread(this); + mPrimarySampler.makePrimary(); } - void addChildThread(ThreadTracer* child) + void activate(Sampler* sampler) { - mChildThreadTracers.push_back(child); + flushPrimary(); + mActiveSamplers.push_back(sampler); } - void removeChildThread(ThreadTracer* child) + void deactivate(Sampler* sampler) { + sampler->mergeFrom(mPrimarySampler); + // TODO: replace with intrusive list - std::list<ThreadTracer*>::iterator found_it = std::find(mChildThreadTracers.begin(), mChildThreadTracers.end(), child); - if (found_it != mChildThreadTracers.end()) + std::list<Sampler*>::iterator found_it = std::find(mActiveSamplers.begin(), mActiveSamplers.end(), sampler); + if (found_it != mActiveSamplers.end()) { - mChildThreadTracers.erase(found_it); + mActiveSamplers.erase(found_it); } } - void flushData() + void flushPrimary() { for (std::list<Sampler*>::iterator it = mActiveSamplers.begin(), end_it = mActiveSamplers.end(); it != end_it; @@ -426,56 +434,96 @@ namespace LLTrace mPrimarySampler.reset(); } - void activate(Sampler* sampler) + Sampler* getPrimarySampler() { return &mPrimarySampler; } + protected: + Sampler mPrimarySampler; + std::list<Sampler*> mActiveSamplers; + static LLThreadLocalPtr<ThreadTraceData> sCurThreadTrace; + }; + + class MasterThreadTrace : public ThreadTraceData + { + public: + MasterThreadTrace() + {} + + void addSlaveThread(class SlaveThreadTrace* child); + void removeSlaveThread(class SlaveThreadTrace* child); + + // call this periodically to gather stats data from worker threads + void pullFromWorkerThreads(); + + private: + struct WorkerThreadTraceProxy { - mActiveSamplers.push_back(sampler); + WorkerThreadTraceProxy(class SlaveThreadTrace* trace) + : mWorkerTrace(trace) + {} + class SlaveThreadTrace* mWorkerTrace; + Sampler mSamplerStorage; + }; + typedef std::list<WorkerThreadTraceProxy> worker_thread_trace_list_t; + + worker_thread_trace_list_t mSlaveThreadTraces; + LLMutex mSlaveListMutex; + }; + + class SlaveThreadTrace : public ThreadTraceData + { + public: + explicit + SlaveThreadTrace(MasterThreadTrace& master_trace) + : mMaster(master_trace), + ThreadTraceData(master_trace), + mSharedData(mPrimarySampler) + { + master_trace.addSlaveThread(this); } - void deactivate(Sampler* sampler) + ~SlaveThreadTrace() { - // TODO: replace with intrusive list - std::list<Sampler*>::iterator found_it = std::find(mActiveSamplers.begin(), mActiveSamplers.end(), sampler); - if (found_it != mActiveSamplers.end()) - { - mActiveSamplers.erase(found_it); - } + mMaster.removeSlaveThread(this); } - - // call this periodically to gather stats data in parent thread - void publishToParent() + + // call this periodically to gather stats data for master thread to consume + void pushToParent() { - mSharedSamplerMutex.lock(); - { - mSharedSampler.mergeFrom(mPrimarySampler); - } - mSharedSamplerMutex.unlock(); + mSharedData.copyFrom(mPrimarySampler); } - // call this periodically to gather stats data from children - void gatherChildData() + MasterThreadTrace& mMaster; + + // this data is accessed by other threads, so give it a 64 byte alignment + // to avoid false sharing on most x86 processors + LL_ALIGNED(64) class SharedData { - for (std::list<ThreadTracer*>::iterator child_it = mChildThreadTracers.begin(), end_it = mChildThreadTracers.end(); - child_it != end_it; - ++child_it) + public: + explicit + SharedData(const Sampler& other_sampler) + : mSampler(other_sampler) + {} + + void copyFrom(Sampler& source) { - (*child_it)->mSharedSamplerMutex.lock(); - { - //TODO for now, just aggregate, later keep track of thread data independently - mPrimarySampler.mergeFrom((*child_it)->mSharedSampler); + LLMutexLock lock(&mSamplerMutex); + { + mSampler.mergeFrom(source); } - (*child_it)->mSharedSamplerMutex.unlock(); } - } - Sampler mPrimarySampler; - - ThreadTracer& mParent; - std::list<Sampler*> mActiveSamplers; - std::list<ThreadTracer*> mChildThreadTracers; - - // TODO: add unused space here to avoid false sharing? - LLMutex mSharedSamplerMutex; - Sampler mSharedSampler; + void copyTo(Sampler& sink) + { + LLMutexLock lock(&mSamplerMutex); + { + sink.mergeFrom(mSampler); + } + } + private: + // add a cache line's worth of unused space to avoid any potential of false sharing + LLMutex mSamplerMutex; + Sampler mSampler; + }; + SharedData mSharedData; }; @@ -486,19 +534,6 @@ namespace LLTrace void stop() {} void resume() {} }; - - class Sampler - { - public: - void start() {} - void stop() {} - void resume() {} - }; - } -#define TOKEN_PASTE_ACTUAL(x, y) x##y -#define TOKEN_PASTE(x, y) TOKEN_PASTE_ACTUAL(x, y) -#define RECORD_BLOCK_TIME(block_timer) LLTrace::BlockTimer::Recorder TOKEN_PASTE(block_time_recorder, __COUNTER__)(block_timer); - #endif // LL_LLTRACE_H |