/**
* @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 .
*
* 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(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)) ;
}