aleric avatar aleric committed b782a75

Introduces a LLThreadLocalData class that can be
accessed through the static LLThread::tldata().
Currently this object contains two (public) thread-local
objects: a LLAPRRootPool and a LLVolatileAPRPool.

The first is the general memory pool used by this thread
(and this thread alone), while the second is intended
for short lived memory allocations (needed for APR).
The advantages of not mixing those two is that the latter
is used most frequently, and as a result of it's nature
can be destroyed and reconstructed on a "regular" basis.

This patch adds LLAPRPool (completely replacing the old one),
which is a wrapper around apr_pool_t* and has complete
thread-safity checking.

Whenever an apr call requires memory for some resource,
a memory pool in the form of an LLAPRPool object can
be created with the same life-time as this resource;
assuring clean up of the memory no sooner, but also
not much later than the life-time of the resource
that needs the memory.

Many, many function calls and constructors had the
pool parameter simply removed (it is no longer the
concern of the developer, if you don't write code
that actually does an libapr call then you are no
longer bothered with memory pools at all).

However, I kept the notion of short-lived and
long-lived allocations alive (see my remark in
the jira here: https://jira.secondlife.com/browse/STORM-864?focusedCommentId=235356&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-235356
which requires that the LLAPRFile API needs
to allow the user to specify how long they
think a file will stay open. By choosing
'short_lived' as default for the constructor
that immediately opens a file, the number of
instances where this needs to be specified is
drastically reduced however (obviously, any
automatic LLAPRFile is short lived).

***

Addressed Boroondas remarks in https://codereview.secondlife.com/r/99/
regarding (doxygen) comments. This patch effectively only changes comments.

Includes some 'merge' stuff that ended up in llvocache.cpp
(while starting as a bug fix, now only resulting in a cleanup).

***

Added comment 'The use of apr_pool_t is OK here'.

Added this comment on every line where apr_pool_t
is correctly being used.

This should make it easier to spot (future) errors
where someone started to use apr_pool_t; you can
just grep all sources for 'apr_pool_t' and immediately
see where it's being used while LLAPRPool should
have been used.

Note that merging this patch is very easy:
If there are no other uses of apr_pool_t in the code
(one grep) and it compiles, then it will work.

***

Second Merge (needed to remove 'delete mCreationMutex'
from LLImageDecodeThread::~LLImageDecodeThread).

***

Added back #include <apr_pools.h>.

Apparently that is needed on libapr version 1.2.8.,
the version used by Linden Lab, for calls to
apr_queue_*. This is a bug in libapr (we also
include <apr_queue.h>, that is fixed in (at least) 1.3.7.

Note that 1.2.8 is VERY old. Even 1.3.x is old.

***

License fixes (GPL -> LGPL). And typo in comments.
Addresses merov's comments on the review board.

***

Added Merov's compile fixes for windows.

Comments (0)

Files changed (80)

doc/contributions.txt

 Alejandro Rosenthal
 	VWR-1184
 Aleric Inglewood
+	SNOW-84
 	SNOW-240
+	SNOW-477
 	SNOW-522
 	SNOW-626
+	SNOW-744
 	SNOW-756
 	SNOW-764
+	SNOW-766
 	VWR-10001
 	VWR-10579
 	VWR-10759
     VWR-24321
  	VWR-24354
 	VWR-24519
-	SNOW-84
-	SNOW-477
-	SNOW-744
-	SNOW-766
 	STORM-163
+	STORM-864
 Ales Beaumont
 	VWR-9352
 	SNOW-240

indra/llaudio/llaudioengine_fmod.cpp

 		return false;
 	}
 
-	if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB))
+	if (!LLAPRFile::isExist(filename, LL_APR_RPB))
 	{
 		// File not found, abort.
 		return false;

indra/llaudio/llvorbisencode.cpp

 	error_msg.clear();
 
 	//********************************
-	LLAPRFile infile ;
-    infile.open(in_fname,LL_APR_RB);
+	LLAPRFile infile(in_fname, LL_APR_RB);
 	//********************************
 	if (!infile.getFileHandle())
 	{
 
 	S32 data_left = 0;
 
-	LLAPRFile infile ;
-	infile.open(in_fname,LL_APR_RB);
+	LLAPRFile infile(in_fname,LL_APR_RB);
 	if (!infile.getFileHandle())
 	{
 		llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
 		return(LLVORBISENC_SOURCE_OPEN_ERR);
 	}
 
-	LLAPRFile outfile ;
-	outfile.open(out_fname,LL_APR_WPB);
+	LLAPRFile outfile(out_fname, LL_APR_WPB);
 	if (!outfile.getFileHandle())
 	{
 		llwarns << "Couldn't open upload sound file for reading: " << in_fname

indra/llcharacter/llbvhloader.cpp

 	//--------------------------------------------------------------------
 	std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName);
 
-	LLAPRFile infile ;
-	infile.open(path, LL_APR_R);
+	LLAPRFile infile(path, LL_APR_R);
 	apr_file_t *fp = infile.getFileHandle();
 	if (!fp)
 		return E_ST_NO_XLT_FILE;

indra/llcharacter/llkeyframemotionparam.cpp

 	// open the file
 	//-------------------------------------------------------------------------
 	S32 fileSize = 0;
-	LLAPRFile infile ;
-	infile.open(path, LL_APR_R, NULL, &fileSize);
+	LLAPRFile infile(path, LL_APR_R, &fileSize);
 	apr_file_t* fp = infile.getFileHandle() ;
 	if (!fp || fileSize == 0)
 	{

indra/llcharacter/llstatemachine.cpp

 
 BOOL LLStateDiagram::saveDotFile(const std::string& filename)
 {
-	LLAPRFile outfile ;
-	outfile.open(filename, LL_APR_W);
+	LLAPRFile outfile(filename, LL_APR_W);
 	apr_file_t* dot_file = outfile.getFileHandle() ;
 
 	if (!dot_file)

indra/llcommon/CMakeLists.txt

     llallocator_heap_profile.cpp
     llapp.cpp
     llapr.cpp
+    llaprpool.cpp
     llassettype.cpp
     llavatarname.cpp
     llbase32.cpp
     llrand.cpp
     llrefcount.cpp
     llrun.cpp
+    llscopedvolatileaprpool.h
     llsd.cpp
     llsdserialize.cpp
     llsdserialize_xml.cpp
     llavatarname.h
     llapp.h
     llapr.h
+    llaprpool.h
     llassettype.h
     llassoclist.h
     llavatarconstants.h

indra/llcommon/llapp.cpp

 		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;
 	

indra/llcommon/llapr.cpp

 #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
 //
 LLAPRFile::LLAPRFile()
 	: mFile(NULL),
-	  mCurrentFilePoolp(NULL)
+	  mVolatileFilePoolp(NULL),
+	  mRegularFilePoolp(NULL)
 {
 }
 
-LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool)
+LLAPRFile::LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep, access_t access_type)
 	: mFile(NULL),
-	  mCurrentFilePoolp(NULL)
+	  mVolatileFilePoolp(NULL),
+	  mRegularFilePoolp(NULL)
 {
-	open(filename, flags, pool);
+	open(filename, flags, access_type, sizep);
 }
 
 LLAPRFile::~LLAPRFile()
 		mFile = NULL ;
 	}
 
-	if(mCurrentFilePoolp)
+	if (mVolatileFilePoolp)
 	{
-		mCurrentFilePoolp->clearVolatileAPRPool() ;
-		mCurrentFilePoolp = NULL ;
+		mVolatileFilePoolp->clearVolatileAPRPool() ;
+		mVolatileFilePoolp = NULL ;
+	}
+
+	if (mRegularFilePoolp)
+	{
+		delete mRegularFilePoolp;
+		mRegularFilePoolp = NULL;
 	}
 
 	return ret ;
 }
 
-apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep)
+apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep)
 {
-	apr_status_t s ;
+	llassert_always(!mFile);
+	llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp);
 
-	//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)
+	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)
 	{
 		mFile = NULL ;
-		
+		close() ;
 		if (sizep)
 		{
 			*sizep = 0;
 		}
+		return status;
 	}
-	else if (sizep)
+
+	if (sizep)
 	{
 		S32 file_size = 0;
 		apr_off_t offset = 0;
 		*sizep = file_size;
 	}
 
-	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 ;
+	return status;
 }
 
 // File I/O
 //
 
 //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)
 }
 
 //static
-S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
 {
-	//*****************************************
-	apr_file_t* file_handle = open(filename, pool, APR_READ|APR_BINARY); 
-	//*****************************************	
-	if (!file_handle)
+	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)
 	{
+		ll_apr_warn_status(s);
+		LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL;
 		return 0;
 	}
 
 		}
 	}
 	
-	//*****************************************
-	close(file_handle, pool) ; 
-	//*****************************************
+	apr_file_close(file_handle);
+
 	return (S32)bytes_read;
 }
 
 //static
-S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
 {
 	apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
 	if (offset < 0)
 		offset = 0;
 	}
 	
-	//*****************************************
-	apr_file_t* file_handle = open(filename, pool, flags);
-	//*****************************************
-	if (!file_handle)
+	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)
 	{
+		ll_apr_warn_status(s);
+		LL_WARNS("APR") << " while attempting to open file \"" << filename << '"' << LL_ENDL;
 		return 0;
 	}
 
 		}
 	}
 
-	//*****************************************
-	LLAPRFile::close(file_handle, pool);
-	//*****************************************
+	apr_file_close(file_handle);
 
 	return (S32)bytes_written;
 }
 
 //static
-bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
+bool LLAPRFile::remove(const std::string& filename)
 {
 	apr_status_t s;
 
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_file_remove(filename.c_str(), pool->getVolatileAPRPool());
-	pool->clearVolatileAPRPool() ;
+	LLScopedVolatileAPRPool pool;
+	s = apr_file_remove(filename.c_str(), pool);
 
 	if (s != APR_SUCCESS)
 	{
 }
 
 //static
-bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
+bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
 {
 	apr_status_t s;
 
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_file_rename(filename.c_str(), newname.c_str(), pool->getVolatileAPRPool());
-	pool->clearVolatileAPRPool() ;
+	LLScopedVolatileAPRPool pool;
+	s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
 	
 	if (s != APR_SUCCESS)
 	{
 }
 
 //static
-bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
+bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags)
 {
-	apr_file_t* apr_file;
+	apr_file_t* file_handle;
 	apr_status_t s;
 
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool->getVolatileAPRPool());	
+	LLScopedVolatileAPRPool pool;
+	s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
 
-	if (s != APR_SUCCESS || !apr_file)
+	if (s != APR_SUCCESS || !file_handle)
 	{
-		pool->clearVolatileAPRPool() ;
 		return false;
 	}
 	else
 	{
-		apr_file_close(apr_file) ;
-		pool->clearVolatileAPRPool() ;
+		apr_file_close(file_handle);
 		return true;
 	}
 }
 
 //static
-S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
+S32 LLAPRFile::size(const std::string& filename)
 {
-	apr_file_t* apr_file;
+	apr_file_t* file_handle;
 	apr_finfo_t info;
 	apr_status_t s;
 	
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool->getVolatileAPRPool());
+	LLScopedVolatileAPRPool pool;
+	s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
 	
-	if (s != APR_SUCCESS || !apr_file)
+	if (s != APR_SUCCESS || !file_handle)
 	{		
-		pool->clearVolatileAPRPool() ;
-		
 		return 0;
 	}
 	else
 	{
-		apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);		
+		apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, file_handle);
 
-		apr_file_close(apr_file) ;
-		pool->clearVolatileAPRPool() ;
+		apr_file_close(file_handle) ;
 		
 		if (s == APR_SUCCESS)
 		{
 }
 
 //static
-bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+bool LLAPRFile::makeDir(const std::string& dirname)
 {
 	apr_status_t s;
 
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool->getVolatileAPRPool());
-	pool->clearVolatileAPRPool() ;
+	LLScopedVolatileAPRPool pool;
+	s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
 		
 	if (s != APR_SUCCESS)
 	{
 		ll_apr_warn_status(s);
-		LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
+		LL_WARNS("APR") << " while attempting to make directory: " << dirname << LL_ENDL;
 		return false;
 	}
 	return true;
 }
 
 //static
-bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+bool LLAPRFile::removeDir(const std::string& dirname)
 {
 	apr_status_t s;
 
-	pool = pool ? pool : LLAPRFile::sAPRFilePoolp ;
-	s = apr_file_remove(dirname.c_str(), pool->getVolatileAPRPool());
-	pool->clearVolatileAPRPool() ;
+	LLScopedVolatileAPRPool pool;
+	s = apr_file_remove(dirname.c_str(), pool);
 	
 	if (s != APR_SUCCESS)
 	{

indra/llcommon/llapr.h

 #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;
-
-/** 
- * @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 LLAPRPool;
+class LLVolatileAPRPool;
 
 /** 
  * @class LLScopedLock
 	// make this non copyable since a copy closes the file
 private:
 	apr_file_t* mFile ;
-	LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool. 
+	LLVolatileAPRPool* mVolatileFilePoolp;	// (Thread local) APR pool currently in use.
+	LLAPRPool* mRegularFilePoolp;		// ...or a regular 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(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL);
+	LLAPRFile(std::string const& filename, apr_int32_t flags, S32* sizep = NULL, access_t access_type = short_lived);
 	~LLAPRFile() ;
-	
-	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 open(const std::string& filename, apr_int32_t flags, access_t access_type, S32* sizep = NULL);
 	apr_status_t close() ;
 
 	// Returns actual offset, -1 if seek fails
 	
 	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, 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);
+	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);
 
 	// Returns bytes read/written, 0 if read/write fails:
-	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
+	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
 //*******************************************************************************************************************************
 };
 
 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

indra/llcommon/llaprpool.cpp

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

indra/llcommon/llaprpool.h

+/**
+ * @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

indra/llcommon/llcommon.cpp

 #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();
 // 	LLWorkerThread::cleanupClass();
 	LLThreadSafeRefCount::cleanupThreadSafeRefCount();
 	LLTimer::cleanupClass();
-	if (sAprInitialized)
-	{
-		ll_cleanup_apr();
-		sAprInitialized = FALSE;
-	}
 	LLMemory::cleanupClass();
 }

indra/llcommon/llcommon.h

 public:
 	static void initClass();
 	static void cleanupClass();
-private:
-	static BOOL sAprInitialized;
 };
 
 #endif

indra/llcommon/llerror.cpp

 	
 */
 
+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)

indra/llcommon/llerror.h

 		Such computation is done iff the message will be logged.
 	*/
 
-
 #endif // LL_LLERROR_H

indra/llcommon/llfixedbuffer.cpp

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

indra/llcommon/llscopedvolatileaprpool.h

+/**
+ * @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

indra/llcommon/llthread.cpp

 {
 	LLThread *threadp = (LLThread *)datap;
 
+	// Create a thread local data.
+	LLThreadLocalData::create(threadp);
+
 	// Run the user supplied function
 	threadp->run();
 
 }
 
 
-LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
-	mPaused(FALSE),
+LLThread::LLThread(std::string const& name) :
+	mPaused(false),
 	mName(name),
 	mAPRThreadp(NULL),
-	mStatus(STOPPED)
+	mStatus(STOPPED),
+	mThreadLocalData(NULL)
 {
-	// Thread creation probably CAN be paranoid about APR being initialized, if necessary
-	if (poolp)
-	{
-		mIsLocalPool = FALSE;
-		mAPRPoolp = poolp;
-	}
-	else
-	{
-		mIsLocalPool = TRUE;
-		apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
-	}
-	mRunCondition = new LLCondition(mAPRPoolp);
-
-	mLocalAPRFilePoolp = NULL ;
+	mRunCondition = new LLCondition;
 }
 
 
 LLThread::~LLThread()
 {
 	shutdown();
-
-	if(mLocalAPRFilePoolp)
-	{
-		delete mLocalAPRFilePoolp ;
-		mLocalAPRFilePoolp = NULL ;
-	}
 }
 
 void LLThread::shutdown()
 		if (!isStopped())
 		{
 			// This thread just wouldn't stop, even though we gave it time
-			llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+			llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl;
 			// Put a stake in its heart.
 			apr_thread_exit(mAPRThreadp, -1);
 			return;
 
 	delete mRunCondition;
 	mRunCondition = 0;
-	
-	if (mIsLocalPool && mAPRPoolp)
-	{
-		apr_pool_destroy(mAPRPoolp);
-		mAPRPoolp = 0;
-	}
 }
 
-
 void LLThread::start()
 {
 	llassert(isStopped());
 	mStatus = RUNNING;
 
 	apr_status_t status =
-		apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+		apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool());
 	
 	if(status == APR_SUCCESS)
 	{	
 	if (!mPaused)
 	{
 		// this will cause the thread to stop execution as soon as checkPause() is called
-		mPaused = 1;		// Does not need to be atomic since this is only set/unset from the main thread
+		mPaused = true;		// Does not need to be atomic since this is only set/unset from the main thread
 	}	
 }
 
 {
 	if (mPaused)
 	{
-		mPaused = 0;
+		mPaused = false;
 	}
 
 	wake(); // wake up the thread if necessary
 	}
 }
 
-//============================================================================
+#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
 
-LLMutex::LLMutex(apr_pool_t *poolp) :
-	mAPRMutexp(NULL)
+// The thread private handle to access the LLThreadLocalData instance.
+apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey;
+
+//static
+void LLThreadLocalData::init(void)
 {
-	//if (poolp)
-	//{
-	//	mIsLocalPool = FALSE;
-	//	mAPRPoolp = poolp;
-	//}
-	//else
+	// Only do this once.
+	if (sThreadLocalDataKey)
 	{
-		mIsLocalPool = TRUE;
-		apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+		return;
 	}
-	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.
 
-LLMutex::~LLMutex()
-{
-#if MUTEX_DEBUG
-	llassert_always(!isLocked()); // better not be locked!
-#endif
-	apr_thread_mutex_destroy(mAPRMutexp);
-	mAPRMutexp = NULL;
-	if (mIsLocalPool)
-	{
-		apr_pool_destroy(mAPRPoolp);
-	}
-}
+	// Create the thread-local data for the main thread (this function is called by the main thread).
+	LLThreadLocalData::create(NULL);
 
-
-void LLMutex::lock()
-{
-	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;
+#ifdef SHOW_ASSERT
+	// This function is called by the main thread.
+	main_thread_id = apr_os_thread_current();
 #endif
 }
 
-void LLMutex::unlock()
+// This is called once for every thread when the thread is destructed.
+//static
+void LLThreadLocalData::destroy(void* thread_local_data)
 {
-#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
-	apr_thread_mutex_unlock(mAPRMutexp);
+	delete static_cast<LLThreadLocalData*>(thread_local_data);
 }
 
-bool LLMutex::isLocked()
+//static
+void LLThreadLocalData::create(LLThread* threadp)
 {
-	apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
-	if (APR_STATUS_IS_EBUSY(status))
+	LLThreadLocalData* new_tld = new LLThreadLocalData;
+	if (threadp)
 	{
-		return true;
+		threadp->mThreadLocalData = new_tld;
 	}
-	else
+	apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey);
+	llassert_always(status == APR_SUCCESS);
+}
+
+//static
+LLThreadLocalData& LLThreadLocalData::tldata(void)
+{
+	if (!sThreadLocalDataKey)
 	{
-		apr_thread_mutex_unlock(mAPRMutexp);
-		return false;
+		LLThreadLocalData::init();
 	}
+
+	void* data;
+	apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey);
+	llassert_always(status == APR_SUCCESS);
+	return *static_cast<LLThreadLocalData*>(data);
 }
 
 //============================================================================
 
-LLCondition::LLCondition(apr_pool_t *poolp) :
-	LLMutex(poolp)
+LLCondition::LLCondition(LLAPRPool& parent) : LLMutex(parent)
 {
-	// base class (LLMutex) has already ensured that mAPRPoolp is set up.
-
-	apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
+	apr_thread_cond_create(&mAPRCondp, mPool());
 }
 
 
 {
 	if (!sMutex)
 	{
-		sMutex = new LLMutex(0);
+		sMutex = new LLMutex;
 	}
 }
 

indra/llcommon/llthread.h

 
 #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;
 class LLCondition;
 
+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
 {
 public:
 		QUITTING= 2 	// Someone wants this thread to quit
 	} EThreadStatus;
 
-	LLThread(const std::string& name, apr_pool_t *poolp = NULL);
+	LLThread(std::string const& name);
 	virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state.
 	virtual void shutdown(); // stops the thread
 	
 	// Called from MAIN THREAD.
 	void pause();
 	void unpause();
-	bool isPaused() { return isStopped() || mPaused == TRUE; }
+	bool isPaused() { return isStopped() || mPaused; }
 	
 	// Cause the thread to wake up and check its condition
 	void wake();
 	// this kicks off the apr thread
 	void start(void);
 
-	apr_pool_t *getAPRPool() { return mAPRPoolp; }
-	LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
+	// Return thread-local data for the current thread.
+	static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); }
 
 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);
 	LLCondition*		mRunCondition;
 
 	apr_thread_t		*mAPRThreadp;
-	apr_pool_t			*mAPRPoolp;
-	BOOL				mIsLocalPool;
 	EThreadStatus		mStatus;
 
-	//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 ; 
+	friend void LLThreadLocalData::create(LLThread* threadp);
+	LLThreadLocalData*	mThreadLocalData;
 
 	void setQuitting();
 	
 
 #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
 
-class LL_COMMON_API LLMutex
+#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
 {
 public:
-	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
-	
+	void lock() { apr_thread_mutex_lock(mAPRMutexp); }
+	void unlock() { apr_thread_mutex_unlock(mAPRMutexp); }
+	// 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; }
+
 protected:
-	apr_thread_mutex_t *mAPRMutexp;
-	apr_pool_t			*mAPRPoolp;
-	BOOL				mIsLocalPool;
-#if MUTEX_DEBUG
-	std::map<U32, BOOL> mIsLocked;
+	// mAPRMutexp is initialized and uninitialized in the derived class.
+	apr_thread_mutex_t* mAPRMutexp;
+};
+
+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();
 #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(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+	LLCondition(LLAPRPool& parent = LLThread::tldata().mRootPool);
 	~LLCondition();
 	
 	void wait();		// blocks
 	apr_thread_cond_t *mAPRCondp;
 };
 
-class LLMutexLock
+class LL_COMMON_API LLMutexLock
 {
 public:
-	LLMutexLock(LLMutex* mutex)
+	LLMutexLock(LLMutexBase* mutex)
 	{
 		mMutex = mutex;
 		mMutex->lock();
 		mMutex->unlock();
 	}
 private:
-	LLMutex* mMutex;
+	LLMutexBase* mMutex;
 };
 
 //============================================================================

indra/llcommon/llthreadsafequeue.cpp

 //-----------------------------------------------------------------------------
 
 
-LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity):
-	mOwnsPool(pool == 0),
-	mPool(pool),
+LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(unsigned int capacity):
 	mQueue(0)
 {
-	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);
+	mPool.create();
+	apr_status_t status = apr_queue_create(&mQueue, capacity, mPool());
 	if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue");
 }
 
 			" elements;" << "memory will be leaked" << LL_ENDL;
 		apr_queue_term(mQueue);
 	}
-	if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool);
 }
 
 

indra/llcommon/llthreadsafequeue.h

 
 #include <string>
 #include <stdexcept>
+#include "llaprpool.h"
 
 
-struct apr_pool_t; // From apr_pools.h
 class LLThreadSafeQueueImplementation; // See below.
 
 
 class LL_COMMON_API LLThreadSafeQueueImplementation
 {
 public:
-	LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity);
+	LLThreadSafeQueueImplementation(unsigned int capacity);
 	~LLThreadSafeQueueImplementation();
 	void pushFront(void * element);
 	bool tryPushFront(void * element);
 	size_t size();
 	
 private:
-	bool mOwnsPool;
-	apr_pool_t * mPool;
+	LLAPRPool mPool;			// The pool used for mQueue.
 	apr_queue_t * mQueue;
 };
 
 public:
 	typedef ElementT value_type;
 	
-	// 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);
+	// Constructor.
+	LLThreadSafeQueue(unsigned int capacity = 1024);
 	
 	// Add an element to the front of queue (will block if the queue has
 	// reached capacity).
 
 
 template<typename ElementT>
-LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity):
-	mImplementation(pool, capacity)
+LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(unsigned int capacity) :
+	mImplementation(capacity)
 {
 	; // No op.
 }

indra/llcommon/llworkerthread.cpp

 LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
 	LLQueuedThread(name, threaded)
 {
-	mDeleteMutex = new LLMutex(NULL);
-
-	if(!mLocalAPRFilePoolp)
-	{
-		mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
-	}
+	mDeleteMutex = new LLMutex;
 }
 
 LLWorkerThread::~LLWorkerThread()
 	  mWorkerClassName(name),
 	  mRequestHandle(LLWorkerThread::nullHandle()),
 	  mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
-	  mMutex(NULL),
 	  mWorkFlags(0)
 {
 	if (!mWorkerThread)

indra/llcommon/llworkerthread.h

 
 private: