summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt6
-rw-r--r--indra/llcommon/llaccountingcost.h (renamed from indra/llcommon/llaccountingquota.h)24
-rw-r--r--indra/llcommon/llapp.cpp4
-rw-r--r--indra/llcommon/llapr.cpp445
-rw-r--r--indra/llcommon/llapr.h107
-rw-r--r--indra/llcommon/llaprpool.cpp202
-rw-r--r--indra/llcommon/llaprpool.h256
-rw-r--r--indra/llcommon/llassettype.cpp3
-rw-r--r--indra/llcommon/llassettype.h8
-rw-r--r--indra/llcommon/llcommon.cpp13
-rw-r--r--indra/llcommon/llcommon.h2
-rw-r--r--indra/llcommon/llerror.cpp3
-rw-r--r--indra/llcommon/llerror.h1
-rw-r--r--indra/llcommon/lleventapi.cpp30
-rw-r--r--indra/llcommon/lleventapi.h83
-rw-r--r--indra/llcommon/llevents.cpp11
-rw-r--r--indra/llcommon/llfasttimer_class.cpp19
-rw-r--r--indra/llcommon/llfasttimer_class.h2
-rw-r--r--indra/llcommon/llfixedbuffer.cpp3
-rw-r--r--indra/llcommon/llinstancetracker.cpp19
-rw-r--r--indra/llcommon/llinstancetracker.h113
-rw-r--r--indra/llcommon/llmemory.cpp138
-rw-r--r--indra/llcommon/llmemory.h12
-rw-r--r--indra/llcommon/llqueuedthread.cpp7
-rw-r--r--indra/llcommon/llqueuedthread.h2
-rw-r--r--indra/llcommon/llscopedvolatileaprpool.h52
-rw-r--r--indra/llcommon/llsdserialize_xml.cpp3
-rw-r--r--indra/llcommon/llsingleton.h24
-rw-r--r--indra/llcommon/llstring.cpp13
-rw-r--r--indra/llcommon/llthread.cpp241
-rw-r--r--indra/llcommon/llthread.h133
-rw-r--r--indra/llcommon/llthreadsafequeue.cpp15
-rw-r--r--indra/llcommon/llthreadsafequeue.h16
-rw-r--r--[-rwxr-xr-x]indra/llcommon/llversionviewer.h4
-rw-r--r--indra/llcommon/llworkerthread.cpp12
-rw-r--r--indra/llcommon/llworkerthread.h5
-rw-r--r--indra/llcommon/stdenums.h5
-rw-r--r--indra/llcommon/tests/llinstancetracker_test.cpp64
-rw-r--r--indra/llcommon/tests/llsingleton_test.cpp76
-rw-r--r--indra/llcommon/tests/llstring_test.cpp8
40 files changed, 1214 insertions, 970 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index cc6bddc4da..0a3eaec5c5 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -31,7 +31,6 @@ set(llcommon_SOURCE_FILES
llallocator_heap_profile.cpp
llapp.cpp
llapr.cpp
- llaprpool.cpp
llassettype.cpp
llavatarname.cpp
llbase32.cpp
@@ -81,7 +80,6 @@ set(llcommon_SOURCE_FILES
llrand.cpp
llrefcount.cpp
llrun.cpp
- llscopedvolatileaprpool.h
llsd.cpp
llsdserialize.cpp
llsdserialize_xml.cpp
@@ -117,14 +115,13 @@ set(llcommon_HEADER_FILES
indra_constants.h
linden_common.h
linked_lists.h
- llaccountingquota.h
+ llaccountingcost.h
llallocator.h
llallocator_heap_profile.h
llagentconstants.h
llavatarname.h
llapp.h
llapr.h
- llaprpool.h
llassettype.h
llassoclist.h
llavatarconstants.h
@@ -322,6 +319,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}"
"${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
+ LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
diff --git a/indra/llcommon/llaccountingquota.h b/indra/llcommon/llaccountingcost.h
index 140333de07..0ef3b50c6d 100644
--- a/indra/llcommon/llaccountingquota.h
+++ b/indra/llcommon/llaccountingcost.h
@@ -1,5 +1,5 @@
/**
- * @file llaccountingquota.h
+ * @file llaccountingcost.h
* @
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
@@ -58,22 +58,28 @@ struct ParcelQuota
F32 mParcelCapacity;
};
-struct SelectionQuota
+//SelectionQuota atm does not require a id
+struct SelectionCost
{
- SelectionQuota( LLUUID localId, F32 renderCost, F32 physicsCost, F32 networkCost, F32 simulationCost )
- : mLocalId( localId)
- , mRenderCost( renderCost )
- , mPhysicsCost( physicsCost )
+ SelectionCost( /*LLTransactionID transactionId, */ F32 physicsCost, F32 networkCost, F32 simulationCost )
+ //: mTransactionId( transactionId)
+ : mPhysicsCost( physicsCost )
, mNetworkCost( networkCost )
, mSimulationCost( simulationCost )
{
}
- SelectionQuota() {}
+ SelectionCost()
+ : mPhysicsCost( 0.0f )
+ , mNetworkCost( 0.0f )
+ , mSimulationCost( 0.0f )
+ {}
- F32 mRenderCost, mPhysicsCost, mNetworkCost, mSimulationCost;
- LLUUID mLocalId;
+ F32 mPhysicsCost, mNetworkCost, mSimulationCost;
+ //LLTransactionID mTransactionId;
};
+typedef enum { Roots = 0 , Prims } eSelectionType;
+
#endif
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index a8b7106078..ed192a9975 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -137,6 +137,10 @@ void LLApp::commonCtor()
mOptions.append(sd);
}
+ // Make sure we clean up APR when we exit
+ // Don't need to do this if we're cleaning up APR in the destructor
+ //atexit(ll_cleanup_apr);
+
// Set the application to this instance.
sApplication = this;
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 1e4a51102e..d1c44c9403 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -29,8 +29,212 @@
#include "linden_common.h"
#include "llapr.h"
#include "apr_dso.h"
-#include "llscopedvolatileaprpool.h"
+apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
+LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
+apr_thread_mutex_t *gLogMutexp = NULL;
+apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
+
+const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
+
+void ll_init_apr()
+{
+ if (!gAPRPoolp)
+ {
+ // Initialize APR and create the global pool
+ apr_initialize();
+ apr_pool_create(&gAPRPoolp, NULL);
+
+ // Initialize the logging mutex
+ apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
+ apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
+ }
+
+ if(!LLAPRFile::sAPRFilePoolp)
+ {
+ LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ;
+ }
+}
+
+
+void ll_cleanup_apr()
+{
+ LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
+
+ if (gLogMutexp)
+ {
+ // Clean up the logging mutex
+
+ // All other threads NEED to be done before we clean up APR, so this is okay.
+ apr_thread_mutex_destroy(gLogMutexp);
+ gLogMutexp = NULL;
+ }
+ if (gCallStacksLogMutexp)
+ {
+ // Clean up the logging mutex
+
+ // All other threads NEED to be done before we clean up APR, so this is okay.
+ apr_thread_mutex_destroy(gCallStacksLogMutexp);
+ gCallStacksLogMutexp = NULL;
+ }
+ if (gAPRPoolp)
+ {
+ apr_pool_destroy(gAPRPoolp);
+ gAPRPoolp = NULL;
+ }
+ if (LLAPRFile::sAPRFilePoolp)
+ {
+ delete LLAPRFile::sAPRFilePoolp ;
+ LLAPRFile::sAPRFilePoolp = NULL ;
+ }
+ apr_terminate();
+}
+
+//
+//
+//LLAPRPool
+//
+LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
+ : mParent(parent),
+ mReleasePoolFlag(releasePoolFlag),
+ mMaxSize(size),
+ mPool(NULL)
+{
+ createAPRPool() ;
+}
+
+LLAPRPool::~LLAPRPool()
+{
+ releaseAPRPool() ;
+}
+
+void LLAPRPool::createAPRPool()
+{
+ if(mPool)
+ {
+ return ;
+ }
+
+ mStatus = apr_pool_create(&mPool, mParent);
+ ll_apr_warn_status(mStatus) ;
+
+ if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes.
+ {
+ apr_allocator_t *allocator = apr_pool_allocator_get(mPool);
+ if (allocator)
+ {
+ apr_allocator_max_free_set(allocator, mMaxSize) ;
+ }
+ }
+}
+
+void LLAPRPool::releaseAPRPool()
+{
+ if(!mPool)
+ {
+ return ;
+ }
+
+ if(!mParent || mReleasePoolFlag)
+ {
+ apr_pool_destroy(mPool) ;
+ mPool = NULL ;
+ }
+}
+
+//virtual
+apr_pool_t* LLAPRPool::getAPRPool()
+{
+ return mPool ;
+}
+
+LLVolatileAPRPool::LLVolatileAPRPool(BOOL is_local, apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
+ : LLAPRPool(parent, size, releasePoolFlag),
+ mNumActiveRef(0),
+ mNumTotalRef(0),
+ mMutexPool(NULL),
+ mMutexp(NULL)
+{
+ //create mutex
+ if(!is_local) //not a local apr_pool, that is: shared by multiple threads.
+ {
+ apr_pool_create(&mMutexPool, NULL); // Create a pool for mutex
+ apr_thread_mutex_create(&mMutexp, APR_THREAD_MUTEX_UNNESTED, mMutexPool);
+ }
+}
+
+LLVolatileAPRPool::~LLVolatileAPRPool()
+{
+ //delete mutex
+ if(mMutexp)
+ {
+ apr_thread_mutex_destroy(mMutexp);
+ apr_pool_destroy(mMutexPool);
+ }
+}
+
+//
+//define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+//
+//virtual
+apr_pool_t* LLVolatileAPRPool::getAPRPool()
+{
+ return LLVolatileAPRPool::getVolatileAPRPool() ;
+}
+
+apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
+{
+ LLScopedLock lock(mMutexp) ;
+
+ mNumTotalRef++ ;
+ mNumActiveRef++ ;
+
+ if(!mPool)
+ {
+ createAPRPool() ;
+ }
+
+ return mPool ;
+}
+
+void LLVolatileAPRPool::clearVolatileAPRPool()
+{
+ LLScopedLock lock(mMutexp) ;
+
+ if(mNumActiveRef > 0)
+ {
+ mNumActiveRef--;
+ if(mNumActiveRef < 1)
+ {
+ if(isFull())
+ {
+ mNumTotalRef = 0 ;
+
+ //destroy the apr_pool.
+ releaseAPRPool() ;
+ }
+ else
+ {
+ //This does not actually free the memory,
+ //it just allows the pool to re-use this memory for the next allocation.
+ apr_pool_clear(mPool) ;
+ }
+ }
+ }
+ else
+ {
+ llassert_always(mNumActiveRef > 0) ;
+ }
+
+ //paranoia check if the pool is jammed.
+ //will remove the check before going to release.
+ llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
+}
+
+BOOL LLVolatileAPRPool::isFull()
+{
+ return mNumTotalRef > FULL_VOLATILE_APR_POOL ;
+}
//---------------------------------------------------------------------
//
// LLScopedLock
@@ -109,17 +313,15 @@ void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle)
//
LLAPRFile::LLAPRFile()
: mFile(NULL),
- mVolatileFilePoolp(NULL),
- mRegularFilePoolp(NULL)
+ mCurrentFilePoolp(NULL)
{
}
-LLAPRFile::LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep, access_t access_type)
+LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool)
: mFile(NULL),
- mVolatileFilePoolp(NULL),
- mRegularFilePoolp(NULL)
+ mCurrentFilePoolp(NULL)
{
- open(filename, flags, access_type, sizep);
+ open(filename, flags, pool);
}
LLAPRFile::~LLAPRFile()
@@ -136,58 +338,36 @@ apr_status_t LLAPRFile::close()
mFile = NULL ;
}
- if (mVolatileFilePoolp)
+ if(mCurrentFilePoolp)
{
- mVolatileFilePoolp->clearVolatileAPRPool() ;
- mVolatileFilePoolp = NULL ;
- }
-
- if (mRegularFilePoolp)
- {
- delete mRegularFilePoolp;
- mRegularFilePoolp = NULL;
+ mCurrentFilePoolp->clearVolatileAPRPool() ;
+ mCurrentFilePoolp = NULL ;
}
return ret ;
}
-apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep)
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep)
{
- llassert_always(!mFile);
- llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp);
+ apr_status_t s ;
- apr_status_t status;
- {
- apr_pool_t* apr_file_open_pool; // The use of apr_pool_t is OK here.
- // This is a temporary variable for a pool that is passed directly to apr_file_open below.
- if (access_type == short_lived)
- {
- // Use a "volatile" thread-local pool.
- mVolatileFilePoolp = &LLThreadLocalData::tldata().mVolatileAPRPool;
- // Access the pool and increment its reference count.
- // The reference count of LLVolatileAPRPool objects will be decremented
- // again in LLAPRFile::close by calling mVolatileFilePoolp->clearVolatileAPRPool().
- apr_file_open_pool = mVolatileFilePoolp->getVolatileAPRPool();
- }
- else
- {
- mRegularFilePoolp = new LLAPRPool(LLThreadLocalData::tldata().mRootPool);
- apr_file_open_pool = (*mRegularFilePoolp)();
- }
- status = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_file_open_pool);
- }
- if (status != APR_SUCCESS || !mFile)
+ //check if already open some file
+ llassert_always(!mFile) ;
+ llassert_always(!mCurrentFilePoolp) ;
+
+ apr_pool_t* apr_pool = pool ? pool->getVolatileAPRPool() : NULL ;
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, getAPRFilePool(apr_pool));
+
+ if (s != APR_SUCCESS || !mFile)
{
mFile = NULL ;
- close() ;
+
if (sizep)
{
*sizep = 0;
}
- return status;
}
-
- if (sizep)
+ else if (sizep)
{
S32 file_size = 0;
apr_off_t offset = 0;
@@ -201,7 +381,49 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
*sizep = file_size;
}
- return status;
+ if(!mCurrentFilePoolp)
+ {
+ mCurrentFilePoolp = pool ;
+
+ if(!mFile)
+ {
+ close() ;
+ }
+ }
+
+ return s ;
+}
+
+//use gAPRPoolp.
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool)
+{
+ apr_status_t s;
+
+ //check if already open some file
+ llassert_always(!mFile) ;
+ llassert_always(!mCurrentFilePoolp) ;
+ llassert_always(use_global_pool) ; //be aware of using gAPRPoolp.
+
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
+ if (s != APR_SUCCESS || !mFile)
+ {
+ mFile = NULL ;
+ close() ;
+ return s;
+ }
+
+ return s;
+}
+
+apr_pool_t* LLAPRFile::getAPRFilePool(apr_pool_t* pool)
+{
+ if(!pool)
+ {
+ mCurrentFilePoolp = sAPRFilePoolp ;
+ return mCurrentFilePoolp->getVolatileAPRPool() ;
+ }
+
+ return pool ;
}
// File I/O
@@ -260,6 +482,45 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
//
//static
+apr_status_t LLAPRFile::close(apr_file_t* file_handle, LLVolatileAPRPool* pool)
+{
+ apr_status_t ret = APR_SUCCESS ;
+ if(file_handle)
+ {
+ ret = apr_file_close(file_handle);
+ file_handle = NULL ;
+ }
+
+ if(pool)
+ {
+ pool->clearVolatileAPRPool() ;
+ }
+
+ return ret ;
+}
+
+//static
+apr_file_t* LLAPRFile::open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
+{
+ apr_status_t s;
+ apr_file_t* file_handle ;
+
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+
+ s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());
+ if (s != APR_SUCCESS || !file_handle)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL;
+ file_handle = NULL ;
+ close(file_handle, pool) ;
+ return NULL;
+ }
+
+ return file_handle ;
+}
+
+//static
S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
{
if(!file_handle)
@@ -292,15 +553,13 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
}
//static
-S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
+S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
- apr_file_t* file_handle;
- LLScopedVolatileAPRPool pool;
- apr_status_t s = apr_file_open(&file_handle, filename.c_str(), APR_READ|APR_BINARY, APR_OS_DEFAULT, pool);
- if (s != APR_SUCCESS || !file_handle)
+ //*****************************************
+ apr_file_t* file_handle = open(filename, pool, APR_READ|APR_BINARY);
+ //*****************************************
+ if (!file_handle)
{
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL;
return 0;
}
@@ -330,13 +589,14 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb
}
}
- apr_file_close(file_handle);
-
+ //*****************************************
+ close(file_handle, pool) ;
+ //*****************************************
return (S32)bytes_read;
}
//static
-S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
+S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
if (offset < 0)
@@ -345,13 +605,11 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
offset = 0;
}
- apr_file_t* file_handle;
- LLScopedVolatileAPRPool pool;
- apr_status_t s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
- if (s != APR_SUCCESS || !file_handle)
+ //*****************************************
+ apr_file_t* file_handle = open(filename, pool, flags);
+ //*****************************************
+ if (!file_handle)
{
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL;
return 0;
}
@@ -381,18 +639,21 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
}
}
- apr_file_close(file_handle);
+ //*****************************************
+ LLAPRFile::close(file_handle, pool);
+ //*****************************************
return (S32)bytes_written;
}
//static
-bool LLAPRFile::remove(const std::string& filename)
+bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
{
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_file_remove(filename.c_str(), pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_file_remove(filename.c_str(), pool->getVolatileAPRPool());
+ pool->clearVolatileAPRPool() ;
if (s != APR_SUCCESS)
{
@@ -404,12 +665,13 @@ bool LLAPRFile::remove(const std::string& filename)
}
//static
-bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
+bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
{
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_file_rename(filename.c_str(), newname.c_str(), pool->getVolatileAPRPool());
+ pool->clearVolatileAPRPool() ;
if (s != APR_SUCCESS)
{
@@ -421,44 +683,49 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
}
//static
-bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags)
+bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
{
- apr_file_t* file_handle;
+ apr_file_t* apr_file;
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());
- if (s != APR_SUCCESS || !file_handle)
+ if (s != APR_SUCCESS || !apr_file)
{
+ pool->clearVolatileAPRPool() ;
return false;
}
else
{
- apr_file_close(file_handle);
+ apr_file_close(apr_file) ;
+ pool->clearVolatileAPRPool() ;
return true;
}
}
//static
-S32 LLAPRFile::size(const std::string& filename)
+S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
{
- apr_file_t* file_handle;
+ apr_file_t* apr_file;
apr_finfo_t info;
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool->getVolatileAPRPool());
- if (s != APR_SUCCESS || !file_handle)
+ if (s != APR_SUCCESS || !apr_file)
{
+ pool->clearVolatileAPRPool() ;
+
return 0;
}
else
{
- apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, file_handle);
+ apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
- apr_file_close(file_handle) ;
+ apr_file_close(apr_file) ;
+ pool->clearVolatileAPRPool() ;
if (s == APR_SUCCESS)
{
@@ -472,29 +739,31 @@ S32 LLAPRFile::size(const std::string& filename)
}
//static
-bool LLAPRFile::makeDir(const std::string& dirname)
+bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
{
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool->getVolatileAPRPool());
+ pool->clearVolatileAPRPool() ;
if (s != APR_SUCCESS)
{
ll_apr_warn_status(s);
- LL_WARNS("APR") << " while attempting to make directory: " << dirname << LL_ENDL;
+ LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
return false;
}
return true;
}
//static
-bool LLAPRFile::removeDir(const std::string& dirname)
+bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
{
apr_status_t s;
- LLScopedVolatileAPRPool pool;
- s = apr_file_remove(dirname.c_str(), pool);
+ pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
+ s = apr_file_remove(dirname.c_str(), pool->getVolatileAPRPool());
+ pool->clearVolatileAPRPool() ;
if (s != APR_SUCCESS)
{
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 3f846f1314..af33ce666f 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -50,9 +50,71 @@
#include "apr_atomic.h"
#include "llstring.h"
+extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
+extern apr_thread_mutex_t* gCallStacksLogMutexp;
+
struct apr_dso_handle_t;
-class LLAPRPool;
-class LLVolatileAPRPool;
+
+/**
+ * @brief initialize the common apr constructs -- apr itself, the
+ * global pool, and a mutex.
+ */
+void LL_COMMON_API ll_init_apr();
+
+/**
+ * @brief Cleanup those common apr constructs.
+ */
+void LL_COMMON_API ll_cleanup_apr();
+
+//
+//LL apr_pool
+//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
+//
+class LL_COMMON_API LLAPRPool
+{
+public:
+ LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
+ virtual ~LLAPRPool() ;
+
+ virtual apr_pool_t* getAPRPool() ;
+ apr_status_t getStatus() {return mStatus ; }
+
+protected:
+ void releaseAPRPool() ;
+ void createAPRPool() ;
+
+protected:
+ apr_pool_t* mPool ; //pointing to an apr_pool
+ apr_pool_t* mParent ; //parent pool
+ apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
+ apr_status_t mStatus ; //status when creating the pool
+ BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
+};
+
+//
+//volatile LL apr_pool
+//which clears memory automatically.
+//so it can not hold static data or data after memory is cleared
+//
+class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool
+{
+public:
+ LLVolatileAPRPool(BOOL is_local = TRUE, apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
+ virtual ~LLVolatileAPRPool();
+
+ /*virtual*/ apr_pool_t* getAPRPool() ; //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+ apr_pool_t* getVolatileAPRPool() ;
+ void clearVolatileAPRPool() ;
+
+ BOOL isFull() ;
+
+private:
+ S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
+ S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
+
+ apr_thread_mutex_t *mMutexp;
+ apr_pool_t *mMutexPool;
+} ;
/**
* @class LLScopedLock
@@ -143,20 +205,15 @@ class LL_COMMON_API LLAPRFile : boost::noncopyable
// make this non copyable since a copy closes the file
private:
apr_file_t* mFile ;
- LLVolatileAPRPool* mVolatileFilePoolp; // (Thread local) APR pool currently in use.
- LLAPRPool* mRegularFilePoolp; // ...or a regular pool.
+ LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
public:
- enum access_t {
- long_lived, // Use a global pool for long-lived file accesses.
- short_lived // Use a volatile pool for short-lived file accesses.
- };
-
LLAPRFile() ;
- LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep = NULL, access_t access_type = short_lived);
+ LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL);
~LLAPRFile() ;
-
- apr_status_t open(const std::string& filename, apr_int32_t flags, access_t access_type, S32* sizep = NULL);
+
+ apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL);
+ apr_status_t open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool); //use gAPRPoolp.
apr_status_t close() ;
// Returns actual offset, -1 if seek fails
@@ -169,24 +226,32 @@ public:
apr_file_t* getFileHandle() {return mFile;}
+private:
+ apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
+
//
//*******************************************************************************************************************************
//static components
//
+public:
+ static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist.
+
private:
+ static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags);
+ static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ;
static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
public:
// returns false if failure:
- static bool remove(const std::string& filename);
- static bool rename(const std::string& filename, const std::string& newname);
- static bool isExist(const std::string& filename, apr_int32_t flags = APR_READ);
- static S32 size(const std::string& filename);
- static bool makeDir(const std::string& dirname);
- static bool removeDir(const std::string& dirname);
+ static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
+ static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
+ static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+ static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
// Returns bytes read/written, 0 if read/write fails:
- static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
- static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); // offset<0 means append
+ static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+ static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append
//*******************************************************************************************************************************
};
@@ -202,4 +267,6 @@ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* han
void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle);
+extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
+
#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp
deleted file mode 100644
index 6f21b61b65..0000000000
--- a/indra/llcommon/llaprpool.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/**
- * @file llaprpool.cpp
- *
- * $LicenseInfo:firstyear=2011&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- *
- * CHANGELOG
- * and additional copyright holders.
- *
- * 04/04/2010
- * - Initial version, written by Aleric Inglewood @ SL
- *
- * 10/11/2010
- * - Added APR_HAS_THREADS #if's to allow creation and destruction
- * of subpools by threads other than the parent pool owner.
- */
-
-#include "linden_common.h"
-
-#include "llerror.h"
-#include "llaprpool.h"
-#include "llthread.h"
-
-// Create a subpool from parent.
-void LLAPRPool::create(LLAPRPool& parent)
-{
- llassert(!mPool); // Must be non-initialized.
- mParent = &parent;
- if (!mParent) // Using the default parameter?
- {
- // By default use the root pool of the current thread.
- mParent = &LLThreadLocalData::tldata().mRootPool;
- }
- llassert(mParent->mPool); // Parent must be initialized.
-#if APR_HAS_THREADS
- // As per the documentation of APR (ie http://apr.apache.org/docs/apr/1.4/apr__pools_8h.html):
- //
- // Note that most operations on pools are not thread-safe: a single pool should only be
- // accessed by a single thread at any given time. The one exception to this rule is creating
- // a subpool of a given pool: one or more threads can safely create subpools at the same
- // time that another thread accesses the parent pool.
- //
- // In other words, it's safe for any thread to create a (sub)pool, independent of who
- // owns the parent pool.
- mOwner = apr_os_thread_current();
-#else
- mOwner = mParent->mOwner;
- llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
-#endif
- apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
- llassert_always(apr_pool_create_status == APR_SUCCESS);
- llassert(mPool); // Initialized.
- apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
-}
-
-// Destroy the (sub)pool, if any.
-void LLAPRPool::destroy(void)
-{
- // Only do anything if we are not already (being) destroyed.
- if (mPool)
- {
-#if !APR_HAS_THREADS
- // If we are a root pool, then every thread may destruct us: in that case
- // we have to assume that no other thread will use this pool concurrently,
- // of course. Otherwise, if we are a subpool, only the thread that owns
- // the parent may destruct us, since that is the pool that is still alive,
- // possibly being used by others and being altered here.
- llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
-#endif
- apr_pool_t* pool = mPool; // The use of apr_pool_t is OK here.
- // Temporary store before destroying the pool.
- mPool = NULL; // Mark that we are BEING destructed.
- apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
- apr_pool_destroy(pool);
- }
-}
-
-bool LLAPRPool::parent_is_being_destructed(void)
-{
- return mParent && (!mParent->mPool || mParent->parent_is_being_destructed());
-}
-
-LLAPRInitialization::LLAPRInitialization(void)
-{
- static bool apr_initialized = false;
-
- if (!apr_initialized)
- {
- apr_initialize();
- }
-
- apr_initialized = true;
-}
-
-bool LLAPRRootPool::sCountInitialized = false;
-apr_uint32_t volatile LLAPRRootPool::sCount;
-
-apr_thread_mutex_t* gLogMutexp;
-apr_thread_mutex_t* gCallStacksLogMutexp;
-
-LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0)
-{
- // sCountInitialized don't need locking because when we get here there is still only a single thread.
- if (!sCountInitialized)
- {
- // Initialize the logging mutex
- apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
- apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
-
- apr_status_t status = apr_atomic_init(mPool);
- llassert_always(status == APR_SUCCESS);
- apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool.
- sCountInitialized = true;
-
- // Initialize thread-local APR pool support.
- // Because this recursively calls LLAPRRootPool::LLAPRRootPool(void)
- // it must be done last, so that sCount is already initialized.
- LLThreadLocalData::init();
- }
- apr_atomic_inc32(&sCount);
-}
-
-LLAPRRootPool::~LLAPRRootPool()
-{
- if (!apr_atomic_dec32(&sCount))
- {
- // The last pool was destructed. Cleanup remainder of APR.
- LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
-
- if (gLogMutexp)
- {
- // Clean up the logging mutex
-
- // All other threads NEED to be done before we clean up APR, so this is okay.
- apr_thread_mutex_destroy(gLogMutexp);
- gLogMutexp = NULL;
- }
- if (gCallStacksLogMutexp)
- {
- // Clean up the logging mutex
-
- // All other threads NEED to be done before we clean up APR, so this is okay.
- apr_thread_mutex_destroy(gCallStacksLogMutexp);
- gCallStacksLogMutexp = NULL;
- }
-
- // Must destroy ALL, and therefore this last LLAPRRootPool, before terminating APR.
- static_cast<LLAPRRootPool*>(this)->destroy();
-
- apr_terminate();
- }
-}
-
-//static
-// Return a global root pool that is independent of LLThreadLocalData.
-// Normally you should NOT use this. Only use for early initialization
-// (before main) and deinitialization (after main).
-LLAPRRootPool& LLAPRRootPool::get(void)
-{
- static LLAPRRootPool global_APRpool(0);
- return global_APRpool;
-}
-
-void LLVolatileAPRPool::clearVolatileAPRPool()
-{
- llassert_always(mNumActiveRef > 0);
- if (--mNumActiveRef == 0)
- {
- if (isOld())
- {
- destroy();
- mNumTotalRef = 0 ;
- }
- else
- {
- // This does not actually free the memory,
- // it just allows the pool to re-use this memory for the next allocation.
- clear();
- }
- }
-
- // Paranoia check if the pool is jammed.
- llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
-}
diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h
deleted file mode 100644
index bf4102c584..0000000000
--- a/indra/llcommon/llaprpool.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/**
- * @file llaprpool.h
- * @brief Implementation of LLAPRPool
- *
- * $LicenseInfo:firstyear=2011&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- *
- * CHANGELOG
- * and additional copyright holders.
- *
- * 04/04/2010
- * - Initial version, written by Aleric Inglewood @ SL
- *
- * 10/11/2010
- * - Added APR_HAS_THREADS #if's to allow creation and destruction
- * of subpools by threads other than the parent pool owner.
- *
- * 05/02/2011
- * - Fixed compilation on windows: Suppress compile warning 4996
- * and include <winsock2.h> before including <ws2tcpip.h>,
- * by Merov Linden @ SL.
- */
-
-#ifndef LL_LLAPRPOOL_H
-#define LL_LLAPRPOOL_H
-
-#ifdef LL_WINDOWS
-#pragma warning(push)
-#pragma warning(disable:4996)
-#include <winsock2.h>
-#include <ws2tcpip.h> // Needed before including apr_portable.h
-#pragma warning(pop)
-#endif
-
-#include "apr_portable.h"
-#include "apr_pools.h"
-#include "llerror.h"
-
-extern void ll_init_apr();
-
-/**
- * @brief A wrapper around the APR memory pool API.
- *
- * Usage of this class should be restricted to passing it to libapr-1 function calls that need it.
- *
- */
-class LL_COMMON_API LLAPRPool
-{
-protected:
- //! Pointer to the underlaying pool. NULL if not initialized.
- apr_pool_t* mPool; // The use of apr_pool_t is OK here.
- // This is the wrapped pointer that it is all about!
- //! Pointer to the parent pool, if any. Only valid when mPool is non-zero.
- LLAPRPool* mParent;
- //! The thread that owns this memory pool. Only valid when mPool is non-zero.
- apr_os_thread_t mOwner;
-
-public:
- /// Construct an uninitialized (destructed) pool.
- LLAPRPool(void) : mPool(NULL) { }
-
- /// Construct a subpool from an existing pool.
- /// This is not a copy-constructor, this class doesn't have one!
- LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); }
-
- /// Destruct the memory pool (free all of its subpools and allocated memory).
- ~LLAPRPool() { destroy(); }
-
-protected:
- /// Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool.
- LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
- {
- apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
- llassert_always(apr_pool_create_status == APR_SUCCESS);
- llassert(mPool);
- apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
- }
-
-public:
- /// Create a subpool from parent. May only be called for an uninitialized/destroyed pool.
- /// The default parameter causes the root pool of the current thread to be used.
- void create(LLAPRPool& parent = *static_cast<LLAPRPool*>(NULL));
-
- /// Destroy the (sub)pool, if any.
- void destroy(void);
-
- // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
- typedef LLAPRPool* const LLAPRPool::* const bool_type;
- /// Return true if the pool is initialized.
- operator bool_type() const { return mPool ? &LLAPRPool::mParent : 0; }
-
- /// Painful, but we have to either provide access to this, or wrap
- /// every APR function call that needs an apr pool as argument.
- /// NEVER destroy a pool that is returned by this function!
- apr_pool_t* operator()(void) const // The use of apr_pool_t is OK here.
- // This is the accessor for passing the pool to libapr-1 functions.
- {
- llassert(mPool);
- llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
- return mPool;
- }
-
- /// Free all memory without destructing the pool.
- void clear(void)
- {
- llassert(mPool);
- llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
- apr_pool_clear(mPool);
- }
-
-// These methods would make this class 'complete' (as wrapper around the libapr
-// pool functions), but we don't use memory pools in the viewer (only when
-// we are forced to pass one to a libapr call), so don't define them in order
-// not to encourage people to use them.
-#if 0
- void* palloc(size_t size)
- {
- llassert(mPool);
- llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
- return apr_palloc(mPool, size);
- }
- void* pcalloc(size_t size)
- {
- llassert(mPool);
- llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
- return apr_pcalloc(mPool, size);
- }
-#endif
-
-private:
- bool parent_is_being_destructed(void);
- static apr_status_t s_plain_cleanup(void* userdata) { return static_cast<LLAPRPool*>(userdata)->plain_cleanup(); }
-
- apr_status_t plain_cleanup(void)
- {
- if (mPool && // We are not being destructed,
- parent_is_being_destructed()) // but our parent is.
- // This means the pool is being destructed recursively by libapr
- // because one of its parents is being destructed.
- {
- mPool = NULL; // Stop destroy() from destructing the pool again.
- }
- return APR_SUCCESS;
- }
-};
-
-class LLAPRInitialization
-{
-public:
- LLAPRInitialization(void);
-};
-
-/**
- * @brief Root memory pool (allocates memory from the operating system).
- *
- * This class should only be used by LLThreadLocalData
- * (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
- */
-class LL_COMMON_API LLAPRRootPool : public LLAPRInitialization, public LLAPRPool
-{
-private:
- /// Construct a root memory pool. Should only be used by LLThreadLocalData and LLMutexRootPool.
- friend class LLThreadLocalData;
-#if !APR_HAS_THREADS
- friend class LLMutexRootPool;
-#endif
- /// Construct a root memory pool.
- /// Should only be used by LLThreadLocalData.
- LLAPRRootPool(void);
- ~LLAPRRootPool();
-
-private:
- // Keep track of how many root pools exist and when the last one is destructed.
- static bool sCountInitialized;
- static apr_uint32_t volatile sCount;
-
-public:
- // Return a global root pool that is independent of LLThreadLocalData.
- // Normally you should not use this. Only use for early initialization
- // (before main) and deinitialization (after main).
- static LLAPRRootPool& get(void);
-
-#if APR_POOL_DEBUG
- void grab_ownership(void)
- {
- // You need a patched libapr to use this.
- // See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI
- apr_pool_owner_set(mPool);
- }
-#endif
-
-private:
- // Used for constructing the Special Global Root Pool (returned by LLAPRRootPool::get).
- // It is the same as the default constructor but omits to increment sCount. As a result,
- // we must be sure that at least one other LLAPRRootPool is created before termination
- // of the application (which is the case: we create one LLAPRRootPool per thread).
- LLAPRRootPool(int) : LLAPRInitialization(), LLAPRPool(0) { }
-};
-
-/** Volatile memory pool
- *
- * 'Volatile' APR memory pool which normally only clears memory,
- * and does not destroy the pool (the same pool is reused) for
- * greater efficiency. However, as a safe guard the apr pool
- * is destructed every FULL_VOLATILE_APR_POOL uses to allow
- * the system memory to be allocated more efficiently and not
- * get scattered through RAM.
- */
-class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool
-{
-public:
- LLVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { }
-
- void clearVolatileAPRPool(void);
-
- bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; }
- bool isUnused() const { return mNumActiveRef == 0; }
-
-private:
- friend class LLScopedVolatileAPRPool;
- friend class LLAPRFile;
- apr_pool_t* getVolatileAPRPool(void) // The use of apr_pool_t is OK here.
- {
- if (!mPool) create();
- ++mNumActiveRef;
- ++mNumTotalRef;
- return LLAPRPool::operator()();
- }
-
-private:
- S32 mNumActiveRef; // Number of active uses of the pool.
- S32 mNumTotalRef; // Number of total uses of the pool since last creation.
-
- // Maximum number of references to LLVolatileAPRPool until the pool is recreated.
- static S32 const FULL_VOLATILE_APR_POOL = 1024;
-};
-
-#endif // LL_LLAPRPOOL_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 145dddd543..5e566d6c7c 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -93,7 +93,8 @@ LLAssetDictionary::LLAssetDictionary()
addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "sym link", false, false, true));
addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true));
- addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false));
+ addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false));
+ addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false));
addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE));
};
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index 74ccd00324..d538accbf7 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -108,9 +108,13 @@ public:
AT_LINK_FOLDER = 25,
// Inventory folder link
+
+ AT_WIDGET = 40,
+ // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...)
+
AT_MESH = 49,
- // Mesh data in our proprietary SLM format
-
+ // Mesh data in our proprietary SLM format
+
AT_COUNT = 50,
// +*********************************************************+
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index b8a7394852..8be9e4f4de 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -31,9 +31,17 @@
#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();
@@ -47,5 +55,10 @@ void LLCommon::cleanupClass()
// LLWorkerThread::cleanupClass();
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
LLTimer::cleanupClass();
+ if (sAprInitialized)
+ {
+ ll_cleanup_apr();
+ sAprInitialized = FALSE;
+ }
LLMemory::cleanupClass();
}
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
index 171590f3d8..ca9cad5d05 100644
--- a/indra/llcommon/llcommon.h
+++ b/indra/llcommon/llcommon.h
@@ -35,6 +35,8 @@ class LL_COMMON_API LLCommon
public:
static void initClass();
static void cleanupClass();
+private:
+ static BOOL sAprInitialized;
};
#endif
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index bda9d7c177..c35799bbb9 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -866,9 +866,6 @@ You get:
*/
-extern apr_thread_mutex_t* gLogMutexp;
-extern apr_thread_mutex_t* gCallStacksLogMutexp;
-
namespace {
bool checkLevelMap(const LevelMap& map, const std::string& key,
LLError::ELevel& level)
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 369f2a7a97..b3e604f8e8 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -296,4 +296,5 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
Such computation is done iff the message will be logged.
*/
+
#endif // LL_LLERROR_H
diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp
index 4270c8b511..ff5459c1eb 100644
--- a/indra/llcommon/lleventapi.cpp
+++ b/indra/llcommon/lleventapi.cpp
@@ -34,6 +34,7 @@
// std headers
// external library headers
// other Linden headers
+#include "llerror.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
@@ -45,3 +46,32 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s
LLEventAPI::~LLEventAPI()
{
}
+
+LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
+ mResp(seed),
+ mReq(request),
+ mKey(replyKey)
+{}
+
+LLEventAPI::Response::~Response()
+{
+ // When you instantiate a stack Response object, if the original
+ // request requested a reply, send it when we leave this block, no
+ // matter how.
+ sendReply(mResp, mReq, mKey);
+}
+
+void LLEventAPI::Response::warn(const std::string& warning)
+{
+ LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
+ mResp["warnings"].append(warning);
+}
+
+void LLEventAPI::Response::error(const std::string& error)
+{
+ // Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
+ // down altogether.
+ LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
+
+ mResp["error"] = error;
+}
diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h
index d75d521e8e..1a37d780b6 100644
--- a/indra/llcommon/lleventapi.h
+++ b/indra/llcommon/lleventapi.h
@@ -76,6 +76,89 @@ public:
LLEventDispatcher::add(name, desc, callable, required);
}
+ /**
+ * Instantiate a Response object in any LLEventAPI subclass method that
+ * wants to guarantee a reply (if requested) will be sent on exit from the
+ * method. The reply will be sent if request.has(@a replyKey), default
+ * "reply". If specified, the value of request[replyKey] is the name of
+ * the LLEventPump on which to send the reply. Conventionally you might
+ * code something like:
+ *
+ * @code
+ * void MyEventAPI::someMethod(const LLSD& request)
+ * {
+ * // Send a reply event as long as request.has("reply")
+ * Response response(LLSD(), request);
+ * // ...
+ * // will be sent in reply event
+ * response["somekey"] = some_data;
+ * }
+ * @endcode
+ */
+ class LL_COMMON_API Response
+ {
+ public:
+ /**
+ * Instantiating a Response object in an LLEventAPI subclass method
+ * ensures that, if desired, a reply event will be sent.
+ *
+ * @a seed is the initial reply LLSD that will be further decorated before
+ * being sent as the reply
+ *
+ * @a request is the incoming request LLSD; we particularly care about
+ * [replyKey] and ["reqid"]
+ *
+ * @a replyKey [default "reply"] is the string name of the LLEventPump
+ * on which the caller wants a reply. If <tt>(!
+ * request.has(replyKey))</tt>, no reply will be sent.
+ */
+ Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
+ ~Response();
+
+ /**
+ * @code
+ * if (some condition)
+ * {
+ * response.warn("warnings are logged and collected in [\"warnings\"]");
+ * }
+ * @endcode
+ */
+ void warn(const std::string& warning);
+ /**
+ * @code
+ * if (some condition isn't met)
+ * {
+ * // In a function returning void, you can validly 'return
+ * // expression' if the expression is itself of type void. But
+ * // returning is up to you; response.error() has no effect on
+ * // flow of control.
+ * return response.error("error message, logged and also sent as [\"error\"]");
+ * }
+ * @endcode
+ */
+ void error(const std::string& error);
+
+ /**
+ * set other keys...
+ *
+ * @code
+ * // set any attributes you want to be sent in the reply
+ * response["info"] = some_value;
+ * // ...
+ * response["ok"] = went_well;
+ * @endcode
+ */
+ LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
+
+ /**
+ * set the response to the given data
+ */
+ void setResponse(LLSD const & response){ mResp = response; }
+
+ LLSD mResp, mReq;
+ LLSD::String mKey;
+ };
+
private:
std::string mDesc;
};
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index ff03506e84..db1ea4792b 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -591,6 +591,17 @@ void LLReqID::stamp(LLSD& response) const
bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
{
+ // If the original request has no value for replyKey, it's pointless to
+ // construct or send a reply event: on which LLEventPump should we send
+ // it? Allow that to be optional: if the caller wants to require replyKey,
+ // it can so specify when registering the operation method.
+ if (! request.has(replyKey))
+ {
+ return false;
+ }
+
+ // Here the request definitely contains replyKey; reasonable to proceed.
+
// Copy 'reply' to modify it.
LLSD newreply(reply);
// Get the ["reqid"] element from request
diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp
index ebb5961c91..463f558c2c 100644
--- a/indra/llcommon/llfasttimer_class.cpp
+++ b/indra/llcommon/llfasttimer_class.cpp
@@ -303,14 +303,15 @@ LLFastTimer::NamedTimer::~NamedTimer()
std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
{
+ F64 ms_multiplier = 1000.0 / (F64)LLFastTimer::countsPerSecond();
if (history_idx < 0)
{
- // by default, show average number of calls
- return llformat("%s (%d calls)", getName().c_str(), (S32)getCallAverage());
+ // by default, show average number of call
+ return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getCountAverage() * ms_multiplier), (S32)getCallAverage());
}
else
{
- return llformat("%s (%d calls)", getName().c_str(), (S32)getHistoricalCalls(history_idx));
+ return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
}
}
@@ -693,17 +694,7 @@ void LLFastTimer::nextFrame()
llinfos << "Slow frame, fast timers inaccurate" << llendl;
}
- if (sPauseHistory)
- {
- sResetHistory = true;
- }
- else if (sResetHistory)
- {
- sLastFrameIndex = 0;
- sCurFrameIndex = 0;
- sResetHistory = false;
- }
- else // not paused
+ if (!sPauseHistory)
{
NamedTimer::processTimes();
sLastFrameIndex = sCurFrameIndex++;
diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h
index 827747f0c6..f481e968a6 100644
--- a/indra/llcommon/llfasttimer_class.h
+++ b/indra/llcommon/llfasttimer_class.h
@@ -66,7 +66,7 @@ public:
public:
~NamedTimer();
- enum { HISTORY_NUM = 60 };
+ enum { HISTORY_NUM = 300 };
const std::string& getName() const { return mName; }
NamedTimer* getParent() const { return mParent; }
diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp
index 4b5cdbe288..d394f179fb 100644
--- a/indra/llcommon/llfixedbuffer.cpp
+++ b/indra/llcommon/llfixedbuffer.cpp
@@ -30,7 +30,8 @@
LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
: LLLineBuffer(),
- mMaxLines(max_lines)
+ mMaxLines(max_lines),
+ mMutex(NULL)
{
mTimer.reset();
}
diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp
index f576204511..5dc3ea5d7b 100644
--- a/indra/llcommon/llinstancetracker.cpp
+++ b/indra/llcommon/llinstancetracker.cpp
@@ -35,14 +35,15 @@
//static
void * & LLInstanceTrackerBase::getInstances(std::type_info const & info)
{
- static std::map<std::string, void *> instances;
+ typedef std::map<std::string, void *> InstancesMap;
+ static InstancesMap instances;
- std::string k = info.name();
- if(instances.find(k) == instances.end())
- {
- instances[k] = NULL;
- }
-
- return instances[k];
+ // std::map::insert() is just what we want here. You attempt to insert a
+ // (key, value) pair. If the specified key doesn't yet exist, it inserts
+ // the pair and returns a std::pair of (iterator, true). If the specified
+ // key DOES exist, insert() simply returns (iterator, false). One lookup
+ // handles both cases.
+ return instances.insert(InstancesMap::value_type(info.name(),
+ InstancesMap::mapped_type()))
+ .first->second;
}
-
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index afb714c71c..34d841a4e0 100644
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -29,6 +29,7 @@
#define LL_LLINSTANCETRACKER_H
#include <map>
+#include <typeinfo>
#include "string_table.h"
#include <boost/utility.hpp>
@@ -37,10 +38,40 @@
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
+/**
+ * Base class manages "class-static" data that must actually have singleton
+ * semantics: one instance per process, rather than one instance per module as
+ * sometimes happens with data simply declared static.
+ */
class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable
{
- protected:
- static void * & getInstances(std::type_info const & info);
+protected:
+ /// Get a process-unique void* pointer slot for the specified type_info
+ static void * & getInstances(std::type_info const & info);
+
+ /// Find or create a STATICDATA instance for the specified TRACKED class.
+ /// STATICDATA must be default-constructible.
+ template<typename STATICDATA, class TRACKED>
+ static STATICDATA& getStatic()
+ {
+ void *& instances = getInstances(typeid(TRACKED));
+ if (! instances)
+ {
+ instances = new STATICDATA;
+ }
+ return *static_cast<STATICDATA*>(instances);
+ }
+
+ /// It's not essential to derive your STATICDATA (for use with
+ /// getStatic()) from StaticBase; it's just that both known
+ /// implementations do.
+ struct StaticBase
+ {
+ StaticBase():
+ sIterationNestDepth(0)
+ {}
+ S32 sIterationNestDepth;
+ };
};
/// This mix-in class adds support for tracking all instances of the specified class parameter T
@@ -50,8 +81,15 @@ class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable
template<typename T, typename KEY = T*>
class LLInstanceTracker : public LLInstanceTrackerBase
{
- typedef typename std::map<KEY, T*> InstanceMap;
typedef LLInstanceTracker<T, KEY> MyT;
+ typedef typename std::map<KEY, T*> InstanceMap;
+ struct StaticData: public StaticBase
+ {
+ InstanceMap sMap;
+ };
+ static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); }
+ static InstanceMap& getMap_() { return getStatic().sMap; }
+
public:
class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
{
@@ -61,12 +99,12 @@ public:
instance_iter(const typename InstanceMap::iterator& it)
: mIterator(it)
{
- ++sIterationNestDepth;
+ ++getStatic().sIterationNestDepth;
}
~instance_iter()
{
- --sIterationNestDepth;
+ --getStatic().sIterationNestDepth;
}
@@ -95,18 +133,18 @@ public:
key_iter(typename InstanceMap::iterator it)
: mIterator(it)
{
- ++sIterationNestDepth;
+ ++getStatic().sIterationNestDepth;
}
key_iter(const key_iter& other)
: mIterator(other.mIterator)
{
- ++sIterationNestDepth;
+ ++getStatic().sIterationNestDepth;
}
~key_iter()
{
- --sIterationNestDepth;
+ --getStatic().sIterationNestDepth;
}
@@ -155,12 +193,17 @@ public:
}
protected:
- LLInstanceTracker(KEY key) { add_(key); }
+ LLInstanceTracker(KEY key)
+ {
+ // make sure static data outlives all instances
+ getStatic();
+ add_(key);
+ }
virtual ~LLInstanceTracker()
{
// it's unsafe to delete instances of this type while all instances are being iterated over.
- llassert(sIterationNestDepth == 0);
- remove_();
+ llassert_always(getStatic().sIterationNestDepth == 0);
+ remove_();
}
virtual void setKey(KEY key) { remove_(); add_(key); }
virtual const KEY& getKey() const { return mInstanceKey; }
@@ -176,31 +219,24 @@ private:
getMap_().erase(mInstanceKey);
}
- static InstanceMap& getMap_()
- {
- void * & instances = getInstances(typeid(MyT));
- if (! instances)
- {
- instances = new InstanceMap;
- }
- return * static_cast<InstanceMap*>(instances);
- }
-
private:
-
KEY mInstanceKey;
- static S32 sIterationNestDepth;
};
-template <typename T, typename KEY> S32 LLInstanceTracker<T, KEY>::sIterationNestDepth = 0;
-
/// explicit specialization for default case where KEY is T*
/// use a simple std::set<T*>
template<typename T>
class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase
{
- typedef typename std::set<T*> InstanceSet;
typedef LLInstanceTracker<T, T*> MyT;
+ typedef typename std::set<T*> InstanceSet;
+ struct StaticData: public StaticBase
+ {
+ InstanceSet sSet;
+ };
+ static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); }
+ static InstanceSet& getSet_() { return getStatic().sSet; }
+
public:
/// for completeness of analogy with the generic implementation
@@ -213,18 +249,18 @@ public:
instance_iter(const typename InstanceSet::iterator& it)
: mIterator(it)
{
- ++sIterationNestDepth;
+ ++getStatic().sIterationNestDepth;
}
instance_iter(const instance_iter& other)
: mIterator(other.mIterator)
{
- ++sIterationNestDepth;
+ ++getStatic().sIterationNestDepth;
}
~instance_iter()
{
- --sIterationNestDepth;
+ --getStatic().sIterationNestDepth;
}
private:
@@ -250,13 +286,14 @@ public:
protected:
LLInstanceTracker()
{
- // it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle.
+ // make sure static data outlives all instances
+ getStatic();
getSet_().insert(static_cast<T*>(this));
}
virtual ~LLInstanceTracker()
{
// it's unsafe to delete instances of this type while all instances are being iterated over.
- llassert(sIterationNestDepth == 0);
+ llassert_always(getStatic().sIterationNestDepth == 0);
getSet_().erase(static_cast<T*>(this));
}
@@ -264,20 +301,6 @@ protected:
{
getSet_().insert(static_cast<T*>(this));
}
-
- static InstanceSet& getSet_()
- {
- void * & instances = getInstances(typeid(MyT));
- if (! instances)
- {
- instances = new InstanceSet;
- }
- return * static_cast<InstanceSet *>(instances);
- }
-
- static S32 sIterationNestDepth;
};
-template <typename T> S32 LLInstanceTracker<T, T*>::sIterationNestDepth = 0;
-
#endif
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 8c02ad8290..4db1b8bd10 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -165,33 +165,60 @@ void LLMemory::logMemoryInfo(BOOL update)
llinfos << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << llendl ;
llinfos << "Current availabe physical memory(KB): " << sAvailPhysicalMemInKB << llendl ;
llinfos << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << llendl ;
+
+ llinfos << "--- private pool information -- " << llendl ;
+ llinfos << "Total reserved (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalReservedSize / 1024 << llendl ;
+ llinfos << "Total allocated (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalAllocatedSize / 1024 << llendl ;
}
//return 0: everything is normal;
//return 1: the memory pool is low, but not in danger;
//return -1: the memory pool is in danger, is about to crash.
//static
-S32 LLMemory::isMemoryPoolLow()
+bool LLMemory::isMemoryPoolLow()
{
static const U32 LOW_MEMEOY_POOL_THRESHOLD_KB = 64 * 1024 ; //64 MB for emergency use
+ const static U32 MAX_SIZE_CHECKED_MEMORY_BLOCK = 64 * 1024 * 1024 ; //64 MB
+ static void* last_reserved_address = NULL ;
if(!sEnableMemoryFailurePrevention)
{
- return 0 ; //no memory failure prevention.
+ return false ; //no memory failure prevention.
}
if(sAvailPhysicalMemInKB < (LOW_MEMEOY_POOL_THRESHOLD_KB >> 2)) //out of physical memory
{
- return -1 ;
+ return true ;
}
if(sAllocatedPageSizeInKB + (LOW_MEMEOY_POOL_THRESHOLD_KB >> 2) > sMaxHeapSizeInKB) //out of virtual address space.
{
- return -1 ;
+ return true ;
}
- return (S32)(sAvailPhysicalMemInKB < LOW_MEMEOY_POOL_THRESHOLD_KB ||
+ bool is_low = (S32)(sAvailPhysicalMemInKB < LOW_MEMEOY_POOL_THRESHOLD_KB ||
sAllocatedPageSizeInKB + LOW_MEMEOY_POOL_THRESHOLD_KB > sMaxHeapSizeInKB) ;
+
+ //check the virtual address space fragmentation
+ if(!is_low)
+ {
+ if(!last_reserved_address)
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ;
+ }
+ else
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ;
+ if(!last_reserved_address) //failed, try once more
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK) ;
+ }
+ }
+
+ is_low = !last_reserved_address ; //allocation failed
+ }
+
+ return is_low ;
}
//static
@@ -1289,18 +1316,16 @@ U16 LLPrivateMemoryPool::LLMemoryChunk::getPageLevel(U32 size)
//--------------------------------------------------------------------
const U32 CHUNK_SIZE = 4 << 20 ; //4 MB
const U32 LARGE_CHUNK_SIZE = 4 * CHUNK_SIZE ; //16 MB
-LLPrivateMemoryPool::LLPrivateMemoryPool(S32 type) :
+LLPrivateMemoryPool::LLPrivateMemoryPool(S32 type, U32 max_pool_size) :
mMutexp(NULL),
mReservedPoolSize(0),
mHashFactor(1),
- mType(type)
+ mType(type),
+ mMaxPoolSize(max_pool_size)
{
- const U32 MAX_POOL_SIZE = 256 * 1024 * 1024 ; //256 MB
-
- mMaxPoolSize = MAX_POOL_SIZE ;
if(type == STATIC_THREADED || type == VOLATILE_THREADED)
{
- mMutexp = new LLMutex ;
+ mMutexp = new LLMutex(NULL) ;
}
for(S32 i = 0 ; i < SUPER_ALLOCATION ; i++)
@@ -1362,16 +1387,31 @@ char* LLPrivateMemoryPool::allocate(U32 size)
chunk = chunk->mNext ;
}
}
-
- chunk = addChunk(chunk_idx) ;
- if(chunk)
+ else
{
- p = chunk->allocate(size) ;
+ chunk = addChunk(chunk_idx) ;
+ if(chunk)
+ {
+ p = chunk->allocate(size) ;
+ }
}
}
unlock() ;
+ if(!p) //to get memory from the private pool failed, try the heap directly
+ {
+ static bool to_log = true ;
+
+ if(to_log)
+ {
+ llwarns << "The memory pool overflows, now using heap directly!" << llendl ;
+ to_log = false ;
+ }
+
+ return (char*)malloc(size) ;
+ }
+
return p ;
}
@@ -1472,7 +1512,7 @@ void LLPrivateMemoryPool::destroyPool()
unlock() ;
}
-void LLPrivateMemoryPool::checkSize(U32 asked_size)
+bool LLPrivateMemoryPool::checkSize(U32 asked_size)
{
if(mReservedPoolSize + asked_size > mMaxPoolSize)
{
@@ -1480,8 +1520,12 @@ void LLPrivateMemoryPool::checkSize(U32 asked_size)
llinfos << "Total reserved size: " << mReservedPoolSize + asked_size << llendl ;
llinfos << "Total_allocated Size: " << getTotalAllocatedSize() << llendl ;
- llerrs << "The pool is overflowing..." << llendl ;
+ //llerrs << "The pool is overflowing..." << llendl ;
+
+ return false ;
}
+
+ return true ;
}
LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_index)
@@ -1501,7 +1545,11 @@ LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_inde
MAX_SLOT_SIZES[chunk_index], MIN_BLOCK_SIZES[chunk_index], MAX_BLOCK_SIZES[chunk_index]) ;
}
- checkSize(preferred_size + overhead) ;
+ if(!checkSize(preferred_size + overhead))
+ {
+ return NULL ;
+ }
+
mReservedPoolSize += preferred_size + overhead ;
char* buffer = (char*)malloc(preferred_size + overhead) ;
@@ -1593,7 +1641,7 @@ LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* a
void LLPrivateMemoryPool::addToHashTable(LLMemoryChunk* chunk)
{
- static const U16 HASH_FACTORS[] = {41, 83, 193, 317, 419, 523, 0xFFFF};
+ static const U16 HASH_FACTORS[] = {41, 83, 193, 317, 419, 523, 719, 997, 1523, 0xFFFF};
U16 i ;
if(mChunkHashList.empty())
@@ -1773,8 +1821,9 @@ void LLPrivateMemoryPool::LLChunkHashElement::remove(LLPrivateMemoryPool::LLMemo
//class LLPrivateMemoryPoolManager
//--------------------------------------------------------------------
LLPrivateMemoryPoolManager* LLPrivateMemoryPoolManager::sInstance = NULL ;
+std::vector<LLPrivateMemoryPool*> LLPrivateMemoryPoolManager::sDanglingPoolList ;
-LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled)
+LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size)
{
mPoolList.resize(LLPrivateMemoryPool::MAX_TYPES) ;
@@ -1784,6 +1833,9 @@ LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled)
}
mPrivatePoolEnabled = enabled ;
+
+ const U32 MAX_POOL_SIZE = 256 * 1024 * 1024 ; //256 MB
+ mMaxPrivatePoolSize = llmax(max_pool_size, MAX_POOL_SIZE) ;
}
LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()
@@ -1797,7 +1849,7 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()
S32 k = 0 ;
for(mem_allocation_info_t::iterator iter = sMemAllocationTracker.begin() ; iter != sMemAllocationTracker.end() ; ++iter)
{
- llinfos << k++ << ", " << iter->second << llendl ;
+ llinfos << k++ << ", " << (U32)iter->first << " : " << iter->second << llendl ;
}
sMemAllocationTracker.clear() ;
}
@@ -1817,7 +1869,17 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()
{
if(mPoolList[i])
{
- delete mPoolList[i] ;
+ if(mPoolList[i]->isEmpty())
+ {
+ delete mPoolList[i] ;
+ }
+ else
+ {
+ //can not delete this pool because it has alloacted memory to be freed.
+ //move it to the dangling list.
+ sDanglingPoolList.push_back(mPoolList[i]) ;
+ }
+
mPoolList[i] = NULL ;
}
}
@@ -1826,11 +1888,11 @@ LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()
}
//static
-void LLPrivateMemoryPoolManager::initClass(BOOL enabled)
+void LLPrivateMemoryPoolManager::initClass(BOOL enabled, U32 max_pool_size)
{
llassert_always(!sInstance) ;
- sInstance = new LLPrivateMemoryPoolManager(enabled) ;
+ sInstance = new LLPrivateMemoryPoolManager(enabled, max_pool_size) ;
}
//static
@@ -1862,7 +1924,7 @@ LLPrivateMemoryPool* LLPrivateMemoryPoolManager::newPool(S32 type)
if(!mPoolList[type])
{
- mPoolList[type] = new LLPrivateMemoryPool(type) ;
+ mPoolList[type] = new LLPrivateMemoryPool(type, mMaxPrivatePoolSize) ;
}
return mPoolList[type] ;
@@ -1953,6 +2015,32 @@ void LLPrivateMemoryPoolManager::freeMem(LLPrivateMemoryPool* poolp, void* addr
}
else
{
+ if(!sInstance) //the private memory manager is destroyed, try the dangling list
+ {
+ for(S32 i = 0 ; i < sDanglingPoolList.size(); i++)
+ {
+ if(sDanglingPoolList[i]->findChunk((char*)addr))
+ {
+ sDanglingPoolList[i]->freeMem(addr) ;
+ if(sDanglingPoolList[i]->isEmpty())
+ {
+ delete sDanglingPoolList[i] ;
+
+ if(i < sDanglingPoolList.size() - 1)
+ {
+ sDanglingPoolList[i] = sDanglingPoolList[sDanglingPoolList.size() - 1] ;
+ }
+ sDanglingPoolList.pop_back() ;
+ }
+
+ addr = NULL ;
+ break ;
+ }
+ }
+ }
+
+ llassert_always(!addr) ; //addr should be release before hitting here!
+
free(addr) ;
}
}
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index db753f0d8b..74cf42c894 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -122,7 +122,7 @@ public:
static void initMaxHeapSizeGB(F32 max_heap_size_gb, BOOL prevent_heap_failure);
static void updateMemoryInfo() ;
static void logMemoryInfo(BOOL update = FALSE);
- static S32 isMemoryPoolLow();
+ static bool isMemoryPoolLow();
static U32 getAvailableMemKB() ;
static U32 getMaxMemKB() ;
@@ -303,7 +303,7 @@ public:
} ;
private:
- LLPrivateMemoryPool(S32 type) ;
+ LLPrivateMemoryPool(S32 type, U32 max_pool_size) ;
~LLPrivateMemoryPool() ;
char *allocate(U32 size) ;
@@ -320,7 +320,7 @@ private:
void unlock() ;
S32 getChunkIndex(U32 size) ;
LLMemoryChunk* addChunk(S32 chunk_index) ;
- void checkSize(U32 asked_size) ;
+ bool checkSize(U32 asked_size) ;
void removeChunk(LLMemoryChunk* chunk) ;
U16 findHashKey(const char* addr);
void addToHashTable(LLMemoryChunk* chunk) ;
@@ -383,12 +383,12 @@ private:
class LL_COMMON_API LLPrivateMemoryPoolManager
{
private:
- LLPrivateMemoryPoolManager(BOOL enabled) ;
+ LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size) ;
~LLPrivateMemoryPoolManager() ;
public:
static LLPrivateMemoryPoolManager* getInstance() ;
- static void initClass(BOOL enabled) ;
+ static void initClass(BOOL enabled, U32 pool_size) ;
static void destroyClass() ;
LLPrivateMemoryPool* newPool(S32 type) ;
@@ -398,7 +398,9 @@ private:
static LLPrivateMemoryPoolManager* sInstance ;
std::vector<LLPrivateMemoryPool*> mPoolList ;
BOOL mPrivatePoolEnabled;
+ U32 mMaxPrivatePoolSize;
+ static std::vector<LLPrivateMemoryPool*> sDanglingPoolList ;
public:
//debug and statistics info.
void updateStatistics() ;
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index efd9c4b68f..5dee7a3541 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -32,7 +32,7 @@
//============================================================================
// MAIN THREAD
-LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
LLThread(name),
mThreaded(threaded),
mIdleThread(TRUE),
@@ -41,6 +41,11 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
{
if (mThreaded)
{
+ if(should_pause)
+ {
+ pause() ; //call this before start the thread.
+ }
+
start();
}
}
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index a53b22f6fc..499d13a792 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -149,7 +149,7 @@ public:
static handle_t nullHandle() { return handle_t(0); }
public:
- LLQueuedThread(const std::string& name, bool threaded = true);
+ LLQueuedThread(const std::string& name, bool threaded = true, bool should_pause = false);
virtual ~LLQueuedThread();
virtual void shutdown();
diff --git a/indra/llcommon/llscopedvolatileaprpool.h b/indra/llcommon/llscopedvolatileaprpool.h
deleted file mode 100644
index dbaf4edcad..0000000000
--- a/indra/llcommon/llscopedvolatileaprpool.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @file llscopedvolatileaprpool.h
- * @brief Implementation of LLScopedVolatileAPRPool
- *
- * $LicenseInfo:firstyear=2010&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSCOPEDVOLATILEAPRPOOL_H
-#define LL_LLSCOPEDVOLATILEAPRPOOL_H
-
-#include "llthread.h"
-
-/** Scoped volatile memory pool.
- *
- * As the LLVolatileAPRPool should never keep allocations very
- * long, its most common use is for allocations with a lifetime
- * equal to it's scope.
- *
- * This is a convenience class that makes just a little easier to type.
- */
-class LL_COMMON_API LLScopedVolatileAPRPool
-{
-private:
- LLVolatileAPRPool& mPool;
- apr_pool_t* mScopedAPRpool; // The use of apr_pool_t is OK here.
-public:
- LLScopedVolatileAPRPool() : mPool(LLThreadLocalData::tldata().mVolatileAPRPool), mScopedAPRpool(mPool.getVolatileAPRPool()) { }
- ~LLScopedVolatileAPRPool() { mPool.clearVolatileAPRPool(); }
- //! @attention Only use this to pass the underlaying pointer to a libapr-1 function that requires it.
- operator apr_pool_t*() const { return mScopedAPRpool; } // The use of apr_pool_t is OK here.
-};
-
-#endif
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
index bf216d41bf..be9db53906 100644
--- a/indra/llcommon/llsdserialize_xml.cpp
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -354,7 +354,6 @@ static unsigned get_till_eol(std::istream& input, char *buf, unsigned bufsize)
return count;
}
-LLFastTimer::DeclareTimer FTM_SD_PARSE_READ_STREAM("LLSD Read Stream");
S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data)
{
XML_Status status;
@@ -374,7 +373,7 @@ S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data)
{
break;
}
- { LLFastTimer _(FTM_SD_PARSE_READ_STREAM);
+ {
count = get_till_eol(input, (char *)buffer, BUFFER_SIZE);
if (!count)
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 00757be277..49d99f2cd0 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -114,8 +114,7 @@ private:
~SingletonInstanceData()
{
- SingletonInstanceData& data = getData();
- if (data.mInitState != DELETED)
+ if (mInitState != DELETED)
{
deleteSingleton();
}
@@ -130,7 +129,26 @@ public:
data.mInitState = DELETED;
}
- // Can be used to control when the singleton is deleted. Not normally needed.
+ /**
+ * @brief Immediately delete the singleton.
+ *
+ * A subsequent call to LLProxy::getInstance() will construct a new
+ * instance of the class.
+ *
+ * LLSingletons are normally destroyed after main() has exited and the C++
+ * runtime is cleaning up statically-constructed objects. Some classes
+ * derived from LLSingleton have objects that are part of a runtime system
+ * that is terminated before main() exits. Calling the destructor of those
+ * objects after the termination of their respective systems can cause
+ * crashes and other problems during termination of the project. Using this
+ * method to destroy the singleton early can prevent these crashes.
+ *
+ * An example where this is needed is for a LLSingleton that has an APR
+ * object as a member that makes APR calls on destruction. The APR system is
+ * shut down explicitly before main() exits. This causes a crash on exit.
+ * Using this method before the call to apr_terminate() and NOT calling
+ * getInstance() again will prevent the crash.
+ */
static void deleteSingleton()
{
delete getData().mSingletonInstance;
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index f3b48b0156..e7fe656808 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -936,13 +936,18 @@ LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr,
{
const std::string delims (",");
- // Find the first ]
- size_type pos2 = instr.find(']', start);
+ // Find the first [
+ size_type pos1 = instr.find('[', start);
+ if (pos1 == std::string::npos)
+ return std::string::npos;
+
+ //Find the first ] after the initial [
+ size_type pos2 = instr.find(']', pos1);
if (pos2 == std::string::npos)
return std::string::npos;
- // Find the last [ before ]
- size_type pos1 = instr.find_last_of('[', pos2-1);
+ // Find the last [ before ] in case of nested [[]]
+ pos1 = instr.find_last_of('[', pos2-1);
if (pos1 == std::string::npos || pos1 < start)
return std::string::npos;
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index bdde1b5c48..4063cc730b 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -36,12 +36,6 @@
#include <sched.h>
#endif
-#if !LL_DARWIN
-U32 ll_thread_local local_thread_ID = 0;
-#endif
-
-U32 LLThread::sIDIter = 0;
-
//----------------------------------------------------------------------------
// Usage:
// void run_func(LLThread* thread)
@@ -62,6 +56,12 @@ U32 LLThread::sIDIter = 0;
//
//----------------------------------------------------------------------------
+#if !LL_DARWIN
+U32 ll_thread_local sThreadID = 0;
+#endif
+
+U32 LLThread::sIDIter = 0;
+
LL_COMMON_API void assert_main_thread()
{
static U32 s_thread_id = LLThread::currentID();
@@ -79,12 +79,9 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
LLThread *threadp = (LLThread *)datap;
#if !LL_DARWIN
- local_thread_ID = threadp->mID;
+ sThreadID = threadp->mID;
#endif
- // Create a thread local data.
- LLThreadLocalData::create(threadp);
-
// Run the user supplied function
threadp->run();
@@ -97,22 +94,40 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
}
-LLThread::LLThread(std::string const& name) :
- mPaused(false),
+LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
+ mPaused(FALSE),
mName(name),
mAPRThreadp(NULL),
- mStatus(STOPPED),
- mThreadLocalData(NULL)
+ mStatus(STOPPED)
{
- mID = ++sIDIter; //flaw: assume this is called only in the main thread!
+ mID = ++sIDIter;
- mRunCondition = new LLCondition;
+ // Thread creation probably CAN be paranoid about APR being initialized, if necessary
+ if (poolp)
+ {
+ mIsLocalPool = FALSE;
+ mAPRPoolp = poolp;
+ }
+ else
+ {
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+ mRunCondition = new LLCondition(mAPRPoolp);
+
+ mLocalAPRFilePoolp = NULL ;
}
LLThread::~LLThread()
{
shutdown();
+
+ if(mLocalAPRFilePoolp)
+ {
+ delete mLocalAPRFilePoolp ;
+ mLocalAPRFilePoolp = NULL ;
+ }
}
void LLThread::shutdown()
@@ -149,7 +164,7 @@ void LLThread::shutdown()
if (!isStopped())
{
// This thread just wouldn't stop, even though we gave it time
- //llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl;
+ //llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
// Put a stake in its heart.
apr_thread_exit(mAPRThreadp, -1);
return;
@@ -159,8 +174,15 @@ void LLThread::shutdown()
delete mRunCondition;
mRunCondition = 0;
+
+ if (mIsLocalPool && mAPRPoolp)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ mAPRPoolp = 0;
+ }
}
+
void LLThread::start()
{
llassert(isStopped());
@@ -169,7 +191,7 @@ void LLThread::start()
mStatus = RUNNING;
apr_status_t status =
- apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool());
+ apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
if(status == APR_SUCCESS)
{
@@ -194,7 +216,7 @@ void LLThread::pause()
if (!mPaused)
{
// this will cause the thread to stop execution as soon as checkPause() is called
- mPaused = true; // Does not need to be atomic since this is only set/unset from the main thread
+ mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread
}
}
@@ -202,7 +224,7 @@ void LLThread::unpause()
{
if (mPaused)
{
- mPaused = false;
+ mPaused = 0;
}
wake(); // wake up the thread if necessary
@@ -279,76 +301,116 @@ 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(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); }
-#endif
-
-// The thread private handle to access the LLThreadLocalData instance.
-apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey;
+//============================================================================
-//static
-void LLThreadLocalData::init(void)
+LLMutex::LLMutex(apr_pool_t *poolp) :
+ mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
{
- // Only do this once.
- if (sThreadLocalDataKey)
+ //if (poolp)
+ //{
+ // mIsLocalPool = FALSE;
+ // mAPRPoolp = poolp;
+ //}
+ //else
{
- return;
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
}
+ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp);
+}
- apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()());
- ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
- // total number of keys per process {PTHREAD_KEYS_MAX}
- // has been exceeded.
-
- // Create the thread-local data for the main thread (this function is called by the main thread).
- LLThreadLocalData::create(NULL);
-#ifdef SHOW_ASSERT
- // This function is called by the main thread.
- main_thread_id = apr_os_thread_current();
+LLMutex::~LLMutex()
+{
+#if MUTEX_DEBUG
+ //bad assertion, the subclass LLSignal might be "locked", and that's OK
+ //llassert_always(!isLocked()); // better not be locked!
#endif
+ apr_thread_mutex_destroy(mAPRMutexp);
+ mAPRMutexp = NULL;
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
}
-// This is called once for every thread when the thread is destructed.
-//static
-void LLThreadLocalData::destroy(void* thread_local_data)
+
+void LLMutex::lock()
{
- delete static_cast<LLThreadLocalData*>(thread_local_data);
+#if LL_DARWIN
+ if (mLockingThread == LLThread::currentID())
+#else
+ if (mLockingThread == sThreadID)
+#endif
+ { //redundant lock
+ mCount++;
+ return;
+ }
+
+ apr_thread_mutex_lock(mAPRMutexp);
+
+#if MUTEX_DEBUG
+ // Have to have the lock before we can access the debug info
+ U32 id = LLThread::currentID();
+ if (mIsLocked[id] != FALSE)
+ llerrs << "Already locked in Thread: " << id << llendl;
+ mIsLocked[id] = TRUE;
+#endif
+
+#if LL_DARWIN
+ mLockingThread = LLThread::currentID();
+#else
+ mLockingThread = sThreadID;
+#endif
}
-//static
-void LLThreadLocalData::create(LLThread* threadp)
+void LLMutex::unlock()
{
- LLThreadLocalData* new_tld = new LLThreadLocalData;
- if (threadp)
- {
- threadp->mThreadLocalData = new_tld;
+ if (mCount > 0)
+ { //not the root unlock
+ mCount--;
+ return;
}
- apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey);
- llassert_always(status == APR_SUCCESS);
+
+#if MUTEX_DEBUG
+ // Access the debug info while we have the lock
+ U32 id = LLThread::currentID();
+ if (mIsLocked[id] != TRUE)
+ llerrs << "Not locked in Thread: " << id << llendl;
+ mIsLocked[id] = FALSE;
+#endif
+
+ mLockingThread = NO_THREAD;
+ apr_thread_mutex_unlock(mAPRMutexp);
}
-//static
-LLThreadLocalData& LLThreadLocalData::tldata(void)
+bool LLMutex::isLocked()
{
- if (!sThreadLocalDataKey)
+ apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
+ if (APR_STATUS_IS_EBUSY(status))
{
- LLThreadLocalData::init();
+ return true;
}
+ else
+ {
+ apr_thread_mutex_unlock(mAPRMutexp);
+ return false;
+ }
+}
- void* data;
- apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey);
- llassert_always(status == APR_SUCCESS);
- return *static_cast<LLThreadLocalData*>(data);
+U32 LLMutex::lockingThread() const
+{
+ return mLockingThread;
}
//============================================================================
-LLCondition::LLCondition(LLAPRPool& parent) : LLMutex(parent)
+LLCondition::LLCondition(apr_pool_t *poolp) :
+ LLMutex(poolp)
{
- apr_thread_cond_create(&mAPRCondp, mPool());
+ // base class (LLMutex) has already ensured that mAPRPoolp is set up.
+
+ apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
}
@@ -361,6 +423,15 @@ LLCondition::~LLCondition()
void LLCondition::wait()
{
+ if (!isLocked())
+ { //mAPRMutexp MUST be locked before calling apr_thread_cond_wait
+ apr_thread_mutex_lock(mAPRMutexp);
+#if MUTEX_DEBUG
+ // avoid asserts on destruction in non-release builds
+ U32 id = LLThread::currentID();
+ mIsLocked[id] = TRUE;
+#endif
+ }
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
}
@@ -375,44 +446,6 @@ void LLCondition::broadcast()
}
//============================================================================
-LLMutexBase::LLMutexBase() :
- mLockingThread(NO_THREAD),
- mCount(0)
-{
-}
-
-void LLMutexBase::lock()
-{
-#if LL_DARWIN
- if (mLockingThread == LLThread::currentID())
-#else
- if (mLockingThread == local_thread_ID)
-#endif
- { //redundant lock
- mCount++;
- return;
- }
-
- apr_thread_mutex_lock(mAPRMutexp);
-
-#if LL_DARWIN
- mLockingThread = LLThread::currentID();
-#else
- mLockingThread = local_thread_ID;
-#endif
-}
-
-void LLMutexBase::unlock()
-{
- if (mCount > 0)
- { //not the root unlock
- mCount--;
- return;
- }
- mLockingThread = NO_THREAD;
-
- apr_thread_mutex_unlock(mAPRMutexp);
-}
//----------------------------------------------------------------------------
@@ -424,7 +457,7 @@ void LLThreadSafeRefCount::initThreadSafeRefCount()
{
if (!sMutex)
{
- sMutex = new LLMutex;
+ sMutex = new LLMutex(0);
}
}
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index c732e3bc77..40291a2569 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -29,13 +29,7 @@
#include "llapp.h"
#include "llapr.h"
-#include "llmemory.h"
#include "apr_thread_cond.h"
-#include "llaprpool.h"
-
-#ifdef SHOW_ASSERT
-extern LL_COMMON_API bool is_main_thread(void);
-#endif
class LLThread;
class LLMutex;
@@ -47,22 +41,6 @@ class LLCondition;
#define ll_thread_local __thread
#endif
-class LL_COMMON_API LLThreadLocalData
-{
-private:
- static apr_threadkey_t* sThreadLocalDataKey;
-
-public:
- // Thread-local memory pools.
- LLAPRRootPool mRootPool;
- LLVolatileAPRPool mVolatileAPRPool;
-
- static void init(void);
- static void destroy(void* thread_local_data);
- static void create(LLThread* pthread);
- static LLThreadLocalData& tldata(void);
-};
-
class LL_COMMON_API LLThread
{
private:
@@ -76,7 +54,7 @@ public:
QUITTING= 2 // Someone wants this thread to quit
} EThreadStatus;
- LLThread(std::string const& name);
+ LLThread(const std::string& name, apr_pool_t *poolp = NULL);
virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state.
virtual void shutdown(); // stops the thread
@@ -91,7 +69,7 @@ public:
// Called from MAIN THREAD.
void pause();
void unpause();
- bool isPaused() { return isStopped() || mPaused; }
+ bool isPaused() { return isStopped() || mPaused == TRUE; }
// Cause the thread to wake up and check its condition
void wake();
@@ -105,11 +83,13 @@ public:
// this kicks off the apr thread
void start(void);
- // Return thread-local data for the current thread.
- static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); }
+ apr_pool_t *getAPRPool() { return mAPRPoolp; }
+ LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
+
+ U32 getID() const { return mID; }
private:
- bool mPaused;
+ BOOL mPaused;
// static function passed to APR thread creation routine
static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
@@ -119,11 +99,15 @@ protected:
LLCondition* mRunCondition;
apr_thread_t *mAPRThreadp;
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
EThreadStatus mStatus;
U32 mID;
-
- friend void LLThreadLocalData::create(LLThread* threadp);
- LLThreadLocalData* mThreadLocalData;
+
+ //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
+ //Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
+ // otherwise it will cause severe memory leaking!!! --bao
+ LLVolatileAPRPool *mLocalAPRFilePoolp ;
void setQuitting();
@@ -153,15 +137,7 @@ protected:
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
-#ifdef MUTEX_DEBUG
-// We really shouldn't be using recursive locks. Make sure of that in debug mode.
-#define MUTEX_FLAG APR_THREAD_MUTEX_UNNESTED
-#else
-// Use the fastest platform-optimal lock behavior (can be recursive or non-recursive).
-#define MUTEX_FLAG APR_THREAD_MUTEX_DEFAULT
-#endif
-
-class LL_COMMON_API LLMutexBase
+class LL_COMMON_API LLMutex
{
public:
typedef enum
@@ -169,73 +145,32 @@ public:
NO_THREAD = 0xFFFFFFFF
} e_locking_thread;
- LLMutexBase() ;
-
- void lock() ;
- void unlock() ;
- // Returns true if lock was obtained successfully.
- bool trylock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); }
-
- // non-blocking, but does do a lock/unlock so not free
- bool isLocked() { bool is_not_locked = trylock(); if (is_not_locked) unlock(); return !is_not_locked; }
-
+ LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
+ virtual ~LLMutex();
+
+ void lock(); // blocks
+ void unlock();
+ bool isLocked(); // non-blocking, but does do a lock/unlock so not free
+ U32 lockingThread() const; //get ID of locking thread
+
protected:
- // mAPRMutexp is initialized and uninitialized in the derived class.
- apr_thread_mutex_t* mAPRMutexp;
+ apr_thread_mutex_t *mAPRMutexp;
mutable U32 mCount;
mutable U32 mLockingThread;
-};
-
-class LL_COMMON_API LLMutex : public LLMutexBase
-{
-public:
- LLMutex(LLAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent)
- {
- apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mPool());
- }
- ~LLMutex()
- {
- llassert(!isLocked()); // better not be locked!
- apr_thread_mutex_destroy(mAPRMutexp);
- mAPRMutexp = NULL;
- }
-
-protected:
- LLAPRPool mPool;
-};
-
-#if APR_HAS_THREADS
-// No need to use a root pool in this case.
-typedef LLMutex LLMutexRootPool;
-#else // APR_HAS_THREADS
-class LL_COMMON_API LLMutexRootPool : public LLMutexBase
-{
-public:
- LLMutexRootPool(void)
- {
- apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mRootPool());
- }
- ~LLMutexRootPool()
- {
-#if APR_POOL_DEBUG
- // It is allowed to destruct root pools from a different thread.
- mRootPool.grab_ownership();
+
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+
+#if MUTEX_DEBUG
+ std::map<U32, BOOL> mIsLocked;
#endif
- llassert(!isLocked());
- apr_thread_mutex_destroy(mAPRMutexp);
- mAPRMutexp = NULL;
- }
-
-protected:
- LLAPRRootPool mRootPool;
};
-#endif // APR_HAS_THREADS
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
class LL_COMMON_API LLCondition : public LLMutex
{
public:
- LLCondition(LLAPRPool& parent = LLThread::tldata().mRootPool);
+ LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
~LLCondition();
void wait(); // blocks
@@ -246,10 +181,10 @@ protected:
apr_thread_cond_t *mAPRCondp;
};
-class LL_COMMON_API LLMutexLock
+class LLMutexLock
{
public:
- LLMutexLock(LLMutexBase* mutex)
+ LLMutexLock(LLMutex* mutex)
{
mMutex = mutex;
mMutex->lock();
@@ -259,7 +194,7 @@ public:
mMutex->unlock();
}
private:
- LLMutexBase* mMutex;
+ LLMutex* mMutex;
};
//============================================================================
diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp
index 05d24944f3..8a73e632a9 100644
--- a/indra/llcommon/llthreadsafequeue.cpp
+++ b/indra/llcommon/llthreadsafequeue.cpp
@@ -34,11 +34,19 @@
//-----------------------------------------------------------------------------
-LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(unsigned int capacity):
+LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity):
+ mOwnsPool(pool == 0),
+ mPool(pool),
mQueue(0)
{
- mPool.create();
- apr_status_t status = apr_queue_create(&mQueue, capacity, mPool());
+ if(mOwnsPool) {
+ apr_status_t status = apr_pool_create(&mPool, 0);
+ if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool");
+ } else {
+ ; // No op.
+ }
+
+ apr_status_t status = apr_queue_create(&mQueue, capacity, mPool);
if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue");
}
@@ -51,6 +59,7 @@ LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation()
" elements;" << "memory will be leaked" << LL_ENDL;
apr_queue_term(mQueue);
}
+ if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool);
}
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
index 43d0b396f2..58cac38769 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -30,9 +30,9 @@
#include <string>
#include <stdexcept>
-#include "llaprpool.h"
+struct apr_pool_t; // From apr_pools.h
class LLThreadSafeQueueImplementation; // See below.
@@ -75,7 +75,7 @@ struct apr_queue_t; // From apr_queue.h
class LL_COMMON_API LLThreadSafeQueueImplementation
{
public:
- LLThreadSafeQueueImplementation(unsigned int capacity);
+ LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity);
~LLThreadSafeQueueImplementation();
void pushFront(void * element);
bool tryPushFront(void * element);
@@ -84,7 +84,8 @@ public:
size_t size();
private:
- LLAPRPool mPool; // The pool used for mQueue.
+ bool mOwnsPool;
+ apr_pool_t * mPool;
apr_queue_t * mQueue;
};
@@ -98,8 +99,9 @@ class LLThreadSafeQueue
public:
typedef ElementT value_type;
- // Constructor.
- LLThreadSafeQueue(unsigned int capacity = 1024);
+ // If the pool is set to NULL one will be allocated and managed by this
+ // queue.
+ LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024);
// Add an element to the front of queue (will block if the queue has
// reached capacity).
@@ -137,8 +139,8 @@ private:
template<typename ElementT>
-LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(unsigned int capacity) :
- mImplementation(capacity)
+LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity):
+ mImplementation(pool, capacity)
{
; // No op.
}
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index ef55cfec89..aa37a03ef8 100755..100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -28,8 +28,8 @@
#define LL_LLVERSIONVIEWER_H
const S32 LL_VERSION_MAJOR = 3;
-const S32 LL_VERSION_MINOR = 0;
-const S32 LL_VERSION_PATCH = 5;
+const S32 LL_VERSION_MINOR = 2;
+const S32 LL_VERSION_PATCH = 1;
const S32 LL_VERSION_BUILD = 0;
const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
index 6b308bb917..4988bdf570 100644
--- a/indra/llcommon/llworkerthread.cpp
+++ b/indra/llcommon/llworkerthread.cpp
@@ -34,10 +34,15 @@
//============================================================================
// Run on MAIN thread
-LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
- LLQueuedThread(name, threaded)
+LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded, bool should_pause) :
+ LLQueuedThread(name, threaded, should_pause)
{
- mDeleteMutex = new LLMutex;
+ mDeleteMutex = new LLMutex(NULL);
+
+ if(!mLocalAPRFilePoolp)
+ {
+ mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
+ }
}
LLWorkerThread::~LLWorkerThread()
@@ -199,6 +204,7 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
mWorkerClassName(name),
mRequestHandle(LLWorkerThread::nullHandle()),
mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
+ mMutex(NULL),
mWorkFlags(0)
{
if (!mWorkerThread)
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index bef5ef53fe..78a4781d15 100644
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -83,7 +83,7 @@ private:
LLMutex* mDeleteMutex;
public:
- LLWorkerThread(const std::string& name, bool threaded = true);
+ LLWorkerThread(const std::string& name, bool threaded = true, bool should_pause = false);
~LLWorkerThread();
/*virtual*/ S32 update(U32 max_time_ms);
@@ -94,6 +94,7 @@ public:
private:
void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion
+
};
//============================================================================
@@ -193,7 +194,7 @@ protected:
U32 mRequestPriority; // last priority set
private:
- LLMutexRootPool mMutex; // Use LLMutexRootPool since this object is created and destructed by multiple threads.
+ LLMutex mMutex;
LLAtomicU32 mWorkFlags;
};
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
index 556eff8370..40b3364b36 100644
--- a/indra/llcommon/stdenums.h
+++ b/indra/llcommon/stdenums.h
@@ -49,8 +49,9 @@ enum EDragAndDropType
DAD_ANIMATION = 12,
DAD_GESTURE = 13,
DAD_LINK = 14,
- DAD_MESH = 15,
- DAD_COUNT = 16, // number of types in this enum
+ DAD_MESH = 15,
+ DAD_WIDGET = 16,
+ DAD_COUNT = 17, // number of types in this enum
};
// Reasons for drags to be denied.
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index 80b35bbdc3..b34d1c5fd3 100644
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -40,6 +40,7 @@
#include <boost/scoped_ptr.hpp>
// other Linden headers
#include "../test/lltut.h"
+#include "wrapllerrs.h"
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
@@ -165,4 +166,67 @@ namespace tut
ensure_equals("unreported instance", instances.size(), 0);
}
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("delete Keyed with outstanding instance_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("one");
+ {
+ WrapLL_ERRS wrapper;
+ Keyed::instance_iter i(Keyed::beginInstances());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("delete Keyed with outstanding key_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("one");
+ {
+ WrapLL_ERRS wrapper;
+ Keyed::key_iter i(Keyed::beginKeys());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("delete Unkeyed with outstanding instance_iter");
+ std::string what;
+ Unkeyed* unkeyed = new Unkeyed;
+ {
+ WrapLL_ERRS wrapper;
+ Unkeyed::instance_iter i(Unkeyed::beginInstances());
+ try
+ {
+ delete unkeyed;
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
new file mode 100644
index 0000000000..385289aefe
--- /dev/null
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llsingleton.h"
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct singleton
+ {
+ // We need a class created with the LLSingleton template to test with.
+ class LLSingletonTest: public LLSingleton<LLSingletonTest>
+ {
+
+ };
+ };
+
+ typedef test_group<singleton> singleton_t;
+ typedef singleton_t::object singleton_object_t;
+ tut::singleton_t tut_singleton("LLSingleton");
+
+ template<> template<>
+ void singleton_object_t::test<1>()
+ {
+
+ }
+ template<> template<>
+ void singleton_object_t::test<2>()
+ {
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ }
+ template<> template<>
+ void singleton_object_t::test<3>()
+ {
+ //Construct the instance
+ LLSingletonTest::getInstance();
+ ensure(LLSingletonTest::instanceExists());
+
+ //Delete the instance
+ LLSingletonTest::deleteSingleton();
+ ensure(LLSingletonTest::destroyed());
+ ensure(!LLSingletonTest::instanceExists());
+
+ //Construct it again.
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ ensure(LLSingletonTest::instanceExists());
+ }
+}
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 304e91ed92..6a1cbf652a 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -624,6 +624,14 @@ namespace tut
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]");
ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
+ // Test on nested brackets
+ std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
// Test an assorted substitution
std::string srcs8 = "foo[DELETE]bar?";