This commit is contained in:
Shyotl
2011-05-07 23:48:28 -05:00
178 changed files with 2242 additions and 1938 deletions

View File

@@ -13,6 +13,7 @@ include_directories(
)
set(llcommon_SOURCE_FILES
aiaprpool.cpp
imageids.cpp
indra_constants.cpp
llapp.cpp
@@ -78,6 +79,8 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
aiaprpool.h
aithreadsafe.h
bitpack.h
ctype_workaround.h
doublelinkedlist.h
@@ -155,6 +158,7 @@ set(llcommon_HEADER_FILES
llqueuedthread.h
llrand.h
llrun.h
llscopedvolatileaprpool.h
llsd.h
llsdserialize.h
llsdserialize_xml.h
@@ -201,7 +205,7 @@ set_source_files_properties(${llcommon_HEADER_FILES}
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
add_library (llcommon ${llcommon_SOURCE_FILES})
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
add_dependencies(llcommon prepare)
target_link_libraries(
llcommon

View 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
View 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

View File

@@ -0,0 +1,482 @@
/**
* @file aithreadsafe.h
* @brief Implementation of AIThreadSafe, AIReadAccessConst, AIReadAccess and AIWriteAccess.
*
* 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.
*
* 31/03/2010
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AITHREADSAFE_H
#define AITHREADSAFE_H
#include <new>
#include "llthread.h"
#include "llerror.h"
template<typename T> struct AIReadAccessConst;
template<typename T> struct AIReadAccess;
template<typename T> struct AIWriteAccess;
template<typename T> struct AIAccess;
template<typename T>
class AIThreadSafeBits
{
private:
// AIThreadSafe is a wrapper around an instance of T.
// Because T might not have a default constructor, it is constructed
// 'in place', with placement new, in the memory reserved here.
//
// Make sure that the memory that T will be placed in is properly
// aligned by using an array of long's.
long mMemory[(sizeof(T) + sizeof(long) - 1) / sizeof(long)];
public:
// The wrapped objects are constructed in-place with placement new *outside*
// of this object (by AITHREADSAFE macro(s) or derived classes).
// However, we are responsible for the destruction of the wrapped object.
~AIThreadSafeBits() { ptr()->~T(); }
// Only for use by AITHREADSAFE, see below.
void* memory() const { return const_cast<long*>(&mMemory[0]); }
protected:
// Accessors.
T const* ptr() const { return reinterpret_cast<T const*>(mMemory); }
T* ptr() { return reinterpret_cast<T*>(mMemory); }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread, allowing concurrent readers.
*
* Use AITHREADSAFE to define instances of any type, and use AIReadAccessConst,
* AIReadAccess and AIWriteAccess to get access to the instance.
*
* For example,
*
* <code>
* class Foo { public: Foo(int, int); };
*
* AITHREADSAFE(Foo, foo, (2, 3));
*
* AIReadAccess<Foo> foo_r(foo);
* // Use foo_r-> for read access.
*
* AIWriteAccess<Foo> foo_w(foo);
* // Use foo_w-> for write access.
* </code>
*
* If <code>foo</code> is constant, you have to use <code>AIReadAccessConst<Foo></code>.
*
* It is possible to pass access objects to a function that
* downgrades the access, for example:
*
* <code>
* void readfunc(AIReadAccess const& access);
*
* AIWriteAccess<Foo> foo_w(foo);
* readfunc(foo_w); // readfunc will perform read access to foo_w.
* </code>
*
* If <code>AIReadAccess</code> is non-const, you can upgrade the access by creating
* an <code>AIWriteAccess</code> object from it. For example:
*
* <code>
* AIWriteAccess<Foo> foo_w(foo_r);
* </code>
*
* This API is Robust(tm). If you try anything that could result in problems,
* it simply won't compile. The only mistake you can still easily make is
* to obtain write access to an object when it is not needed, or to unlock
* an object in between accesses while the state of the object should be
* preserved. For example:
*
* <code>
* // This resets foo to point to the first file and then returns that.
* std::string filename = AIWriteAccess<Foo>(foo)->get_first_filename();
*
* // WRONG! The state between calling get_first_filename and get_next_filename should be preserved!
*
* AIWriteAccess<Foo> foo_w(foo); // Wrong. The code below only needs read-access.
* while (!filename.empty())
* {
* something(filename);
* filename = foo_w->next_filename();
* }
* </code>
*
* Correct would be
*
* <code>
* AIReadAccess<Foo> foo_r(foo);
* std::string filename = AIWriteAccess<Foo>(foo_r)->get_first_filename();
* while (!filename.empty())
* {
* something(filename);
* filename = foo_r->next_filename();
* }
* </code>
*
*/
template<typename T>
class AIThreadSafe : public AIThreadSafeBits<T>
{
protected:
// Only these may access the object (through ptr()).
friend struct AIReadAccessConst<T>;
friend struct AIReadAccess<T>;
friend struct AIWriteAccess<T>;
// Locking control.
AIRWLock mRWLock;
// For use by AIThreadSafeDC
AIThreadSafe(void) { }
AIThreadSafe(AIAPRPool& parent) : mRWLock(parent) { }
public:
// Only for use by AITHREADSAFE, see below.
AIThreadSafe(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); }
};
/**
* @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafe, using an arbitrary constructor.
*
* For example, instead of doing
*
* <code>
* Foo foo(x, y);
* static Bar bar;
* </code>
*
* One can instantiate a thread-safe instance with
*
* <code>
* AITHREADSAFE(Foo, foo, (x, y));
* static AITHREADSAFE(Bar, bar, );
* </code>
*
* Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeDC.
*/
#define AITHREADSAFE(type, var, paramlist) AIThreadSafe<type> var(new (var.memory()) type paramlist)
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* This class is the same as an AIThreadSafe wrapper, except that it can only
* be used for default constructed objects.
*
* For example, instead of
*
* <code>
* Foo foo;
* </code>
*
* One would use
*
* <code>
* AIThreadSafeDC<Foo> foo;
* </code>
*
* The advantage over AITHREADSAFE is that this object can be allocated with
* new on the heap. For example:
*
* <code>
* AIThreadSafeDC<Foo>* ptr = new AIThreadSafeDC<Foo>;
* </code>
*
* which is not possible with AITHREADSAFE.
*/
template<typename T>
class AIThreadSafeDC : public AIThreadSafe<T>
{
public:
// Construct a wrapper around a default constructed object.
AIThreadSafeDC(void) { new (AIThreadSafe<T>::ptr()) T; }
};
/**
* @brief Read lock object and provide read access.
*/
template<typename T>
struct AIReadAccessConst
{
//! Internal enum for the lock-type of the AI*Access object.
enum state_type
{
readlocked, //!< A AIReadAccessConst or AIReadAccess.
read2writelocked, //!< A AIWriteAccess constructed from a AIReadAccess.
writelocked, //!< A AIWriteAccess constructed from a AIThreadSafe.
write2writelocked //!< A AIWriteAccess constructed from (the AIReadAccess base class of) a AIWriteAccess.
};
//! Construct a AIReadAccessConst from a constant AIThreadSafe.
AIReadAccessConst(AIThreadSafe<T> const& wrapper)
: mWrapper(const_cast<AIThreadSafe<T>&>(wrapper)),
mState(readlocked)
{
mWrapper.mRWLock.rdlock();
}
//! Destruct the AI*Access object.
// These should never be dynamically allocated, so there is no need to make this virtual.
~AIReadAccessConst()
{
if (mState == readlocked)
mWrapper.mRWLock.rdunlock();
else if (mState == writelocked)
mWrapper.mRWLock.wrunlock();
else if (mState == read2writelocked)
mWrapper.mRWLock.wr2rdlock();
}
//! Access the underlaying object for read access.
T const* operator->() const { return mWrapper.ptr(); }
//! Access the underlaying object for read access.
T const& operator*() const { return *mWrapper.ptr(); }
protected:
//! Constructor used by AIReadAccess.
AIReadAccessConst(AIThreadSafe<T>& wrapper, state_type state)
: mWrapper(wrapper), mState(state) { }
AIThreadSafe<T>& mWrapper; //!< Reference to the object that we provide access to.
state_type const mState; //!< The lock state that mWrapper is in.
private:
// Disallow copy constructing directly.
AIReadAccessConst(AIReadAccessConst const&);
};
/**
* @brief Read lock object and provide read access, with possible promotion to write access.
*/
template<typename T>
struct AIReadAccess : public AIReadAccessConst<T>
{
typedef typename AIReadAccessConst<T>::state_type state_type;
using AIReadAccessConst<T>::readlocked;
//! Construct a AIReadAccess from a non-constant AIThreadSafe.
AIReadAccess(AIThreadSafe<T>& wrapper) : AIReadAccessConst<T>(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(); }
protected:
//! Constructor used by AIWriteAccess.
AIReadAccess(AIThreadSafe<T>& wrapper, state_type state) : AIReadAccessConst<T>(wrapper, state) { }
friend class AIWriteAccess<T>;
};
/**
* @brief Write lock object and provide read/write access.
*/
template<typename T>
struct AIWriteAccess : public AIReadAccess<T>
{
using AIReadAccessConst<T>::readlocked;
using AIReadAccessConst<T>::read2writelocked;
using AIReadAccessConst<T>::writelocked;
using AIReadAccessConst<T>::write2writelocked;
//! Construct a AIWriteAccess from a non-constant AIThreadSafe.
AIWriteAccess(AIThreadSafe<T>& wrapper) : AIReadAccess<T>(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();}
//! Promote read access to write access.
explicit AIWriteAccess(AIReadAccess<T>& access)
: AIReadAccess<T>(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked)
{
if (this->mState == read2writelocked)
{
this->mWrapper.mRWLock.rd2wrlock();
}
}
//! Access the underlaying object for (read and) write access.
T* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T& operator*() const { return *this->mWrapper.ptr(); }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* Use AITHREADSAFESIMPLE to define instances of any type, and use AIAccess
* to get access to the instance.
*
* For example,
*
* <code>
* class Foo { public: Foo(int, int); };
*
* AITHREADSAFESIMPLE(Foo, foo, (2, 3));
*
* AIAccess<Foo> foo_w(foo);
* // Use foo_w-> for read and write access.
*
* See also AIThreadSafe
*/
template<typename T>
class AIThreadSafeSimple : public AIThreadSafeBits<T>
{
protected:
// Only this one may access the object (through ptr()).
friend struct AIAccess<T>;
// Locking control.
LLMutex mMutex;
// For use by AIThreadSafeSimpleDC
AIThreadSafeSimple(void) { }
AIThreadSafeSimple(AIAPRPool& parent) : mMutex(parent) { }
public:
// Only for use by AITHREADSAFESIMPLE, see below.
AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); }
};
/**
* @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafeSimple, using an arbitrary constructor.
*
* For example, instead of doing
*
* <code>
* Foo foo(x, y);
* static Bar bar;
* </code>
*
* One can instantiate a thread-safe instance with
*
* <code>
* AITHREADSAFESIMPLE(Foo, foo, (x, y));
* static AITHREADSAFESIMPLE(Bar, bar, );
* </code>
*
* Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeSimpleDC.
*/
#define AITHREADSAFESIMPLE(type, var, paramlist) AIThreadSafeSimple<type> var(new (var.memory()) type paramlist)
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* This class is the same as an AIThreadSafeSimple wrapper, except that it can only
* be used for default constructed objects.
*
* For example, instead of
*
* <code>
* Foo foo;
* </code>
*
* One would use
*
* <code>
* AIThreadSafeSimpleDC<Foo> foo;
* </code>
*
* The advantage over AITHREADSAFESIMPLE is that this object can be allocated with
* new on the heap. For example:
*
* <code>
* AIThreadSafeSimpleDC<Foo>* ptr = new AIThreadSafeSimpleDC<Foo>;
* </code>
*
* which is not possible with AITHREADSAFESIMPLE.
*/
template<typename T>
class AIThreadSafeSimpleDC : public AIThreadSafeSimple<T>
{
public:
// Construct a wrapper around a default constructed object.
AIThreadSafeSimpleDC(void) { new (AIThreadSafeSimple<T>::ptr()) T; }
protected:
// For use by AIThreadSafeSimpleDCRootPool
AIThreadSafeSimpleDC(AIAPRPool& parent) : AIThreadSafeSimple<T>(parent) { new (AIThreadSafeSimple<T>::ptr()) T; }
};
// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
// the root pool before constructing AIThreadSafeSimpleDC.
class AIThreadSafeSimpleDCRootPool_pbase
{
protected:
AIAPRRootPool mRootPool;
private:
template<typename T> friend class AIThreadSafeSimpleDCRootPool;
AIThreadSafeSimpleDCRootPool_pbase(void) { }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* The same as AIThreadSafeSimpleDC except that this class creates its own AIAPRRootPool
* for the internally used mutexes and condition, instead of using the current threads
* root pool. The advantage of this is that it can be used for objects that need to
* be accessed from the destructors of global objects (after main). The disadvantage
* is that it's less efficient to use your own root pool, therefore it's use should be
* restricted to those cases where it is absolutely necessary.
*/
template<typename T>
class AIThreadSafeSimpleDCRootPool : private AIThreadSafeSimpleDCRootPool_pbase, public AIThreadSafeSimpleDC<T>
{
public:
// Construct a wrapper around a default constructed object, using memory allocated
// from the operating system for the internal APR objects (mutexes and conditional),
// as opposed to allocated from the current threads root pool.
AIThreadSafeSimpleDCRootPool(void) :
AIThreadSafeSimpleDCRootPool_pbase(),
AIThreadSafeSimpleDC<T>(mRootPool) { }
};
/**
* @brief Write lock object and provide read/write access.
*/
template<typename T>
struct AIAccess
{
//! Construct a AIAccess from a non-constant AIThreadSafeSimple.
AIAccess(AIThreadSafeSimple<T>& wrapper) : mWrapper(wrapper) { this->mWrapper.mMutex.lock(); }
//! Access the underlaying object for (read and) write access.
T* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T& operator*() const { return *this->mWrapper.ptr(); }
~AIAccess() { this->mWrapper.mMutex.unlock(); }
protected:
AIThreadSafeSimple<T>& mWrapper; //!< Reference to the object that we provide access to.
private:
// Disallow copy constructing directly.
AIAccess(AIAccess const&);
};
#endif

View File

@@ -95,7 +95,6 @@ LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
LLApp::LLApp() : mThreadErrorp(NULL)
{
commonCtor();
startErrorThread();
}
void LLApp::commonCtor()
@@ -123,13 +122,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) :

View File

@@ -256,9 +256,12 @@ protected:
*/
void stepFrame();
private:
/**
* @ brief This method is called once as soon as logging is initialized.
*/
void startErrorThread();
private:
void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)
static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
static void runSyncErrorHandler(); // run IMMEDIATELY when we get an error, ran in the context of the faulting thread.

View File

@@ -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)

View File

@@ -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

View File

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

View File

@@ -43,8 +43,6 @@ class LL_COMMON_API LLCommon
public:
static void initClass();
static void cleanupClass();
private:
static BOOL sAprInitialized;
};
#endif

View File

@@ -55,6 +55,7 @@
#include "llsdserialize.h"
#include "llstl.h"
#include "lltimer.h"
#include "aithreadsafe.h"
extern apr_thread_mutex_t* gCallStacksLogMutexp;
@@ -357,12 +358,15 @@ namespace
void addCallSite(LLError::CallSite&);
void invalidateCallSites();
static Globals& get();
static AIThreadSafeSimple<Globals>& get();
// return the one instance of the globals
private:
CallSiteVector callSites;
friend class AIThreadSafeSimpleDC<Globals>; // Calls constructor.
friend class AIThreadSafeSimple<Globals>; // Calls destructor.
Globals()
: messageStreamInUse(false)
{ }
@@ -386,7 +390,7 @@ namespace
callSites.clear();
}
Globals& Globals::get()
AIThreadSafeSimple<Globals>& Globals::get()
{
/* This pattern, of returning a reference to a static function
variable, is to ensure that this global is constructed before
@@ -394,8 +398,8 @@ namespace
is.
See C++ FAQ Lite, sections 10.12 through 10.14
*/
static Globals* globals = new Globals;
return *globals;
static AIThreadSafeSimpleDCRootPool<Globals>* ts_globals_ptr = new AIThreadSafeSimpleDCRootPool<Globals>;
return *ts_globals_ptr;
}
}
@@ -424,13 +428,16 @@ namespace LLError
int shouldLogCallCounter;
static Settings& get();
static AIThreadSafeSimple<Settings>& get();
static void reset();
static Settings* saveAndReset();
static void restore(Settings*);
static AIThreadSafeSimple<Settings>* saveAndReset();
static void restore(AIThreadSafeSimple<Settings>*);
private:
friend class AIThreadSafeBits<Settings>; // Calls destructor.
friend class AIThreadSafeSimpleDC<Settings>; // Calls constructor.
Settings()
: printLocation(false),
defaultLevel(LLError::LEVEL_DEBUG),
@@ -446,53 +453,42 @@ namespace LLError
for_each(recorders.begin(), recorders.end(),
DeletePointer());
}
static Settings*& getPtr();
static AIThreadSafeSimple<Settings>* sSettings;
};
// Pointer to current AIThreadSafeSimple<Settings> object if any (NULL otherwise).
AIThreadSafeSimple<Settings>* Settings::sSettings;
Settings& Settings::get()
AIThreadSafeSimple<Settings>& Settings::get()
{
Settings* p = getPtr();
if (!p)
if (!sSettings)
{
reset();
p = getPtr();
}
return *p;
return *sSettings;
}
void Settings::reset()
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
delete p;
p = new Settings();
AIAccess<Globals>(Globals::get())->invalidateCallSites();
delete sSettings;
sSettings = new AIThreadSafeSimpleDC<Settings>;
}
Settings* Settings::saveAndReset()
AIThreadSafeSimple<Settings>* Settings::saveAndReset()
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
Settings* originalSettings = p;
p = new Settings();
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIThreadSafeSimple<Settings>* originalSettings = sSettings;
sSettings = new AIThreadSafeSimpleDC<Settings>;
return originalSettings;
}
void Settings::restore(Settings* originalSettings)
void Settings::restore(AIThreadSafeSimple<Settings>* originalSettings)
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
delete p;
p = originalSettings;
}
Settings*& Settings::getPtr()
{
static Settings* currentSettings = NULL;
return currentSettings;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
delete sSettings;
sSettings = originalSettings;
}
}
@@ -596,68 +592,64 @@ namespace LLError
commonInit(dir);
}
void setPrintLocation(AIAccess<Settings> const& settings_w, bool print)
{
settings_w->printLocation = print;
}
void setPrintLocation(bool print)
{
Settings& s = Settings::get();
s.printLocation = print;
setPrintLocation(AIAccess<Settings>(Settings::get()), print);
}
void setFatalFunction(const FatalFunction& f)
{
Settings& s = Settings::get();
s.crashFunction = f;
AIAccess<Settings>(Settings::get())->crashFunction = f;
}
FatalFunction getFatalFunction()
{
Settings& s = Settings::get();
return s.crashFunction;
return AIAccess<Settings>(Settings::get())->crashFunction;
}
void setTimeFunction(TimeFunction f)
{
Settings& s = Settings::get();
s.timeFunction = f;
AIAccess<Settings>(Settings::get())->timeFunction = f;
}
void setDefaultLevel(AIAccess<Settings> const& settings_w, ELevel level)
{
AIAccess<Globals>(Globals::get())->invalidateCallSites();
settings_w->defaultLevel = level;
}
void setDefaultLevel(ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.defaultLevel = level;
setDefaultLevel(AIAccess<Settings>(Settings::get()), level);
}
void setFunctionLevel(const std::string& function_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.functionLevelMap[function_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->functionLevelMap[function_name] = level;
}
void setClassLevel(const std::string& class_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.classLevelMap[class_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->classLevelMap[class_name] = level;
}
void setFileLevel(const std::string& file_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.fileLevelMap[file_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->fileLevelMap[file_name] = level;
}
void setTagLevel(const std::string& tag_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.tagLevelMap[tag_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->tagLevelMap[tag_name] = level;
}
}
@@ -701,18 +693,16 @@ namespace LLError
{
void configure(const LLSD& config)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
AIAccess<Settings> settings_w(Settings::get());
AIAccess<Globals>(Globals::get())->invalidateCallSites();
settings_w->functionLevelMap.clear();
settings_w->classLevelMap.clear();
settings_w->fileLevelMap.clear();
settings_w->tagLevelMap.clear();
settings_w->uniqueLogMessages.clear();
g.invalidateCallSites();
s.functionLevelMap.clear();
s.classLevelMap.clear();
s.fileLevelMap.clear();
s.tagLevelMap.clear();
s.uniqueLogMessages.clear();
setPrintLocation(config["print-location"]);
setDefaultLevel(decodeLevel(config["default-level"]));
setPrintLocation(settings_w, config["print-location"]);
setDefaultLevel(settings_w, decodeLevel(config["default-level"]));
LLSD sets = config["settings"];
LLSD::array_const_iterator a, end;
@@ -722,10 +712,10 @@ namespace LLError
ELevel level = decodeLevel(entry["level"]);
setLevels(s.functionLevelMap, entry["functions"], level);
setLevels(s.classLevelMap, entry["classes"], level);
setLevels(s.fileLevelMap, entry["files"], level);
setLevels(s.tagLevelMap, entry["tags"], level);
setLevels(settings_w->functionLevelMap, entry["functions"], level);
setLevels(settings_w->classLevelMap, entry["classes"], level);
setLevels(settings_w->fileLevelMap, entry["files"], level);
setLevels(settings_w->tagLevelMap, entry["tags"], level);
}
}
}
@@ -742,26 +732,34 @@ namespace LLError
void addRecorder(Recorder* recorder)
void addRecorder(AIAccess<Settings> const& settings_w, Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
Settings& s = Settings::get();
s.recorders.push_back(recorder);
settings_w->recorders.push_back(recorder);
}
void addRecorder(Recorder* recorder)
{
addRecorder(AIAccess<Settings>(Settings::get()), recorder);
}
void removeRecorder(AIAccess<Settings> const& settings_w, Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
settings_w->recorders.erase(
std::remove(settings_w->recorders.begin(), settings_w->recorders.end(), recorder),
settings_w->recorders.end());
}
void removeRecorder(Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
Settings& s = Settings::get();
s.recorders.erase(
std::remove(s.recorders.begin(), s.recorders.end(), recorder),
s.recorders.end());
removeRecorder(AIAccess<Settings>(Settings::get()), recorder);
}
}
@@ -769,12 +767,12 @@ namespace LLError
{
void logToFile(const std::string& file_name)
{
LLError::Settings& s = LLError::Settings::get();
AIAccess<Settings> settings_w(Settings::get());
removeRecorder(s.fileRecorder);
delete s.fileRecorder;
s.fileRecorder = NULL;
s.fileRecorderFileName.clear();
removeRecorder(settings_w, settings_w->fileRecorder);
delete settings_w->fileRecorder;
settings_w->fileRecorder = NULL;
settings_w->fileRecorderFileName.clear();
if (file_name.empty())
{
@@ -788,54 +786,51 @@ namespace LLError
return;
}
s.fileRecorderFileName = file_name;
s.fileRecorder = f;
addRecorder(f);
settings_w->fileRecorderFileName = file_name;
settings_w->fileRecorder = f;
addRecorder(settings_w, f);
}
void logToFixedBuffer(LLLineBuffer* fixedBuffer)
{
LLError::Settings& s = LLError::Settings::get();
AIAccess<Settings> settings_w(Settings::get());
removeRecorder(s.fixedBufferRecorder);
delete s.fixedBufferRecorder;
s.fixedBufferRecorder = NULL;
removeRecorder(settings_w, settings_w->fixedBufferRecorder);
delete settings_w->fixedBufferRecorder;
settings_w->fixedBufferRecorder = NULL;
if (!fixedBuffer)
{
return;
}
s.fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
addRecorder(s.fixedBufferRecorder);
settings_w->fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
addRecorder(settings_w, settings_w->fixedBufferRecorder);
}
std::string logFileName()
{
LLError::Settings& s = LLError::Settings::get();
return s.fileRecorderFileName;
return AIAccess<Settings>(Settings::get())->fileRecorderFileName;
}
}
namespace
{
void writeToRecorders(LLError::ELevel level, const std::string& message)
void writeToRecorders(AIAccess<LLError::Settings> const& settings_w, LLError::ELevel level, const std::string& message)
{
LLError::Settings& s = LLError::Settings::get();
std::string messageWithTime;
for (Recorders::const_iterator i = s.recorders.begin();
i != s.recorders.end();
for (Recorders::const_iterator i = settings_w->recorders.begin();
i != settings_w->recorders.end();
++i)
{
LLError::Recorder* r = *i;
if (r->wantsTime() && s.timeFunction != NULL)
if (r->wantsTime() && settings_w->timeFunction != NULL)
{
if (messageWithTime.empty())
{
messageWithTime = s.timeFunction() + " " + message;
messageWithTime = settings_w->timeFunction() + " " + message;
}
r->recordMessage(level, messageWithTime);
@@ -877,6 +872,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)
@@ -952,10 +950,9 @@ namespace LLError
return false;
}
Globals& g = Globals::get();
Settings& s = Settings::get();
AIAccess<Settings> settings_w(Settings::get());
s.shouldLogCallCounter += 1;
settings_w->shouldLogCallCounter += 1;
std::string class_name = className(site.mClassInfo);
std::string function_name = functionName(site.mFunction);
@@ -964,20 +961,20 @@ namespace LLError
function_name = class_name + "::" + function_name;
}
ELevel compareLevel = s.defaultLevel;
ELevel compareLevel = settings_w->defaultLevel;
// The most specific match found will be used as the log level,
// since the computation short circuits.
// So, in increasing order of importance:
// Default < Broad Tag < File < Class < Function < Narrow Tag
((site.mNarrowTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(s.functionLevelMap, function_name, compareLevel)
|| checkLevelMap(s.classLevelMap, class_name, compareLevel)
|| checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mBroadTag, compareLevel) : false);
((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)
|| checkLevelMap(settings_w->classLevelMap, class_name, compareLevel)
|| checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false);
site.mCached = true;
g.addCallSite(site);
AIAccess<Globals>(Globals::get())->addCallSite(site);
return site.mShouldLog = site.mLevel >= compareLevel;
}
@@ -987,16 +984,16 @@ namespace LLError
LogLock lock;
if (lock.ok())
{
Globals& g = Globals::get();
AIAccess<Globals> globals(Globals::get());
if (!g.messageStreamInUse)
if (!globals->messageStreamInUse)
{
g.messageStreamInUse = true;
return &g.messageStream;
globals->messageStreamInUse = true;
return &globals->messageStream; // Returns pointer to member of unlocked object, apparently "protected" by having set globals->messageStreamInUse.
}
}
return new std::ostringstream;
return new std::ostringstream; // Holy memory leak.
}
void Log::flush(std::ostringstream* out, char* message)
@@ -1017,12 +1014,12 @@ namespace LLError
message[127] = '\0' ;
}
Globals& g = Globals::get();
if (out == &g.messageStream)
AIAccess<Globals> globals(Globals::get());
if (out == &globals->messageStream)
{
g.messageStream.clear();
g.messageStream.str("");
g.messageStreamInUse = false;
globals->messageStream.clear();
globals->messageStream.str("");
globals->messageStreamInUse = false;
}
else
{
@@ -1039,28 +1036,31 @@ namespace LLError
return;
}
Globals& g = Globals::get();
Settings& s = Settings::get();
std::string message = out->str();
if (out == &g.messageStream)
{
g.messageStream.clear();
g.messageStream.str("");
g.messageStreamInUse = false;
}
else
{
delete out;
AIAccess<Globals> globals(Globals::get());
if (out == &globals->messageStream)
{
globals->messageStream.clear();
globals->messageStream.str("");
globals->messageStreamInUse = false;
}
else
{
delete out;
}
}
AIAccess<Settings> settings_w(Settings::get());
if (site.mLevel == LEVEL_ERROR)
{
std::ostringstream fatalMessage;
fatalMessage << abbreviateFile(site.mFile)
<< "(" << site.mLine << ") : error";
writeToRecorders(site.mLevel, fatalMessage.str());
writeToRecorders(settings_w, site.mLevel, fatalMessage.str());
}
@@ -1075,7 +1075,7 @@ namespace LLError
default: prefix << "XXX: "; break;
};
if (s.printLocation)
if (settings_w->printLocation)
{
prefix << abbreviateFile(site.mFile)
<< "(" << site.mLine << ") : ";
@@ -1093,8 +1093,8 @@ namespace LLError
if (site.mPrintOnce)
{
std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
if (messageIter != s.uniqueLogMessages.end())
std::map<std::string, unsigned int>::iterator messageIter = settings_w->uniqueLogMessages.find(message);
if (messageIter != settings_w->uniqueLogMessages.end())
{
messageIter->second++;
unsigned int num_messages = messageIter->second;
@@ -1110,14 +1110,14 @@ namespace LLError
else
{
prefix << "ONCE: ";
s.uniqueLogMessages[message] = 1;
settings_w->uniqueLogMessages[message] = 1;
}
}
if (site.mPrintOnce)
{
std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
if (messageIter != s.uniqueLogMessages.end())
std::map<std::string, unsigned int>::iterator messageIter = settings_w->uniqueLogMessages.find(message);
if (messageIter != settings_w->uniqueLogMessages.end())
{
messageIter->second++;
unsigned int num_messages = messageIter->second;
@@ -1133,18 +1133,18 @@ namespace LLError
else
{
prefix << "ONCE: ";
s.uniqueLogMessages[message] = 1;
settings_w->uniqueLogMessages[message] = 1;
}
}
prefix << message;
message = prefix.str();
writeToRecorders(site.mLevel, message);
writeToRecorders(settings_w, site.mLevel, message);
if (site.mLevel == LEVEL_ERROR && s.crashFunction)
if (site.mLevel == LEVEL_ERROR && settings_w->crashFunction)
{
s.crashFunction(message);
settings_w->crashFunction(message);
}
}
}
@@ -1154,14 +1154,16 @@ namespace LLError
namespace LLError
{
Settings* saveAndResetSettings()
class ThreadSafeSettings { };
ThreadSafeSettings* saveAndResetSettings()
{
return Settings::saveAndReset();
return reinterpret_cast<ThreadSafeSettings*>(Settings::saveAndReset());
}
void restoreSettings(Settings* s)
void restoreSettings(ThreadSafeSettings* s)
{
return Settings::restore(s);
Settings::restore(reinterpret_cast<AIThreadSafeSimple<Settings>*>(s));
}
std::string removePrefix(std::string& s, const std::string& p)
@@ -1207,8 +1209,7 @@ namespace LLError
int shouldLogCallCount()
{
Settings& s = Settings::get();
return s.shouldLogCallCounter;
return AIAccess<Settings>(Settings::get())->shouldLogCallCounter;
}
#if LL_WINDOWS

View File

@@ -167,9 +167,9 @@ namespace LLError
Utilities for use by the unit tests of LLError itself.
*/
class Settings;
LL_COMMON_API Settings* saveAndResetSettings();
LL_COMMON_API void restoreSettings(Settings *);
class ThreadSafeSettings;
LL_COMMON_API ThreadSafeSettings* saveAndResetSettings();
LL_COMMON_API void restoreSettings(ThreadSafeSettings *);
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
LL_COMMON_API int shouldLogCallCount();

View File

@@ -36,8 +36,7 @@
LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
: LLLineBuffer(),
mMaxLines(max_lines),
mMutex(NULL)
mMaxLines(max_lines)
{
mTimer.reset();
}

View File

@@ -107,17 +107,7 @@
#endif
// Static linking with apr on windows needs to be declared.
#ifdef LL_WINDOWS
#ifndef APR_DECLARE_STATIC
#define APR_DECLARE_STATIC // For APR on Windows
#endif
#ifndef APU_DECLARE_STATIC
#define APU_DECLARE_STATIC // For APR util on Windows
#endif
#endif
// Deal with the differeneces on Windows
// Deal with the differences on Windows
#if defined(LL_WINDOWS)
#define BOOST_REGEX_NO_LIB 1
#define CURL_STATICLIB 1
@@ -169,7 +159,6 @@
#define LL_DLLIMPORT
#endif // LL_WINDOWS
/*
#ifdef llcommon_EXPORTS
// Compiling llcommon (shared)
#define LL_COMMON_API LL_DLLEXPORT
@@ -177,9 +166,5 @@
// Using llcommon (shared)
#define LL_COMMON_API LL_DLLIMPORT
#endif // llcommon_EXPORTS
*/
//I hate DLLs -SG
#define LL_COMMON_API
#endif // not LL_LINDEN_PREPROCESSOR_H

View 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

View File

@@ -36,6 +36,7 @@
#include <string>
#include <cstdio>
#include <algorithm>
#include <map>
#if LL_LINUX || LL_SOLARIS
#include <wctype.h>
@@ -212,7 +213,7 @@ public:
/////////////////////////////////////////////////////////////////////////////////////////
// Static Utility functions that operate on std::strings
static std::basic_string<T> null;
static std::basic_string<T> const null;
typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
@@ -303,7 +304,7 @@ public:
};
template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
template<class T> std::basic_string<T> const LLStringUtilBase<T>::null;
typedef LLStringUtilBase<char> LLStringUtil;
typedef LLStringUtilBase<llwchar> LLWStringUtil;

View File

@@ -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;
}
}

View File

@@ -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,102 @@ public:
mMutex->unlock();
}
private:
LLMutex* mMutex;
LLMutexBase* mMutex;
};
class AIRWLock
{
public:
AIRWLock(AIAPRPool& parent = LLThread::tldata().mRootPool) :
mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { }
private:
LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access.
LLCondition mNoHoldersCondition; //!< Access control for mHoldersCount. Condition true when there are no more holders.
int mHoldersCount; //!< Number of readers or -1 if a writer locked this object.
// This is volatile because we read it outside the critical area of mWriterWaitingMutex, at [1].
// That means that other threads can change it while we are already in the (inlined) function rdlock.
// Without volatile, the following assembly would fail:
// register x = mWriterIsWaiting;
// /* some thread changes mWriterIsWaiting */
// if (x ...
// However, because the function is fuzzy to begin with (we don't mind that this race
// condition exists) it would work fine without volatile. So, basically it's just here
// out of principle ;). -- Aleric
bool volatile mWriterIsWaiting; //!< True when there is a writer waiting for write access.
public:
void rdlock(bool high_priority = false)
{
// Give a writer a higher priority (kinda fuzzy).
if (mWriterIsWaiting && !high_priority) // [1] If there is a writer interested,
{
mWriterWaitingMutex.lock(); // [2] then give it precedence and wait here.
// If we get here then the writer got it's access; mHoldersCount == -1.
mWriterWaitingMutex.unlock();
}
mNoHoldersCondition.lock(); // [3] Get exclusive access to mHoldersCount.
while (mHoldersCount == -1) // [4]
{
mNoHoldersCondition.wait(); // [5] Wait till mHoldersCount is (or just was) 0.
}
++mHoldersCount; // One more reader.
mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
}
void rdunlock(void)
{
mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
if (--mHoldersCount == 0) // Was this the last reader?
{
mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
}
mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
}
void wrlock(void)
{
mWriterWaitingMutex.lock(); // Block new readers, see [2],
mWriterIsWaiting = true; // from this moment on, see [1].
mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
while (mHoldersCount != 0) // Other readers or writers have this lock?
{
mNoHoldersCondition.wait(); // [6] Wait till mHoldersCount is (or just was) 0.
}
mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
}
void wrunlock(void)
{
mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
mHoldersCount = 0; // We have no writer anymore.
mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
}
void rd2wrlock(void)
{
mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount. Blocks new readers at [3].
if (--mHoldersCount > 0) // Any other reads left?
{
mWriterWaitingMutex.lock(); // Block new readers, see [2],
mWriterIsWaiting = true; // from this moment on, see [1].
while (mHoldersCount != 0) // Other readers (still) have this lock?
{
mNoHoldersCondition.wait(); // [7] Wait till mHoldersCount is (or just was) 0.
}
mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
}
mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
}
void wr2rdlock(void)
{
mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
mHoldersCount = 1; // Turn writer into a reader.
mNoHoldersCondition.signal(); // Tell waiting readers, see [5].
mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
}
};
//============================================================================
@@ -187,7 +346,6 @@ void LLThread::unlockData()
mRunCondition->unlock();
}
//============================================================================
// see llmemory.h for LLPointer<> definition

View File

@@ -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)

View File

@@ -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;
};