Added a thread-safe and robust wrapper for APR pools.
See http://redmine.imprudenceviewer.org/issues/590 and https://jira.secondlife.com/browse/SNOW-596
This commit is contained in:
@@ -13,6 +13,7 @@ include_directories(
|
||||
)
|
||||
|
||||
set(llcommon_SOURCE_FILES
|
||||
aiaprpool.cpp
|
||||
imageids.cpp
|
||||
indra_constants.cpp
|
||||
llapp.cpp
|
||||
@@ -78,6 +79,7 @@ set(llcommon_SOURCE_FILES
|
||||
set(llcommon_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
|
||||
aiaprpool.h
|
||||
bitpack.h
|
||||
ctype_workaround.h
|
||||
doublelinkedlist.h
|
||||
@@ -155,6 +157,7 @@ set(llcommon_HEADER_FILES
|
||||
llqueuedthread.h
|
||||
llrand.h
|
||||
llrun.h
|
||||
llscopedvolatileaprpool.h
|
||||
llsd.h
|
||||
llsdserialize.h
|
||||
llsdserialize_xml.h
|
||||
|
||||
198
indra/llcommon/aiaprpool.cpp
Normal file
198
indra/llcommon/aiaprpool.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* @file aiaprpool.cpp
|
||||
*
|
||||
* Copyright (c) 2010, Aleric Inglewood.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution.
|
||||
*
|
||||
* CHANGELOG
|
||||
* and additional copyright holders.
|
||||
*
|
||||
* 04/04/2010
|
||||
* - Initial version, written by Aleric Inglewood @ SL
|
||||
*
|
||||
* 10/11/2010
|
||||
* - Changed filename, class names and license to a more
|
||||
* company-neutral format.
|
||||
* - 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 "aiaprpool.h"
|
||||
#include "llthread.h"
|
||||
|
||||
// Create a subpool from parent.
|
||||
void AIAPRPool::create(AIAPRPool& 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 = &AIThreadLocalData::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 AIAPRPool::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;
|
||||
mPool = NULL; // Mark that we are BEING destructed.
|
||||
apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
|
||||
apr_pool_destroy(pool);
|
||||
}
|
||||
}
|
||||
|
||||
bool AIAPRPool::parent_is_being_destructed(void)
|
||||
{
|
||||
return mParent && (!mParent->mPool || mParent->parent_is_being_destructed());
|
||||
}
|
||||
|
||||
AIAPRInitialization::AIAPRInitialization(void)
|
||||
{
|
||||
static bool apr_initialized = false;
|
||||
|
||||
if (!apr_initialized)
|
||||
{
|
||||
apr_initialize();
|
||||
}
|
||||
|
||||
apr_initialized = true;
|
||||
}
|
||||
|
||||
bool AIAPRRootPool::sCountInitialized = false;
|
||||
apr_uint32_t volatile AIAPRRootPool::sCount;
|
||||
|
||||
extern apr_thread_mutex_t* gLogMutexp;
|
||||
extern apr_thread_mutex_t* gCallStacksLogMutexp;
|
||||
|
||||
AIAPRRootPool::AIAPRRootPool(void) : AIAPRInitialization(), AIAPRPool(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 AIAPRRootPool::AIAPRRootPool(void)
|
||||
// it must be done last, so that sCount is already initialized.
|
||||
AIThreadLocalData::init();
|
||||
}
|
||||
apr_atomic_inc32(&sCount);
|
||||
}
|
||||
|
||||
AIAPRRootPool::~AIAPRRootPool()
|
||||
{
|
||||
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 AIAPRRootPool, before terminating APR.
|
||||
static_cast<AIAPRRootPool*>(this)->destroy();
|
||||
|
||||
apr_terminate();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
AIAPRRootPool& AIAPRRootPool::get(void)
|
||||
{
|
||||
static AIAPRRootPool global_APRpool(0); // This is what used to be gAPRPoolp.
|
||||
return global_APRpool;
|
||||
}
|
||||
|
||||
void AIVolatileAPRPool::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)) ;
|
||||
}
|
||||
238
indra/llcommon/aiaprpool.h
Normal file
238
indra/llcommon/aiaprpool.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* @file aiaprpool.h
|
||||
* @brief Implementation of AIAPRPool.
|
||||
*
|
||||
* Copyright (c) 2010, Aleric Inglewood.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution.
|
||||
*
|
||||
* CHANGELOG
|
||||
* and additional copyright holders.
|
||||
*
|
||||
* 04/04/2010
|
||||
* - Initial version, written by Aleric Inglewood @ SL
|
||||
*
|
||||
* 10/11/2010
|
||||
* - Changed filename, class names and license to a more
|
||||
* company-neutral format.
|
||||
* - Added APR_HAS_THREADS #if's to allow creation and destruction
|
||||
* of subpools by threads other than the parent pool owner.
|
||||
*/
|
||||
|
||||
#ifndef AIAPRPOOL_H
|
||||
#define AIAPRPOOL_H
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
#include <ws2tcpip.h> // Needed before including apr_portable.h
|
||||
#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 AIAPRPool
|
||||
{
|
||||
protected:
|
||||
apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized.
|
||||
AIAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero.
|
||||
apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
|
||||
|
||||
public:
|
||||
//! Construct an uninitialized (destructed) pool.
|
||||
AIAPRPool(void) : mPool(NULL) { }
|
||||
|
||||
//! Construct a subpool from an existing pool.
|
||||
// This is not a copy-constructor, this class doesn't have one!
|
||||
AIAPRPool(AIAPRPool& parent) : mPool(NULL) { create(parent); }
|
||||
|
||||
//! Destruct the memory pool (free all of it's subpools and allocated memory).
|
||||
~AIAPRPool() { destroy(); }
|
||||
|
||||
protected:
|
||||
// Create a pool that is allocated from the Operating System. Only used by AIAPRRootPool.
|
||||
AIAPRPool(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(AIAPRPool& parent = *static_cast<AIAPRPool*>(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 apr_pool_t* const AIAPRPool::* const bool_type;
|
||||
//! Return true if the pool is initialized.
|
||||
operator bool_type() const { return mPool ? &AIAPRPool::mPool : 0; }
|
||||
|
||||
// Painful, but we have to either provide access to this, or wrap
|
||||
// every APR function call that needs a apr_pool_t* to be passed.
|
||||
// NEVER destroy a pool that is returned by this function!
|
||||
apr_pool_t* operator()(void) const
|
||||
{
|
||||
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<AIAPRPool*>(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 it's parents is being destructed.
|
||||
{
|
||||
mPool = NULL; // Stop destroy() from destructing the pool again.
|
||||
}
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
class AIAPRInitialization
|
||||
{
|
||||
public:
|
||||
AIAPRInitialization(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Root memory pool (allocates memory from the operating system).
|
||||
*
|
||||
* This class should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase
|
||||
* (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
|
||||
*/
|
||||
class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool
|
||||
{
|
||||
private:
|
||||
friend class AIThreadLocalData;
|
||||
friend class AIThreadSafeSimpleDCRootPool_pbase;
|
||||
#if !APR_HAS_THREADS
|
||||
friend class LLMutexRootPool;
|
||||
#endif
|
||||
//! Construct a root memory pool.
|
||||
// Should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase.
|
||||
AIAPRRootPool(void);
|
||||
~AIAPRRootPool();
|
||||
|
||||
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 AIThreadLocalData.
|
||||
// Normally you should not use this. Only use for early initialization
|
||||
// (before main) and deinitialization (after main).
|
||||
static AIAPRRootPool& 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 AIAPRRootPool::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 AIAPRRootPool is created before termination
|
||||
// of the application (which is the case: we create one AIAPRRootPool per thread).
|
||||
AIAPRRootPool(int) : AIAPRInitialization(), AIAPRPool(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 AIVolatileAPRPool : protected AIAPRPool
|
||||
{
|
||||
public:
|
||||
AIVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { }
|
||||
|
||||
apr_pool_t* getVolatileAPRPool(void)
|
||||
{
|
||||
if (!mPool) create();
|
||||
++mNumActiveRef;
|
||||
++mNumTotalRef;
|
||||
return AIAPRPool::operator()();
|
||||
}
|
||||
void clearVolatileAPRPool(void);
|
||||
|
||||
bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; }
|
||||
bool isUnused() const { return mNumActiveRef == 0; }
|
||||
|
||||
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 AIVolatileAPRPool until the pool is recreated.
|
||||
static S32 const FULL_VOLATILE_APR_POOL = 1024;
|
||||
};
|
||||
|
||||
#endif // AIAPRPOOL_H
|
||||
@@ -123,13 +123,8 @@ 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;
|
||||
|
||||
}
|
||||
|
||||
LLApp::LLApp(LLErrorThread *error_thread) :
|
||||
|
||||
@@ -34,219 +34,7 @@
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llapr.h"
|
||||
|
||||
apr_pool_t *gAPRPoolp = NULL; // Global 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);
|
||||
|
||||
// Initialize thread-local APR pool support.
|
||||
LLVolatileAPRPool::initLocalAPRFilePool();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
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 ;
|
||||
}
|
||||
}
|
||||
|
||||
apr_pool_t* LLAPRPool::getAPRPool()
|
||||
{
|
||||
if(!mPool)
|
||||
{
|
||||
createAPRPool() ;
|
||||
}
|
||||
|
||||
return mPool ;
|
||||
}
|
||||
LLVolatileAPRPool::LLVolatileAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
|
||||
: LLAPRPool(parent, size, releasePoolFlag)
|
||||
{
|
||||
mNumActiveRef = 0 ;
|
||||
mNumTotalRef = 0 ;
|
||||
}
|
||||
|
||||
apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
|
||||
{
|
||||
mNumTotalRef++ ;
|
||||
mNumActiveRef++ ;
|
||||
return getAPRPool() ;
|
||||
}
|
||||
|
||||
void LLVolatileAPRPool::clearVolatileAPRPool()
|
||||
{
|
||||
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 ;
|
||||
}
|
||||
|
||||
#ifdef SHOW_ASSERT
|
||||
// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
|
||||
static void* gIsMainThread;
|
||||
bool is_main_thread() { return gIsMainThread == LLVolatileAPRPool::getLocalAPRFilePool(); }
|
||||
#endif
|
||||
|
||||
// The thread private handle to access the LocalAPRFilePool.
|
||||
apr_threadkey_t* LLVolatileAPRPool::sLocalAPRFilePoolKey;
|
||||
|
||||
// This should be called exactly once, before the first call to createLocalAPRFilePool.
|
||||
// static
|
||||
void LLVolatileAPRPool::initLocalAPRFilePool()
|
||||
{
|
||||
apr_status_t status = apr_threadkey_private_create(&sLocalAPRFilePoolKey, &destroyLocalAPRFilePool, gAPRPoolp);
|
||||
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.
|
||||
// Create the thread-local pool for the main thread (this function is called by the main thread).
|
||||
createLocalAPRFilePool();
|
||||
#ifdef SHOW_ASSERT
|
||||
gIsMainThread = getLocalAPRFilePool();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This should be called once for every thread, before it uses getLocalAPRFilePool.
|
||||
// static
|
||||
void LLVolatileAPRPool::createLocalAPRFilePool()
|
||||
{
|
||||
void* thread_local_data = new LLVolatileAPRPool;
|
||||
apr_status_t status = apr_threadkey_private_set(thread_local_data, sLocalAPRFilePoolKey);
|
||||
llassert_always(status == APR_SUCCESS);
|
||||
}
|
||||
|
||||
// This is called once for every thread when the thread is destructed.
|
||||
// static
|
||||
void LLVolatileAPRPool::destroyLocalAPRFilePool(void* thread_local_data)
|
||||
{
|
||||
delete reinterpret_cast<LLVolatileAPRPool*>(thread_local_data);
|
||||
}
|
||||
|
||||
// static
|
||||
LLVolatileAPRPool* LLVolatileAPRPool::getLocalAPRFilePool()
|
||||
{
|
||||
void* thread_local_data;
|
||||
apr_status_t status = apr_threadkey_private_get(&thread_local_data, sLocalAPRFilePoolKey);
|
||||
llassert_always(status == APR_SUCCESS);
|
||||
return reinterpret_cast<LLVolatileAPRPool*>(thread_local_data);
|
||||
}
|
||||
#include "llscopedvolatileaprpool.h"
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
@@ -309,13 +97,15 @@ void ll_apr_assert_status(apr_status_t status)
|
||||
//
|
||||
LLAPRFile::LLAPRFile()
|
||||
: mFile(NULL),
|
||||
mCurrentFilePoolp(NULL)
|
||||
mVolatileFilePoolp(NULL),
|
||||
mRegularFilePoolp(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type)
|
||||
: mFile(NULL),
|
||||
mCurrentFilePoolp(NULL)
|
||||
mVolatileFilePoolp(NULL),
|
||||
mRegularFilePoolp(NULL)
|
||||
{
|
||||
open(filename, flags, access_type);
|
||||
}
|
||||
@@ -334,10 +124,16 @@ 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 ;
|
||||
@@ -346,25 +142,28 @@ apr_status_t LLAPRFile::close()
|
||||
apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep)
|
||||
{
|
||||
llassert_always(!mFile);
|
||||
llassert_always(!mCurrentFilePoolp);
|
||||
llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp);
|
||||
|
||||
// Access the pool and increment it's reference count.
|
||||
// The reference count of LLVolatileAPRPool objects will be decremented
|
||||
// again in LLAPRFile::close by calling mCurrentFilePoolp->clearVolatileAPRPool().
|
||||
apr_pool_t* pool;
|
||||
if (access_type == local)
|
||||
apr_status_t status;
|
||||
{
|
||||
// Use a "volatile" thread-local pool.
|
||||
mCurrentFilePoolp = LLVolatileAPRPool::getLocalAPRFilePool();
|
||||
pool = mCurrentFilePoolp->getVolatileAPRPool();
|
||||
apr_pool_t* apr_file_open_pool;
|
||||
if (access_type == local)
|
||||
{
|
||||
// Use a "volatile" thread-local pool.
|
||||
mVolatileFilePoolp = &AIThreadLocalData::tldata().mVolatileAPRPool;
|
||||
// Access the pool and increment it's reference count.
|
||||
// The reference count of AIVolatileAPRPool objects will be decremented
|
||||
// again in LLAPRFile::close by calling mVolatileFilePoolp->clearVolatileAPRPool().
|
||||
apr_file_open_pool = mVolatileFilePoolp->getVolatileAPRPool();
|
||||
}
|
||||
else
|
||||
{
|
||||
mRegularFilePoolp = new AIAPRPool(AIThreadLocalData::tldata().mRootPool);
|
||||
apr_file_open_pool = (*mRegularFilePoolp)();
|
||||
}
|
||||
status = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_file_open_pool);
|
||||
}
|
||||
else
|
||||
{
|
||||
llassert(is_main_thread());
|
||||
pool = gAPRPoolp;
|
||||
}
|
||||
apr_status_t s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, pool);
|
||||
if (s != APR_SUCCESS || !mFile)
|
||||
if (status != APR_SUCCESS || !mFile)
|
||||
{
|
||||
mFile = NULL ;
|
||||
close() ;
|
||||
@@ -372,7 +171,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
|
||||
{
|
||||
*sizep = 0;
|
||||
}
|
||||
return s;
|
||||
return status;
|
||||
}
|
||||
|
||||
if (sizep)
|
||||
@@ -389,7 +188,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
|
||||
*sizep = file_size;
|
||||
}
|
||||
|
||||
return s;
|
||||
return status;
|
||||
}
|
||||
|
||||
// File I/O
|
||||
@@ -449,17 +248,6 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
|
||||
//static components of LLAPRFile
|
||||
//
|
||||
|
||||
// Used in the static functions below.
|
||||
class LLScopedVolatileAPRFilePool {
|
||||
private:
|
||||
LLVolatileAPRPool* mPool;
|
||||
apr_pool_t* apr_pool;
|
||||
public:
|
||||
LLScopedVolatileAPRFilePool() : mPool(LLVolatileAPRPool::getLocalAPRFilePool()), apr_pool(mPool->getVolatileAPRPool()) { }
|
||||
~LLScopedVolatileAPRFilePool() { mPool->clearVolatileAPRPool(); }
|
||||
operator apr_pool_t*() const { return apr_pool; }
|
||||
};
|
||||
|
||||
//static
|
||||
S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
|
||||
{
|
||||
@@ -496,7 +284,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
|
||||
S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
|
||||
{
|
||||
apr_file_t* file_handle;
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
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)
|
||||
{
|
||||
@@ -548,7 +336,7 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
|
||||
}
|
||||
|
||||
apr_file_t* file_handle;
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
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)
|
||||
{
|
||||
@@ -593,7 +381,7 @@ bool LLAPRFile::remove(const std::string& filename)
|
||||
{
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_file_remove(filename.c_str(), pool);
|
||||
|
||||
if (s != APR_SUCCESS)
|
||||
@@ -613,7 +401,7 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
|
||||
{
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
|
||||
|
||||
if (s != APR_SUCCESS)
|
||||
@@ -631,7 +419,7 @@ bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags)
|
||||
apr_file_t* file_handle;
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
|
||||
|
||||
if (s != APR_SUCCESS || !file_handle)
|
||||
@@ -652,7 +440,7 @@ S32 LLAPRFile::size(const std::string& filename)
|
||||
apr_finfo_t info;
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
|
||||
|
||||
if (s != APR_SUCCESS || !file_handle)
|
||||
@@ -681,7 +469,7 @@ bool LLAPRFile::makeDir(const std::string& dirname)
|
||||
{
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
|
||||
|
||||
if (s != APR_SUCCESS)
|
||||
@@ -698,7 +486,7 @@ bool LLAPRFile::removeDir(const std::string& dirname)
|
||||
{
|
||||
apr_status_t s;
|
||||
|
||||
LLScopedVolatileAPRFilePool pool;
|
||||
LLScopedVolatileAPRPool pool;
|
||||
s = apr_file_remove(dirname.c_str(), pool);
|
||||
|
||||
if (s != APR_SUCCESS)
|
||||
|
||||
@@ -48,73 +48,8 @@
|
||||
#include "apr_atomic.h"
|
||||
#include "llstring.h"
|
||||
|
||||
extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
|
||||
|
||||
/**
|
||||
* @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) ;
|
||||
~LLAPRPool() ;
|
||||
|
||||
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 : protected LLAPRPool
|
||||
{
|
||||
public:
|
||||
LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
|
||||
~LLVolatileAPRPool(){}
|
||||
|
||||
apr_pool_t* getVolatileAPRPool() ;
|
||||
|
||||
void clearVolatileAPRPool() ;
|
||||
|
||||
BOOL isFull() ;
|
||||
BOOL isEmpty() {return !mNumActiveRef ;}
|
||||
|
||||
static void initLocalAPRFilePool();
|
||||
static void createLocalAPRFilePool();
|
||||
static void destroyLocalAPRFilePool(void* thread_local_data);
|
||||
static LLVolatileAPRPool* getLocalAPRFilePool();
|
||||
|
||||
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.
|
||||
|
||||
static apr_threadkey_t* sLocalAPRFilePoolKey;
|
||||
} ;
|
||||
class AIAPRPool;
|
||||
class AIVolatileAPRPool;
|
||||
|
||||
/**
|
||||
* @class LLScopedLock
|
||||
@@ -205,7 +140,8 @@ 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.
|
||||
AIVolatileAPRPool* mVolatileFilePoolp; // (Thread local) APR pool currently in use.
|
||||
AIAPRPool* mRegularFilePoolp; // ...or a regular pool.
|
||||
|
||||
public:
|
||||
enum access_t {
|
||||
@@ -260,6 +196,4 @@ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
|
||||
|
||||
void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
|
||||
|
||||
extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
|
||||
|
||||
#endif // LL_LLAPR_H
|
||||
|
||||
@@ -34,18 +34,10 @@
|
||||
#include "llcommon.h"
|
||||
#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();
|
||||
@@ -59,10 +51,5 @@ void LLCommon::cleanupClass()
|
||||
// LLWorkerThread::cleanupClass();
|
||||
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
|
||||
LLTimer::cleanupClass();
|
||||
if (sAprInitialized)
|
||||
{
|
||||
ll_cleanup_apr();
|
||||
sAprInitialized = FALSE;
|
||||
}
|
||||
LLMemory::cleanupClass();
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ class LL_COMMON_API LLCommon
|
||||
public:
|
||||
static void initClass();
|
||||
static void cleanupClass();
|
||||
private:
|
||||
static BOOL sAprInitialized;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -877,6 +877,9 @@ You get:
|
||||
|
||||
*/
|
||||
|
||||
apr_thread_mutex_t* gLogMutexp;
|
||||
apr_thread_mutex_t* gCallStacksLogMutexp;
|
||||
|
||||
namespace {
|
||||
bool checkLevelMap(const LevelMap& map, const std::string& key,
|
||||
LLError::ELevel& level)
|
||||
|
||||
@@ -36,8 +36,7 @@
|
||||
|
||||
LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
|
||||
: LLLineBuffer(),
|
||||
mMaxLines(max_lines),
|
||||
mMutex(NULL)
|
||||
mMaxLines(max_lines)
|
||||
{
|
||||
mTimer.reset();
|
||||
}
|
||||
|
||||
58
indra/llcommon/llscopedvolatileaprpool.h
Normal file
58
indra/llcommon/llscopedvolatileaprpool.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file llscopedvolatileaprpool.h
|
||||
* @brief Implementation of LLScopedVolatileAPRPool
|
||||
*
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLSCOPEDVOLATILEAPRPOOL_H
|
||||
#define LL_LLSCOPEDVOLATILEAPRPOOL_H
|
||||
|
||||
#include "llthread.h"
|
||||
|
||||
//! Scoped volatile memory pool.
|
||||
//
|
||||
// As the AIVolatileAPRPool should never keep allocations very
|
||||
// long, it's 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 LLScopedVolatileAPRPool
|
||||
{
|
||||
private:
|
||||
AIVolatileAPRPool& mPool;
|
||||
apr_pool_t* mScopedAPRpool;
|
||||
public:
|
||||
LLScopedVolatileAPRPool() : mPool(AIThreadLocalData::tldata().mVolatileAPRPool), mScopedAPRpool(mPool.getVolatileAPRPool()) { }
|
||||
~LLScopedVolatileAPRPool() { mPool.clearVolatileAPRPool(); }
|
||||
// Only use this to pass the pointer to a libapr-1 function that requires it.
|
||||
operator apr_pool_t*() const { return mScopedAPRpool; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -72,8 +72,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
|
||||
// Set thread state to running
|
||||
threadp->mStatus = RUNNING;
|
||||
|
||||
// Create a thread local APRFile pool.
|
||||
LLVolatileAPRPool::createLocalAPRFilePool();
|
||||
// Create a thread local data.
|
||||
AIThreadLocalData::create(threadp);
|
||||
|
||||
// Run the user supplied function
|
||||
threadp->run();
|
||||
@@ -87,24 +87,14 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
|
||||
}
|
||||
|
||||
|
||||
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
||||
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);
|
||||
mRunCondition = new LLCondition;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,24 +137,18 @@ 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;
|
||||
return;
|
||||
}
|
||||
mAPRThreadp = NULL;
|
||||
}
|
||||
|
||||
delete mRunCondition;
|
||||
|
||||
if (mIsLocalPool)
|
||||
{
|
||||
apr_pool_destroy(mAPRPoolp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLThread::start()
|
||||
{
|
||||
apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
|
||||
apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool());
|
||||
|
||||
// We won't bother joining
|
||||
apr_thread_detach(mAPRThreadp);
|
||||
@@ -265,38 +249,72 @@ void LLThread::wakeLocked()
|
||||
}
|
||||
}
|
||||
|
||||
#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() { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); }
|
||||
#endif
|
||||
|
||||
// The thread private handle to access the AIThreadLocalData instance.
|
||||
apr_threadkey_t* AIThreadLocalData::sThreadLocalDataKey;
|
||||
|
||||
//static
|
||||
void AIThreadLocalData::init(void)
|
||||
{
|
||||
// Only do this once.
|
||||
if (sThreadLocalDataKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &AIThreadLocalData::destroy, AIAPRRootPool::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.
|
||||
|
||||
// Create the thread-local data for the main thread (this function is called by the main thread).
|
||||
AIThreadLocalData::create(NULL);
|
||||
|
||||
#ifdef SHOW_ASSERT
|
||||
// This function is called by the main thread.
|
||||
main_thread_id = apr_os_thread_current();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is called once for every thread when the thread is destructed.
|
||||
//static
|
||||
void AIThreadLocalData::destroy(void* thread_local_data)
|
||||
{
|
||||
delete reinterpret_cast<AIThreadLocalData*>(thread_local_data);
|
||||
}
|
||||
|
||||
//static
|
||||
void AIThreadLocalData::create(LLThread* threadp)
|
||||
{
|
||||
AIThreadLocalData* new_tld = new AIThreadLocalData;
|
||||
if (threadp)
|
||||
{
|
||||
threadp->mThreadLocalData = new_tld;
|
||||
}
|
||||
apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey);
|
||||
llassert_always(status == APR_SUCCESS);
|
||||
}
|
||||
|
||||
//static
|
||||
AIThreadLocalData& AIThreadLocalData::tldata(void)
|
||||
{
|
||||
if (!sThreadLocalDataKey)
|
||||
AIThreadLocalData::init();
|
||||
|
||||
void* data;
|
||||
apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey);
|
||||
llassert_always(status == APR_SUCCESS);
|
||||
return *static_cast<AIThreadLocalData*>(data);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
LLMutex::~LLMutex()
|
||||
{
|
||||
#if _DEBUG
|
||||
llassert(!isLocked()); // better not be locked!
|
||||
#endif
|
||||
apr_thread_mutex_destroy(mAPRMutexp);
|
||||
mAPRMutexp = NULL;
|
||||
if (mIsLocalPool)
|
||||
{
|
||||
apr_pool_destroy(mAPRPoolp);
|
||||
}
|
||||
}
|
||||
|
||||
bool LLMutex::isLocked()
|
||||
bool LLMutexBase::isLocked()
|
||||
{
|
||||
if (!tryLock())
|
||||
{
|
||||
@@ -308,12 +326,9 @@ bool LLMutex::isLocked()
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLCondition::LLCondition(apr_pool_t *poolp) :
|
||||
LLMutex(poolp)
|
||||
LLCondition::LLCondition(AIAPRPool& 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());
|
||||
}
|
||||
|
||||
LLCondition::~LLCondition()
|
||||
@@ -349,7 +364,7 @@ void LLThreadSafeRefCount::initThreadSafeRefCount()
|
||||
{
|
||||
if (!sMutex)
|
||||
{
|
||||
sMutex = new LLMutex(0);
|
||||
sMutex = new LLMutex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,32 @@
|
||||
#include "llmemory.h"
|
||||
|
||||
#include "apr_thread_cond.h"
|
||||
#include "aiaprpool.h"
|
||||
|
||||
#ifdef SHOW_ASSERT
|
||||
extern bool is_main_thread(void);
|
||||
#endif
|
||||
|
||||
class LLThread;
|
||||
class LLMutex;
|
||||
class LLCondition;
|
||||
|
||||
class LL_COMMON_API AIThreadLocalData
|
||||
{
|
||||
private:
|
||||
static apr_threadkey_t* sThreadLocalDataKey;
|
||||
|
||||
public:
|
||||
// Thread-local memory pool.
|
||||
AIAPRRootPool mRootPool;
|
||||
AIVolatileAPRPool mVolatileAPRPool;
|
||||
|
||||
static void init(void);
|
||||
static void destroy(void* thread_local_data);
|
||||
static void create(LLThread* pthread);
|
||||
static AIThreadLocalData& tldata(void);
|
||||
};
|
||||
|
||||
class LL_COMMON_API LLThread
|
||||
{
|
||||
public:
|
||||
@@ -53,7 +74,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
|
||||
|
||||
@@ -82,7 +103,8 @@ public:
|
||||
// this kicks off the apr thread
|
||||
void start(void);
|
||||
|
||||
apr_pool_t *getAPRPool() { return mAPRPoolp; }
|
||||
// Return thread-local data for the current thread.
|
||||
static AIThreadLocalData& tldata(void) { return AIThreadLocalData::tldata(); }
|
||||
|
||||
private:
|
||||
BOOL mPaused;
|
||||
@@ -95,10 +117,11 @@ protected:
|
||||
LLCondition* mRunCondition;
|
||||
|
||||
apr_thread_t *mAPRThreadp;
|
||||
apr_pool_t *mAPRPoolp;
|
||||
BOOL mIsLocalPool;
|
||||
EThreadStatus mStatus;
|
||||
|
||||
friend void AIThreadLocalData::create(LLThread* threadp);
|
||||
AIThreadLocalData* mThreadLocalData;
|
||||
|
||||
void setQuitting();
|
||||
|
||||
// virtual function overridden by subclass -- this will be called when the thread runs
|
||||
@@ -125,12 +148,9 @@ protected:
|
||||
|
||||
//============================================================================
|
||||
|
||||
class LL_COMMON_API LLMutex
|
||||
class LL_COMMON_API LLMutexBase
|
||||
{
|
||||
public:
|
||||
LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
|
||||
~LLMutex();
|
||||
|
||||
void lock() { apr_thread_mutex_lock(mAPRMutexp); }
|
||||
void unlock() { apr_thread_mutex_unlock(mAPRMutexp); }
|
||||
// Returns true if lock was obtained successfully.
|
||||
@@ -139,16 +159,60 @@ public:
|
||||
bool isLocked(); // non-blocking, but does do a lock/unlock so not free
|
||||
|
||||
protected:
|
||||
apr_thread_mutex_t *mAPRMutexp;
|
||||
apr_pool_t *mAPRPoolp;
|
||||
BOOL mIsLocalPool;
|
||||
// mAPRMutexp is initialized and uninitialized in the derived class.
|
||||
apr_thread_mutex_t* mAPRMutexp;
|
||||
};
|
||||
|
||||
class LL_COMMON_API LLMutex : public LLMutexBase
|
||||
{
|
||||
public:
|
||||
LLMutex(AIAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent)
|
||||
{
|
||||
apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mPool());
|
||||
}
|
||||
~LLMutex()
|
||||
{
|
||||
llassert(!isLocked()); // better not be locked!
|
||||
apr_thread_mutex_destroy(mAPRMutexp);
|
||||
mAPRMutexp = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
AIAPRPool 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, APR_THREAD_MUTEX_UNNESTED, mRootPool());
|
||||
}
|
||||
~LLMutexRootPool()
|
||||
{
|
||||
#if APR_POOL_DEBUG
|
||||
// It is allowed to destruct root pools from a different thread.
|
||||
mRootPool.grab_ownership();
|
||||
#endif
|
||||
llassert(!isLocked()); // better not be locked!
|
||||
apr_thread_mutex_destroy(mAPRMutexp);
|
||||
mAPRMutexp = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
AIAPRRootPool 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(AIAPRPool& parent = LLThread::tldata().mRootPool);
|
||||
~LLCondition();
|
||||
|
||||
void wait(); // blocks
|
||||
@@ -162,7 +226,7 @@ protected:
|
||||
class LL_COMMON_API LLMutexLock
|
||||
{
|
||||
public:
|
||||
LLMutexLock(LLMutex* mutex)
|
||||
LLMutexLock(LLMutexBase* mutex)
|
||||
{
|
||||
mMutex = mutex;
|
||||
mMutex->lock();
|
||||
@@ -172,7 +236,7 @@ public:
|
||||
mMutex->unlock();
|
||||
}
|
||||
private:
|
||||
LLMutex* mMutex;
|
||||
LLMutexBase* mMutex;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
|
||||
LLQueuedThread(name, threaded)
|
||||
{
|
||||
mDeleteMutex = new LLMutex(NULL);
|
||||
mDeleteMutex = new LLMutex;
|
||||
}
|
||||
|
||||
LLWorkerThread::~LLWorkerThread()
|
||||
@@ -205,7 +205,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
|
||||
mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
|
||||
mWorkerClassName(name),
|
||||
mRequestHandle(LLWorkerThread::nullHandle()),
|
||||
mMutex(NULL),
|
||||
mWorkFlags(0)
|
||||
{
|
||||
if (!mWorkerThread)
|
||||
|
||||
@@ -200,7 +200,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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user