Apart from just really cleaning things up and moving everything into one class regarding thread IDs (ie, is_main_thread(), comparing ID's etc), this also fixes an obscure bug where LL was casting thread ID's to U32 and then compared those to find out if it the same thread. It's theoretically possible that such fails on a 64bit OS. By generalizing the interface, I adopted the use of a thread-local cache for the current thread ID as used by LLMutex et al, so now all code benefits from that. The idea was even extended to now also be used for is_main_thread() tests and even resetting a thread ID to the ID of the current thread.
199 lines
6.0 KiB
C++
199 lines
6.0 KiB
C++
/**
|
|
* @file llaprpool.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 "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.reset();
|
|
#else
|
|
mOwner = mParent->mOwner;
|
|
llassert(mOwner.equals_current_thread());
|
|
#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 || mParent->mOwner.equals_current_thread());
|
|
#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 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
|
|
LLAPRRootPool& LLAPRRootPool::get(void)
|
|
{
|
|
static LLAPRRootPool global_APRpool(0); // This is what used to be gAPRPoolp.
|
|
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)) ;
|
|
}
|