diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 3 | ||||
-rw-r--r-- | indra/llcommon/llapp.cpp | 4 | ||||
-rw-r--r-- | indra/llcommon/llapr.cpp | 445 | ||||
-rw-r--r-- | indra/llcommon/llapr.h | 107 | ||||
-rw-r--r-- | indra/llcommon/llaprpool.cpp | 202 | ||||
-rw-r--r-- | indra/llcommon/llaprpool.h | 256 | ||||
-rw-r--r-- | indra/llcommon/llcommon.cpp | 13 | ||||
-rw-r--r-- | indra/llcommon/llcommon.h | 2 | ||||
-rw-r--r-- | indra/llcommon/llerror.cpp | 3 | ||||
-rw-r--r-- | indra/llcommon/llerror.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llfixedbuffer.cpp | 3 | ||||
-rw-r--r-- | indra/llcommon/llscopedvolatileaprpool.h | 52 | ||||
-rw-r--r-- | indra/llcommon/llthread.cpp | 151 | ||||
-rw-r--r-- | indra/llcommon/llthread.h | 124 | ||||
-rw-r--r-- | indra/llcommon/llthreadsafequeue.cpp | 15 | ||||
-rw-r--r-- | indra/llcommon/llthreadsafequeue.h | 16 | ||||
-rw-r--r-- | indra/llcommon/llworkerthread.cpp | 8 | ||||
-rw-r--r-- | indra/llcommon/llworkerthread.h | 3 |
18 files changed, 793 insertions, 615 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9342a22d46..d5b0a67533 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -32,6 +32,7 @@ set(llcommon_SOURCE_FILES llallocator_heap_profile.cpp llapp.cpp llapr.cpp + llaprpool.cpp llassettype.cpp llavatarname.cpp llbase32.cpp @@ -80,6 +81,7 @@ set(llcommon_SOURCE_FILES llrand.cpp llrefcount.cpp llrun.cpp + llscopedvolatileaprpool.h llsd.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -121,6 +123,7 @@ set(llcommon_HEADER_FILES llavatarname.h llapp.h llapr.h + llaprpool.h llassettype.h llassoclist.h llavatarconstants.h diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 39daefd1ad..e5bd2bfa3d 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -136,10 +136,6 @@ void LLApp::commonCtor() mOptions.append(sd); } - // Make sure we clean up APR when we exit - // Don't need to do this if we're cleaning up APR in the destructor - //atexit(ll_cleanup_apr); - // Set the application to this instance. sApplication = this; diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index d1c44c9403..1e4a51102e 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -29,212 +29,8 @@ #include "linden_common.h" #include "llapr.h" #include "apr_dso.h" +#include "llscopedvolatileaprpool.h" -apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool -LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool. -apr_thread_mutex_t *gLogMutexp = NULL; -apr_thread_mutex_t *gCallStacksLogMutexp = NULL; - -const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool - -void ll_init_apr() -{ - if (!gAPRPoolp) - { - // Initialize APR and create the global pool - apr_initialize(); - apr_pool_create(&gAPRPoolp, NULL); - - // Initialize the logging mutex - apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp); - apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp); - } - - if(!LLAPRFile::sAPRFilePoolp) - { - LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ; - } -} - - -void ll_cleanup_apr() -{ - LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL; - - if (gLogMutexp) - { - // Clean up the logging mutex - - // All other threads NEED to be done before we clean up APR, so this is okay. - apr_thread_mutex_destroy(gLogMutexp); - gLogMutexp = NULL; - } - if (gCallStacksLogMutexp) - { - // Clean up the logging mutex - - // All other threads NEED to be done before we clean up APR, so this is okay. - apr_thread_mutex_destroy(gCallStacksLogMutexp); - gCallStacksLogMutexp = NULL; - } - if (gAPRPoolp) - { - apr_pool_destroy(gAPRPoolp); - gAPRPoolp = NULL; - } - if (LLAPRFile::sAPRFilePoolp) - { - delete LLAPRFile::sAPRFilePoolp ; - LLAPRFile::sAPRFilePoolp = NULL ; - } - apr_terminate(); -} - -// -// -//LLAPRPool -// -LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag) - : mParent(parent), - mReleasePoolFlag(releasePoolFlag), - mMaxSize(size), - mPool(NULL) -{ - createAPRPool() ; -} - -LLAPRPool::~LLAPRPool() -{ - releaseAPRPool() ; -} - -void LLAPRPool::createAPRPool() -{ - if(mPool) - { - return ; - } - - mStatus = apr_pool_create(&mPool, mParent); - ll_apr_warn_status(mStatus) ; - - if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes. - { - apr_allocator_t *allocator = apr_pool_allocator_get(mPool); - if (allocator) - { - apr_allocator_max_free_set(allocator, mMaxSize) ; - } - } -} - -void LLAPRPool::releaseAPRPool() -{ - if(!mPool) - { - return ; - } - - if(!mParent || mReleasePoolFlag) - { - apr_pool_destroy(mPool) ; - mPool = NULL ; - } -} - -//virtual -apr_pool_t* LLAPRPool::getAPRPool() -{ - return mPool ; -} - -LLVolatileAPRPool::LLVolatileAPRPool(BOOL is_local, apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag) - : LLAPRPool(parent, size, releasePoolFlag), - mNumActiveRef(0), - mNumTotalRef(0), - mMutexPool(NULL), - mMutexp(NULL) -{ - //create mutex - if(!is_local) //not a local apr_pool, that is: shared by multiple threads. - { - apr_pool_create(&mMutexPool, NULL); // Create a pool for mutex - apr_thread_mutex_create(&mMutexp, APR_THREAD_MUTEX_UNNESTED, mMutexPool); - } -} - -LLVolatileAPRPool::~LLVolatileAPRPool() -{ - //delete mutex - if(mMutexp) - { - apr_thread_mutex_destroy(mMutexp); - apr_pool_destroy(mMutexPool); - } -} - -// -//define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool(). -// -//virtual -apr_pool_t* LLVolatileAPRPool::getAPRPool() -{ - return LLVolatileAPRPool::getVolatileAPRPool() ; -} - -apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool() -{ - LLScopedLock lock(mMutexp) ; - - mNumTotalRef++ ; - mNumActiveRef++ ; - - if(!mPool) - { - createAPRPool() ; - } - - return mPool ; -} - -void LLVolatileAPRPool::clearVolatileAPRPool() -{ - LLScopedLock lock(mMutexp) ; - - if(mNumActiveRef > 0) - { - mNumActiveRef--; - if(mNumActiveRef < 1) - { - if(isFull()) - { - mNumTotalRef = 0 ; - - //destroy the apr_pool. - releaseAPRPool() ; - } - else - { - //This does not actually free the memory, - //it just allows the pool to re-use this memory for the next allocation. - apr_pool_clear(mPool) ; - } - } - } - else - { - llassert_always(mNumActiveRef > 0) ; - } - - //paranoia check if the pool is jammed. - //will remove the check before going to release. - llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ; -} - -BOOL LLVolatileAPRPool::isFull() -{ - return mNumTotalRef > FULL_VOLATILE_APR_POOL ; -} //--------------------------------------------------------------------- // // LLScopedLock @@ -313,15 +109,17 @@ void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle) // LLAPRFile::LLAPRFile() : mFile(NULL), - mCurrentFilePoolp(NULL) + mVolatileFilePoolp(NULL), + mRegularFilePoolp(NULL) { } -LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool) +LLAPRFile::LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep, access_t access_type) : mFile(NULL), - mCurrentFilePoolp(NULL) + mVolatileFilePoolp(NULL), + mRegularFilePoolp(NULL) { - open(filename, flags, pool); + open(filename, flags, access_type, sizep); } LLAPRFile::~LLAPRFile() @@ -338,36 +136,58 @@ apr_status_t LLAPRFile::close() mFile = NULL ; } - if(mCurrentFilePoolp) + if (mVolatileFilePoolp) { - mCurrentFilePoolp->clearVolatileAPRPool() ; - mCurrentFilePoolp = NULL ; + mVolatileFilePoolp->clearVolatileAPRPool() ; + mVolatileFilePoolp = NULL ; + } + + if (mRegularFilePoolp) + { + delete mRegularFilePoolp; + mRegularFilePoolp = NULL; } return ret ; } -apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep) +apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep) { - apr_status_t s ; - - //check if already open some file - llassert_always(!mFile) ; - llassert_always(!mCurrentFilePoolp) ; - - apr_pool_t* apr_pool = pool ? pool->getVolatileAPRPool() : NULL ; - s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, getAPRFilePool(apr_pool)); + llassert_always(!mFile); + llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp); - if (s != APR_SUCCESS || !mFile) + apr_status_t status; + { + apr_pool_t* apr_file_open_pool; // The use of apr_pool_t is OK here. + // This is a temporary variable for a pool that is passed directly to apr_file_open below. + if (access_type == short_lived) + { + // Use a "volatile" thread-local pool. + mVolatileFilePoolp = &LLThreadLocalData::tldata().mVolatileAPRPool; + // Access the pool and increment its reference count. + // The reference count of LLVolatileAPRPool objects will be decremented + // again in LLAPRFile::close by calling mVolatileFilePoolp->clearVolatileAPRPool(). + apr_file_open_pool = mVolatileFilePoolp->getVolatileAPRPool(); + } + else + { + mRegularFilePoolp = new LLAPRPool(LLThreadLocalData::tldata().mRootPool); + apr_file_open_pool = (*mRegularFilePoolp)(); + } + status = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_file_open_pool); + } + if (status != APR_SUCCESS || !mFile) { mFile = NULL ; - + close() ; if (sizep) { *sizep = 0; } + return status; } - else if (sizep) + + if (sizep) { S32 file_size = 0; apr_off_t offset = 0; @@ -381,49 +201,7 @@ apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLV *sizep = file_size; } - if(!mCurrentFilePoolp) - { - mCurrentFilePoolp = pool ; - - if(!mFile) - { - close() ; - } - } - - return s ; -} - -//use gAPRPoolp. -apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool) -{ - apr_status_t s; - - //check if already open some file - llassert_always(!mFile) ; - llassert_always(!mCurrentFilePoolp) ; - llassert_always(use_global_pool) ; //be aware of using gAPRPoolp. - - s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp); - if (s != APR_SUCCESS || !mFile) - { - mFile = NULL ; - close() ; - return s; - } - - return s; -} - -apr_pool_t* LLAPRFile::getAPRFilePool(apr_pool_t* pool) -{ - if(!pool) - { - mCurrentFilePoolp = sAPRFilePoolp ; - return mCurrentFilePoolp->getVolatileAPRPool() ; - } - - return pool ; + return status; } // File I/O @@ -482,45 +260,6 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset) // //static -apr_status_t LLAPRFile::close(apr_file_t* file_handle, LLVolatileAPRPool* pool) -{ - apr_status_t ret = APR_SUCCESS ; - if(file_handle) - { - ret = apr_file_close(file_handle); - file_handle = NULL ; - } - - if(pool) - { - pool->clearVolatileAPRPool() ; - } - - return ret ; -} - -//static -apr_file_t* LLAPRFile::open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags) -{ - apr_status_t s; - apr_file_t* file_handle ; - - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - - s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool()); - if (s != APR_SUCCESS || !file_handle) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL; - file_handle = NULL ; - close(file_handle, pool) ; - return NULL; - } - - return file_handle ; -} - -//static S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) { if(!file_handle) @@ -553,13 +292,15 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) } //static -S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) +S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes) { - //***************************************** - apr_file_t* file_handle = open(filename, pool, APR_READ|APR_BINARY); - //***************************************** - if (!file_handle) + apr_file_t* file_handle; + LLScopedVolatileAPRPool pool; + apr_status_t s = apr_file_open(&file_handle, filename.c_str(), APR_READ|APR_BINARY, APR_OS_DEFAULT, pool); + if (s != APR_SUCCESS || !file_handle) { + ll_apr_warn_status(s); + LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL; return 0; } @@ -589,14 +330,13 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb } } - //***************************************** - close(file_handle, pool) ; - //***************************************** + apr_file_close(file_handle); + return (S32)bytes_read; } //static -S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) +S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes) { apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY; if (offset < 0) @@ -605,11 +345,13 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n offset = 0; } - //***************************************** - apr_file_t* file_handle = open(filename, pool, flags); - //***************************************** - if (!file_handle) + apr_file_t* file_handle; + LLScopedVolatileAPRPool pool; + apr_status_t s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool); + if (s != APR_SUCCESS || !file_handle) { + ll_apr_warn_status(s); + LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL; return 0; } @@ -639,21 +381,18 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n } } - //***************************************** - LLAPRFile::close(file_handle, pool); - //***************************************** + apr_file_close(file_handle); return (S32)bytes_written; } //static -bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool) +bool LLAPRFile::remove(const std::string& filename) { apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_file_remove(filename.c_str(), pool->getVolatileAPRPool()); - pool->clearVolatileAPRPool() ; + LLScopedVolatileAPRPool pool; + s = apr_file_remove(filename.c_str(), pool); if (s != APR_SUCCESS) { @@ -665,13 +404,12 @@ bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool) } //static -bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool) +bool LLAPRFile::rename(const std::string& filename, const std::string& newname) { apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_file_rename(filename.c_str(), newname.c_str(), pool->getVolatileAPRPool()); - pool->clearVolatileAPRPool() ; + LLScopedVolatileAPRPool pool; + s = apr_file_rename(filename.c_str(), newname.c_str(), pool); if (s != APR_SUCCESS) { @@ -683,49 +421,44 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname, } //static -bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags) +bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags) { - apr_file_t* apr_file; + apr_file_t* file_handle; apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool()); + LLScopedVolatileAPRPool pool; + s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool); - if (s != APR_SUCCESS || !apr_file) + if (s != APR_SUCCESS || !file_handle) { - pool->clearVolatileAPRPool() ; return false; } else { - apr_file_close(apr_file) ; - pool->clearVolatileAPRPool() ; + apr_file_close(file_handle); return true; } } //static -S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool) +S32 LLAPRFile::size(const std::string& filename) { - apr_file_t* apr_file; + apr_file_t* file_handle; apr_finfo_t info; apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool->getVolatileAPRPool()); + LLScopedVolatileAPRPool pool; + s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool); - if (s != APR_SUCCESS || !apr_file) + if (s != APR_SUCCESS || !file_handle) { - pool->clearVolatileAPRPool() ; - return 0; } else { - apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file); + apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, file_handle); - apr_file_close(apr_file) ; - pool->clearVolatileAPRPool() ; + apr_file_close(file_handle) ; if (s == APR_SUCCESS) { @@ -739,31 +472,29 @@ S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool) } //static -bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool) +bool LLAPRFile::makeDir(const std::string& dirname) { apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool->getVolatileAPRPool()); - pool->clearVolatileAPRPool() ; + LLScopedVolatileAPRPool pool; + s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool); if (s != APR_SUCCESS) { ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL; + LL_WARNS("APR") << " while attempting to make directory: " << dirname << LL_ENDL; return false; } return true; } //static -bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool) +bool LLAPRFile::removeDir(const std::string& dirname) { apr_status_t s; - pool = pool ? pool : LLAPRFile::sAPRFilePoolp ; - s = apr_file_remove(dirname.c_str(), pool->getVolatileAPRPool()); - pool->clearVolatileAPRPool() ; + LLScopedVolatileAPRPool pool; + s = apr_file_remove(dirname.c_str(), pool); if (s != APR_SUCCESS) { diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f..3f846f1314 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -50,71 +50,9 @@ #include "apr_atomic.h" #include "llstring.h" -extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp; -extern apr_thread_mutex_t* gCallStacksLogMutexp; - struct apr_dso_handle_t; - -/** - * @brief initialize the common apr constructs -- apr itself, the - * global pool, and a mutex. - */ -void LL_COMMON_API ll_init_apr(); - -/** - * @brief Cleanup those common apr constructs. - */ -void LL_COMMON_API ll_cleanup_apr(); - -// -//LL apr_pool -//manage apr_pool_t, destroy allocated apr_pool in the destruction function. -// -class LL_COMMON_API LLAPRPool -{ -public: - LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ; - virtual ~LLAPRPool() ; - - virtual apr_pool_t* getAPRPool() ; - apr_status_t getStatus() {return mStatus ; } - -protected: - void releaseAPRPool() ; - void createAPRPool() ; - -protected: - apr_pool_t* mPool ; //pointing to an apr_pool - apr_pool_t* mParent ; //parent pool - apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work. - apr_status_t mStatus ; //status when creating the pool - BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true. -}; - -// -//volatile LL apr_pool -//which clears memory automatically. -//so it can not hold static data or data after memory is cleared -// -class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool -{ -public: - LLVolatileAPRPool(BOOL is_local = TRUE, apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE); - virtual ~LLVolatileAPRPool(); - - /*virtual*/ apr_pool_t* getAPRPool() ; //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool(). - apr_pool_t* getVolatileAPRPool() ; - void clearVolatileAPRPool() ; - - BOOL isFull() ; - -private: - S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool. - S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating. - - apr_thread_mutex_t *mMutexp; - apr_pool_t *mMutexPool; -} ; +class LLAPRPool; +class LLVolatileAPRPool; /** * @class LLScopedLock @@ -205,15 +143,20 @@ class LL_COMMON_API LLAPRFile : boost::noncopyable // make this non copyable since a copy closes the file private: apr_file_t* mFile ; - LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool. + LLVolatileAPRPool* mVolatileFilePoolp; // (Thread local) APR pool currently in use. + LLAPRPool* mRegularFilePoolp; // ...or a regular pool. public: + enum access_t { + long_lived, // Use a global pool for long-lived file accesses. + short_lived // Use a volatile pool for short-lived file accesses. + }; + LLAPRFile() ; - LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL); + LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep = NULL, access_t access_type = short_lived); ~LLAPRFile() ; - - apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL); - apr_status_t open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool); //use gAPRPoolp. + + apr_status_t open(const std::string& filename, apr_int32_t flags, access_t access_type, S32* sizep = NULL); apr_status_t close() ; // Returns actual offset, -1 if seek fails @@ -226,32 +169,24 @@ public: apr_file_t* getFileHandle() {return mFile;} -private: - apr_pool_t* getAPRFilePool(apr_pool_t* pool) ; - // //******************************************************************************************************************************* //static components // -public: - static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist. - private: - static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags); - static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ; static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset); public: // returns false if failure: - static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL); - static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ); - static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); - static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); + static bool remove(const std::string& filename); + static bool rename(const std::string& filename, const std::string& newname); + static bool isExist(const std::string& filename, apr_int32_t flags = APR_READ); + static S32 size(const std::string& filename); + static bool makeDir(const std::string& dirname); + static bool removeDir(const std::string& dirname); // Returns bytes read/written, 0 if read/write fails: - static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); - static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append + static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); + static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); // offset<0 means append //******************************************************************************************************************************* }; @@ -267,6 +202,4 @@ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* han void LL_COMMON_API ll_apr_assert_status(apr_status_t status); void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle); -extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool - #endif // LL_LLAPR_H diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp new file mode 100644 index 0000000000..6f21b61b65 --- /dev/null +++ b/indra/llcommon/llaprpool.cpp @@ -0,0 +1,202 @@ +/** + * @file llaprpool.cpp + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + * + * CHANGELOG + * and additional copyright holders. + * + * 04/04/2010 + * - Initial version, written by Aleric Inglewood @ SL + * + * 10/11/2010 + * - Added APR_HAS_THREADS #if's to allow creation and destruction + * of subpools by threads other than the parent pool owner. + */ + +#include "linden_common.h" + +#include "llerror.h" +#include "llaprpool.h" +#include "llthread.h" + +// Create a subpool from parent. +void LLAPRPool::create(LLAPRPool& parent) +{ + llassert(!mPool); // Must be non-initialized. + mParent = &parent; + if (!mParent) // Using the default parameter? + { + // By default use the root pool of the current thread. + mParent = &LLThreadLocalData::tldata().mRootPool; + } + llassert(mParent->mPool); // Parent must be initialized. +#if APR_HAS_THREADS + // As per the documentation of APR (ie http://apr.apache.org/docs/apr/1.4/apr__pools_8h.html): + // + // Note that most operations on pools are not thread-safe: a single pool should only be + // accessed by a single thread at any given time. The one exception to this rule is creating + // a subpool of a given pool: one or more threads can safely create subpools at the same + // time that another thread accesses the parent pool. + // + // In other words, it's safe for any thread to create a (sub)pool, independent of who + // owns the parent pool. + mOwner = apr_os_thread_current(); +#else + mOwner = mParent->mOwner; + llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); +#endif + apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool); + llassert_always(apr_pool_create_status == APR_SUCCESS); + llassert(mPool); // Initialized. + apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null); +} + +// Destroy the (sub)pool, if any. +void LLAPRPool::destroy(void) +{ + // Only do anything if we are not already (being) destroyed. + if (mPool) + { +#if !APR_HAS_THREADS + // If we are a root pool, then every thread may destruct us: in that case + // we have to assume that no other thread will use this pool concurrently, + // of course. Otherwise, if we are a subpool, only the thread that owns + // the parent may destruct us, since that is the pool that is still alive, + // possibly being used by others and being altered here. + llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current())); +#endif + apr_pool_t* pool = mPool; // The use of apr_pool_t is OK here. + // Temporary store before destroying the pool. + mPool = NULL; // Mark that we are BEING destructed. + apr_pool_cleanup_kill(pool, this, &s_plain_cleanup); + apr_pool_destroy(pool); + } +} + +bool LLAPRPool::parent_is_being_destructed(void) +{ + return mParent && (!mParent->mPool || mParent->parent_is_being_destructed()); +} + +LLAPRInitialization::LLAPRInitialization(void) +{ + static bool apr_initialized = false; + + if (!apr_initialized) + { + apr_initialize(); + } + + apr_initialized = true; +} + +bool LLAPRRootPool::sCountInitialized = false; +apr_uint32_t volatile LLAPRRootPool::sCount; + +apr_thread_mutex_t* gLogMutexp; +apr_thread_mutex_t* gCallStacksLogMutexp; + +LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0) +{ + // sCountInitialized don't need locking because when we get here there is still only a single thread. + if (!sCountInitialized) + { + // Initialize the logging mutex + apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool); + apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool); + + apr_status_t status = apr_atomic_init(mPool); + llassert_always(status == APR_SUCCESS); + apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool. + sCountInitialized = true; + + // Initialize thread-local APR pool support. + // Because this recursively calls LLAPRRootPool::LLAPRRootPool(void) + // it must be done last, so that sCount is already initialized. + LLThreadLocalData::init(); + } + apr_atomic_inc32(&sCount); +} + +LLAPRRootPool::~LLAPRRootPool() +{ + if (!apr_atomic_dec32(&sCount)) + { + // The last pool was destructed. Cleanup remainder of APR. + LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL; + + if (gLogMutexp) + { + // Clean up the logging mutex + + // All other threads NEED to be done before we clean up APR, so this is okay. + apr_thread_mutex_destroy(gLogMutexp); + gLogMutexp = NULL; + } + if (gCallStacksLogMutexp) + { + // Clean up the logging mutex + + // All other threads NEED to be done before we clean up APR, so this is okay. + apr_thread_mutex_destroy(gCallStacksLogMutexp); + gCallStacksLogMutexp = NULL; + } + + // Must destroy ALL, and therefore this last LLAPRRootPool, before terminating APR. + static_cast<LLAPRRootPool*>(this)->destroy(); + + apr_terminate(); + } +} + +//static +// Return a global root pool that is independent of LLThreadLocalData. +// Normally you should NOT use this. Only use for early initialization +// (before main) and deinitialization (after main). +LLAPRRootPool& LLAPRRootPool::get(void) +{ + static LLAPRRootPool global_APRpool(0); + return global_APRpool; +} + +void LLVolatileAPRPool::clearVolatileAPRPool() +{ + llassert_always(mNumActiveRef > 0); + if (--mNumActiveRef == 0) + { + if (isOld()) + { + destroy(); + mNumTotalRef = 0 ; + } + else + { + // This does not actually free the memory, + // it just allows the pool to re-use this memory for the next allocation. + clear(); + } + } + + // Paranoia check if the pool is jammed. + llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ; +} diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h new file mode 100644 index 0000000000..bf4102c584 --- /dev/null +++ b/indra/llcommon/llaprpool.h @@ -0,0 +1,256 @@ +/** + * @file llaprpool.h + * @brief Implementation of LLAPRPool + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + * + * CHANGELOG + * and additional copyright holders. + * + * 04/04/2010 + * - Initial version, written by Aleric Inglewood @ SL + * + * 10/11/2010 + * - Added APR_HAS_THREADS #if's to allow creation and destruction + * of subpools by threads other than the parent pool owner. + * + * 05/02/2011 + * - Fixed compilation on windows: Suppress compile warning 4996 + * and include <winsock2.h> before including <ws2tcpip.h>, + * by Merov Linden @ SL. + */ + +#ifndef LL_LLAPRPOOL_H +#define LL_LLAPRPOOL_H + +#ifdef LL_WINDOWS +#pragma warning(push) +#pragma warning(disable:4996) +#include <winsock2.h> +#include <ws2tcpip.h> // Needed before including apr_portable.h +#pragma warning(pop) +#endif + +#include "apr_portable.h" +#include "apr_pools.h" +#include "llerror.h" + +extern void ll_init_apr(); + +/** + * @brief A wrapper around the APR memory pool API. + * + * Usage of this class should be restricted to passing it to libapr-1 function calls that need it. + * + */ +class LL_COMMON_API LLAPRPool +{ +protected: + //! Pointer to the underlaying pool. NULL if not initialized. + apr_pool_t* mPool; // The use of apr_pool_t is OK here. + // This is the wrapped pointer that it is all about! + //! Pointer to the parent pool, if any. Only valid when mPool is non-zero. + LLAPRPool* mParent; + //! The thread that owns this memory pool. Only valid when mPool is non-zero. + apr_os_thread_t mOwner; + +public: + /// Construct an uninitialized (destructed) pool. + LLAPRPool(void) : mPool(NULL) { } + + /// Construct a subpool from an existing pool. + /// This is not a copy-constructor, this class doesn't have one! + LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); } + + /// Destruct the memory pool (free all of its subpools and allocated memory). + ~LLAPRPool() { destroy(); } + +protected: + /// Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool. + LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current()) + { + apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL); + llassert_always(apr_pool_create_status == APR_SUCCESS); + llassert(mPool); + apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null); + } + +public: + /// Create a subpool from parent. May only be called for an uninitialized/destroyed pool. + /// The default parameter causes the root pool of the current thread to be used. + void create(LLAPRPool& parent = *static_cast<LLAPRPool*>(NULL)); + + /// Destroy the (sub)pool, if any. + void destroy(void); + + // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool. + typedef LLAPRPool* const LLAPRPool::* const bool_type; + /// Return true if the pool is initialized. + operator bool_type() const { return mPool ? &LLAPRPool::mParent : 0; } + + /// Painful, but we have to either provide access to this, or wrap + /// every APR function call that needs an apr pool as argument. + /// NEVER destroy a pool that is returned by this function! + apr_pool_t* operator()(void) const // The use of apr_pool_t is OK here. + // This is the accessor for passing the pool to libapr-1 functions. + { + llassert(mPool); + llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + return mPool; + } + + /// Free all memory without destructing the pool. + void clear(void) + { + llassert(mPool); + llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + apr_pool_clear(mPool); + } + +// These methods would make this class 'complete' (as wrapper around the libapr +// pool functions), but we don't use memory pools in the viewer (only when +// we are forced to pass one to a libapr call), so don't define them in order +// not to encourage people to use them. +#if 0 + void* palloc(size_t size) + { + llassert(mPool); + llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + return apr_palloc(mPool, size); + } + void* pcalloc(size_t size) + { + llassert(mPool); + llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + return apr_pcalloc(mPool, size); + } +#endif + +private: + bool parent_is_being_destructed(void); + static apr_status_t s_plain_cleanup(void* userdata) { return static_cast<LLAPRPool*>(userdata)->plain_cleanup(); } + + apr_status_t plain_cleanup(void) + { + if (mPool && // We are not being destructed, + parent_is_being_destructed()) // but our parent is. + // This means the pool is being destructed recursively by libapr + // because one of its parents is being destructed. + { + mPool = NULL; // Stop destroy() from destructing the pool again. + } + return APR_SUCCESS; + } +}; + +class LLAPRInitialization +{ +public: + LLAPRInitialization(void); +}; + +/** + * @brief Root memory pool (allocates memory from the operating system). + * + * This class should only be used by LLThreadLocalData + * (and LLMutexRootPool when APR_HAS_THREADS isn't defined). + */ +class LL_COMMON_API LLAPRRootPool : public LLAPRInitialization, public LLAPRPool +{ +private: + /// Construct a root memory pool. Should only be used by LLThreadLocalData and LLMutexRootPool. + friend class LLThreadLocalData; +#if !APR_HAS_THREADS + friend class LLMutexRootPool; +#endif + /// Construct a root memory pool. + /// Should only be used by LLThreadLocalData. + LLAPRRootPool(void); + ~LLAPRRootPool(); + +private: + // Keep track of how many root pools exist and when the last one is destructed. + static bool sCountInitialized; + static apr_uint32_t volatile sCount; + +public: + // Return a global root pool that is independent of LLThreadLocalData. + // Normally you should not use this. Only use for early initialization + // (before main) and deinitialization (after main). + static LLAPRRootPool& get(void); + +#if APR_POOL_DEBUG + void grab_ownership(void) + { + // You need a patched libapr to use this. + // See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI + apr_pool_owner_set(mPool); + } +#endif + +private: + // Used for constructing the Special Global Root Pool (returned by LLAPRRootPool::get). + // It is the same as the default constructor but omits to increment sCount. As a result, + // we must be sure that at least one other LLAPRRootPool is created before termination + // of the application (which is the case: we create one LLAPRRootPool per thread). + LLAPRRootPool(int) : LLAPRInitialization(), LLAPRPool(0) { } +}; + +/** Volatile memory pool + * + * 'Volatile' APR memory pool which normally only clears memory, + * and does not destroy the pool (the same pool is reused) for + * greater efficiency. However, as a safe guard the apr pool + * is destructed every FULL_VOLATILE_APR_POOL uses to allow + * the system memory to be allocated more efficiently and not + * get scattered through RAM. + */ +class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool +{ +public: + LLVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { } + + void clearVolatileAPRPool(void); + + bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; } + bool isUnused() const { return mNumActiveRef == 0; } + +private: + friend class LLScopedVolatileAPRPool; + friend class LLAPRFile; + apr_pool_t* getVolatileAPRPool(void) // The use of apr_pool_t is OK here. + { + if (!mPool) create(); + ++mNumActiveRef; + ++mNumTotalRef; + return LLAPRPool::operator()(); + } + +private: + S32 mNumActiveRef; // Number of active uses of the pool. + S32 mNumTotalRef; // Number of total uses of the pool since last creation. + + // Maximum number of references to LLVolatileAPRPool until the pool is recreated. + static S32 const FULL_VOLATILE_APR_POOL = 1024; +}; + +#endif // LL_LLAPRPOOL_H diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 8be9e4f4de..b8a7394852 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -31,17 +31,9 @@ #include "llthread.h" //static -BOOL LLCommon::sAprInitialized = FALSE; - -//static void LLCommon::initClass() { LLMemory::initClass(); - if (!sAprInitialized) - { - ll_init_apr(); - sAprInitialized = TRUE; - } LLTimer::initClass(); LLThreadSafeRefCount::initThreadSafeRefCount(); // LLWorkerThread::initClass(); @@ -55,10 +47,5 @@ void LLCommon::cleanupClass() // LLWorkerThread::cleanupClass(); LLThreadSafeRefCount::cleanupThreadSafeRefCount(); LLTimer::cleanupClass(); - if (sAprInitialized) - { - ll_cleanup_apr(); - sAprInitialized = FALSE; - } LLMemory::cleanupClass(); } diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h index ca9cad5d05..171590f3d8 100644 --- a/indra/llcommon/llcommon.h +++ b/indra/llcommon/llcommon.h @@ -35,8 +35,6 @@ class LL_COMMON_API LLCommon public: static void initClass(); static void cleanupClass(); -private: - static BOOL sAprInitialized; }; #endif diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index bb64152407..75048073ca 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -866,6 +866,9 @@ You get: */ +extern apr_thread_mutex_t* gLogMutexp; +extern apr_thread_mutex_t* gCallStacksLogMutexp; + namespace { bool checkLevelMap(const LevelMap& map, const std::string& key, LLError::ELevel& level) diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 4a42241c4f..15d167c32e 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -296,5 +296,4 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; Such computation is done iff the message will be logged. */ - #endif // LL_LLERROR_H diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp index d394f179fb..4b5cdbe288 100644 --- a/indra/llcommon/llfixedbuffer.cpp +++ b/indra/llcommon/llfixedbuffer.cpp @@ -30,8 +30,7 @@ LLFixedBuffer::LLFixedBuffer(const U32 max_lines) : LLLineBuffer(), - mMaxLines(max_lines), - mMutex(NULL) + mMaxLines(max_lines) { mTimer.reset(); } diff --git a/indra/llcommon/llscopedvolatileaprpool.h b/indra/llcommon/llscopedvolatileaprpool.h new file mode 100644 index 0000000000..dbaf4edcad --- /dev/null +++ b/indra/llcommon/llscopedvolatileaprpool.h @@ -0,0 +1,52 @@ +/** + * @file llscopedvolatileaprpool.h + * @brief Implementation of LLScopedVolatileAPRPool + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_LLSCOPEDVOLATILEAPRPOOL_H +#define LL_LLSCOPEDVOLATILEAPRPOOL_H + +#include "llthread.h" + +/** Scoped volatile memory pool. + * + * As the LLVolatileAPRPool should never keep allocations very + * long, its most common use is for allocations with a lifetime + * equal to it's scope. + * + * This is a convenience class that makes just a little easier to type. + */ +class LL_COMMON_API LLScopedVolatileAPRPool +{ +private: + LLVolatileAPRPool& mPool; + apr_pool_t* mScopedAPRpool; // The use of apr_pool_t is OK here. +public: + LLScopedVolatileAPRPool() : mPool(LLThreadLocalData::tldata().mVolatileAPRPool), mScopedAPRpool(mPool.getVolatileAPRPool()) { } + ~LLScopedVolatileAPRPool() { mPool.clearVolatileAPRPool(); } + //! @attention Only use this to pass the underlaying pointer to a libapr-1 function that requires it. + operator apr_pool_t*() const { return mScopedAPRpool; } // The use of apr_pool_t is OK here. +}; + +#endif diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 49d05ef411..4917e3b935 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -63,6 +63,9 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap { LLThread *threadp = (LLThread *)datap; + // Create a thread local data. + LLThreadLocalData::create(threadp); + // Run the user supplied function threadp->run(); @@ -75,38 +78,20 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap } -LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : - mPaused(FALSE), +LLThread::LLThread(std::string const& name) : + mPaused(false), mName(name), mAPRThreadp(NULL), - mStatus(STOPPED) + mStatus(STOPPED), + mThreadLocalData(NULL) { - // Thread creation probably CAN be paranoid about APR being initialized, if necessary - if (poolp) - { - mIsLocalPool = FALSE; - mAPRPoolp = poolp; - } - else - { - mIsLocalPool = TRUE; - apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread - } - mRunCondition = new LLCondition(mAPRPoolp); - - mLocalAPRFilePoolp = NULL ; + mRunCondition = new LLCondition; } LLThread::~LLThread() { shutdown(); - - if(mLocalAPRFilePoolp) - { - delete mLocalAPRFilePoolp ; - mLocalAPRFilePoolp = NULL ; - } } void LLThread::shutdown() @@ -143,7 +128,7 @@ void LLThread::shutdown() if (!isStopped()) { // This thread just wouldn't stop, even though we gave it time - llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; + llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl; // Put a stake in its heart. apr_thread_exit(mAPRThreadp, -1); return; @@ -153,15 +138,8 @@ void LLThread::shutdown() delete mRunCondition; mRunCondition = 0; - - if (mIsLocalPool && mAPRPoolp) - { - apr_pool_destroy(mAPRPoolp); - mAPRPoolp = 0; - } } - void LLThread::start() { llassert(isStopped()); @@ -170,7 +148,7 @@ void LLThread::start() mStatus = RUNNING; apr_status_t status = - apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); + apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool()); if(status == APR_SUCCESS) { @@ -195,7 +173,7 @@ void LLThread::pause() if (!mPaused) { // this will cause the thread to stop execution as soon as checkPause() is called - mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread + mPaused = true; // Does not need to be atomic since this is only set/unset from the main thread } } @@ -203,7 +181,7 @@ void LLThread::unpause() { if (mPaused) { - mPaused = 0; + mPaused = false; } wake(); // wake up the thread if necessary @@ -280,85 +258,76 @@ void LLThread::wakeLocked() } } -//============================================================================ - -LLMutex::LLMutex(apr_pool_t *poolp) : - mAPRMutexp(NULL) -{ - //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); -} +#ifdef SHOW_ASSERT +// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread. +static apr_os_thread_t main_thread_id; +LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); } +#endif +// The thread private handle to access the LLThreadLocalData instance. +apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey; -LLMutex::~LLMutex() +//static +void LLThreadLocalData::init(void) { -#if MUTEX_DEBUG - llassert_always(!isLocked()); // better not be locked! -#endif - apr_thread_mutex_destroy(mAPRMutexp); - mAPRMutexp = NULL; - if (mIsLocalPool) + // Only do this once. + if (sThreadLocalDataKey) { - apr_pool_destroy(mAPRPoolp); + return; } -} + apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()()); + ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the + // total number of keys per process {PTHREAD_KEYS_MAX} + // has been exceeded. -void LLMutex::lock() -{ - 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; + // Create the thread-local data for the main thread (this function is called by the main thread). + LLThreadLocalData::create(NULL); + +#ifdef SHOW_ASSERT + // This function is called by the main thread. + main_thread_id = apr_os_thread_current(); #endif } -void LLMutex::unlock() +// This is called once for every thread when the thread is destructed. +//static +void LLThreadLocalData::destroy(void* thread_local_data) { -#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 - apr_thread_mutex_unlock(mAPRMutexp); + delete static_cast<LLThreadLocalData*>(thread_local_data); } -bool LLMutex::isLocked() +//static +void LLThreadLocalData::create(LLThread* threadp) { - apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp); - if (APR_STATUS_IS_EBUSY(status)) + LLThreadLocalData* new_tld = new LLThreadLocalData; + if (threadp) { - return true; + threadp->mThreadLocalData = new_tld; } - else + apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey); + llassert_always(status == APR_SUCCESS); +} + +//static +LLThreadLocalData& LLThreadLocalData::tldata(void) +{ + if (!sThreadLocalDataKey) { - apr_thread_mutex_unlock(mAPRMutexp); - return false; + LLThreadLocalData::init(); } + + void* data; + apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey); + llassert_always(status == APR_SUCCESS); + return *static_cast<LLThreadLocalData*>(data); } //============================================================================ -LLCondition::LLCondition(apr_pool_t *poolp) : - LLMutex(poolp) +LLCondition::LLCondition(LLAPRPool& parent) : LLMutex(parent) { - // base class (LLMutex) has already ensured that mAPRPoolp is set up. - - apr_thread_cond_create(&mAPRCondp, mAPRPoolp); + apr_thread_cond_create(&mAPRCondp, mPool()); } @@ -396,7 +365,7 @@ void LLThreadSafeRefCount::initThreadSafeRefCount() { if (!sMutex) { - sMutex = new LLMutex(0); + sMutex = new LLMutex; } } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index f1c6cd75af..757832b8ca 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -29,12 +29,34 @@ #include "llapp.h" #include "llapr.h" +#include "llmemory.h" #include "apr_thread_cond.h" +#include "llaprpool.h" + +#ifdef SHOW_ASSERT +extern LL_COMMON_API bool is_main_thread(void); +#endif class LLThread; class LLMutex; class LLCondition; +class LL_COMMON_API LLThreadLocalData +{ +private: + static apr_threadkey_t* sThreadLocalDataKey; + +public: + // Thread-local memory pools. + LLAPRRootPool mRootPool; + LLVolatileAPRPool mVolatileAPRPool; + + static void init(void); + static void destroy(void* thread_local_data); + static void create(LLThread* pthread); + static LLThreadLocalData& tldata(void); +}; + class LL_COMMON_API LLThread { public: @@ -45,7 +67,7 @@ public: QUITTING= 2 // Someone wants this thread to quit } EThreadStatus; - LLThread(const std::string& name, apr_pool_t *poolp = NULL); + LLThread(std::string const& name); virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. virtual void shutdown(); // stops the thread @@ -60,7 +82,7 @@ public: // Called from MAIN THREAD. void pause(); void unpause(); - bool isPaused() { return isStopped() || mPaused == TRUE; } + bool isPaused() { return isStopped() || mPaused; } // Cause the thread to wake up and check its condition void wake(); @@ -74,11 +96,11 @@ public: // this kicks off the apr thread void start(void); - apr_pool_t *getAPRPool() { return mAPRPoolp; } - LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } + // Return thread-local data for the current thread. + static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); } private: - BOOL mPaused; + bool mPaused; // static function passed to APR thread creation routine static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap); @@ -88,14 +110,10 @@ protected: LLCondition* mRunCondition; apr_thread_t *mAPRThreadp; - apr_pool_t *mAPRPoolp; - BOOL mIsLocalPool; EThreadStatus mStatus; - //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 - LLVolatileAPRPool *mLocalAPRFilePoolp ; + friend void LLThreadLocalData::create(LLThread* threadp); + LLThreadLocalData* mThreadLocalData; void setQuitting(); @@ -125,30 +143,80 @@ protected: #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) -class LL_COMMON_API LLMutex +#ifdef MUTEX_DEBUG +// We really shouldn't be using recursive locks. Make sure of that in debug mode. +#define MUTEX_FLAG APR_THREAD_MUTEX_UNNESTED +#else +// Use the fastest platform-optimal lock behavior (can be recursive or non-recursive). +#define MUTEX_FLAG APR_THREAD_MUTEX_DEFAULT +#endif + +class LL_COMMON_API LLMutexBase { public: - 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 - + void lock() { apr_thread_mutex_lock(mAPRMutexp); } + void unlock() { apr_thread_mutex_unlock(mAPRMutexp); } + // Returns true if lock was obtained successfully. + bool trylock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); } + + // non-blocking, but does do a lock/unlock so not free + bool isLocked() { bool is_not_locked = trylock(); if (is_not_locked) unlock(); return !is_not_locked; } + +protected: + // mAPRMutexp is initialized and uninitialized in the derived class. + apr_thread_mutex_t* mAPRMutexp; +}; + +class LL_COMMON_API LLMutex : public LLMutexBase +{ +public: + LLMutex(LLAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent) + { + apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mPool()); + } + ~LLMutex() + { + llassert(!isLocked()); // better not be locked! + apr_thread_mutex_destroy(mAPRMutexp); + mAPRMutexp = NULL; + } + protected: - apr_thread_mutex_t *mAPRMutexp; - apr_pool_t *mAPRPoolp; - BOOL mIsLocalPool; -#if MUTEX_DEBUG - std::map<U32, BOOL> mIsLocked; + LLAPRPool mPool; +}; + +#if APR_HAS_THREADS +// No need to use a root pool in this case. +typedef LLMutex LLMutexRootPool; +#else // APR_HAS_THREADS +class LL_COMMON_API LLMutexRootPool : public LLMutexBase +{ +public: + LLMutexRootPool(void) + { + apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mRootPool()); + } + ~LLMutexRootPool() + { +#if APR_POOL_DEBUG + // It is allowed to destruct root pools from a different thread. + mRootPool.grab_ownership(); #endif + llassert(!isLocked()); + apr_thread_mutex_destroy(mAPRMutexp); + mAPRMutexp = NULL; + } + +protected: + LLAPRRootPool mRootPool; }; +#endif // APR_HAS_THREADS // 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(LLAPRPool& parent = LLThread::tldata().mRootPool); ~LLCondition(); void wait(); // blocks @@ -159,10 +227,10 @@ protected: apr_thread_cond_t *mAPRCondp; }; -class LLMutexLock +class LL_COMMON_API LLMutexLock { public: - LLMutexLock(LLMutex* mutex) + LLMutexLock(LLMutexBase* mutex) { mMutex = mutex; mMutex->lock(); @@ -172,7 +240,7 @@ public: mMutex->unlock(); } private: - LLMutex* mMutex; + LLMutexBase* mMutex; }; //============================================================================ diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index 8a73e632a9..05d24944f3 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -34,19 +34,11 @@ //----------------------------------------------------------------------------- -LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity): - mOwnsPool(pool == 0), - mPool(pool), +LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(unsigned int capacity): mQueue(0) { - if(mOwnsPool) { - apr_status_t status = apr_pool_create(&mPool, 0); - if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool"); - } else { - ; // No op. - } - - apr_status_t status = apr_queue_create(&mQueue, capacity, mPool); + mPool.create(); + apr_status_t status = apr_queue_create(&mQueue, capacity, mPool()); if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue"); } @@ -59,7 +51,6 @@ LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation() " elements;" << "memory will be leaked" << LL_ENDL; apr_queue_term(mQueue); } - if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool); } diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 58cac38769..43d0b396f2 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -30,9 +30,9 @@ #include <string> #include <stdexcept> +#include "llaprpool.h" -struct apr_pool_t; // From apr_pools.h class LLThreadSafeQueueImplementation; // See below. @@ -75,7 +75,7 @@ struct apr_queue_t; // From apr_queue.h class LL_COMMON_API LLThreadSafeQueueImplementation { public: - LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity); + LLThreadSafeQueueImplementation(unsigned int capacity); ~LLThreadSafeQueueImplementation(); void pushFront(void * element); bool tryPushFront(void * element); @@ -84,8 +84,7 @@ public: size_t size(); private: - bool mOwnsPool; - apr_pool_t * mPool; + LLAPRPool mPool; // The pool used for mQueue. apr_queue_t * mQueue; }; @@ -99,9 +98,8 @@ class LLThreadSafeQueue public: typedef ElementT value_type; - // If the pool is set to NULL one will be allocated and managed by this - // queue. - LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024); + // Constructor. + LLThreadSafeQueue(unsigned int capacity = 1024); // Add an element to the front of queue (will block if the queue has // reached capacity). @@ -139,8 +137,8 @@ private: template<typename ElementT> -LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity): - mImplementation(pool, capacity) +LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(unsigned int capacity) : + mImplementation(capacity) { ; // No op. } diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp index 3ac50832fd..6b308bb917 100644 --- a/indra/llcommon/llworkerthread.cpp +++ b/indra/llcommon/llworkerthread.cpp @@ -37,12 +37,7 @@ LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) : LLQueuedThread(name, threaded) { - mDeleteMutex = new LLMutex(NULL); - - if(!mLocalAPRFilePoolp) - { - mLocalAPRFilePoolp = new LLVolatileAPRPool() ; - } + mDeleteMutex = new LLMutex; } LLWorkerThread::~LLWorkerThread() @@ -204,7 +199,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na mWorkerClassName(name), mRequestHandle(LLWorkerThread::nullHandle()), mRequestPriority(LLWorkerThread::PRIORITY_NORMAL), - mMutex(NULL), mWorkFlags(0) { if (!mWorkerThread) diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index 9bff18303e..bef5ef53fe 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -94,7 +94,6 @@ public: private: void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion - }; //============================================================================ @@ -194,7 +193,7 @@ protected: U32 mRequestPriority; // last priority set private: - LLMutex mMutex; + LLMutexRootPool mMutex; // Use LLMutexRootPool since this object is created and destructed by multiple threads. LLAtomicU32 mWorkFlags; }; |