From 90df1597295861bbad0fff81cab039df389c1cd2 Mon Sep 17 00:00:00 2001
From: Aleric Inglewood
Date: Tue, 16 Nov 2010 17:44:51 +0100
Subject: IMP-577: Use the same string for FFSAVE_RAW as in strings.xml
Apparently the code of Imprudence was from before this bug was
introduced. Nevertheless, decided to add this one-liner so that
the code becomes the same (in this regard) as snowglobe 1.x
and (soon) viewer 2.x (see https://jira.secondlife.com/browse/STORM-423).
---
linden/indra/newview/llfilepicker.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/linden/indra/newview/llfilepicker.cpp b/linden/indra/newview/llfilepicker.cpp
index a59fa06..ec4e294 100644
--- a/linden/indra/newview/llfilepicker.cpp
+++ b/linden/indra/newview/llfilepicker.cpp
@@ -1181,7 +1181,7 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename
break;
case FFSAVE_RAW:
caption += add_simple_pattern_filter_to_gtkchooser
- (picker, "*.raw", LLTrans::getString("dot_raw_file") + " (*.raw)");
+ (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)");
suggest_ext = ".raw";
break;
case FFSAVE_J2C:
--
cgit v1.1
From 9ce7c1e25abc7cac72183787ac2b079ca304cd0a Mon Sep 17 00:00:00 2001
From: Aleric Inglewood
Date: Wed, 17 Nov 2010 01:00:18 +0100
Subject: IMP-712: Add shortcut for the Groups tab.
Bind control-shift-G to Groups.
This is also done in snowglobe.
---
linden/doc/contributions.txt | 1 +
linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/linden/doc/contributions.txt b/linden/doc/contributions.txt
index 41f896f..6646a40 100644
--- a/linden/doc/contributions.txt
+++ b/linden/doc/contributions.txt
@@ -91,6 +91,7 @@ Aleric Inglewood
IMP-670
IMP-688
IMP-692
+ IMP-712
Alissa Sabre
VWR-81
VWR-83
diff --git a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml
index 31e730a..01a5b6e 100644
--- a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+++ b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml
@@ -267,7 +267,8 @@
-
+
--
cgit v1.1
From b66950bc26cfb3210e786c966141b20a5cc13a84 Mon Sep 17 00:00:00 2001
From: Aleric Inglewood
Date: Wed, 10 Nov 2010 15:19:13 +0100
Subject: IMP-590: Added a thread-safe and robust wrapper for APR pools.
See http://redmine.imprudenceviewer.org/issues/590
---
linden/doc/contributions.txt | 1 +
linden/indra/llcommon/CMakeLists.txt | 3 +
linden/indra/llcommon/aiaprpool.cpp | 198 ++++++++++++++
linden/indra/llcommon/aiaprpool.h | 236 ++++++++++++++++
linden/indra/llcommon/llapp.cpp | 5 -
linden/indra/llcommon/llapr.cpp | 299 +++------------------
linden/indra/llcommon/llapr.h | 74 +----
linden/indra/llcommon/llcommon.cpp | 13 -
linden/indra/llcommon/llcommon.h | 2 -
linden/indra/llcommon/llerror.cpp | 3 +
linden/indra/llcommon/llfixedbuffer.cpp | 3 +-
linden/indra/llcommon/llscopedvolatileaprpool.h | 58 ++++
linden/indra/llcommon/llthread.cpp | 117 ++++----
linden/indra/llcommon/llthread.h | 88 +++++-
linden/indra/llcommon/llworkerthread.cpp | 3 +-
linden/indra/llcommon/llworkerthread.h | 2 +-
linden/indra/llcrashlogger/llcrashlogger.cpp | 3 +-
linden/indra/llimage/llimage.cpp | 2 +-
linden/indra/llimage/llimagej2c.cpp | 15 +-
linden/indra/llimage/llimageworker.cpp | 7 +-
linden/indra/llimage/llimageworker.h | 2 +-
linden/indra/llmath/llvolumemgr.cpp | 4 +-
linden/indra/llmessage/llares.cpp | 17 +-
linden/indra/llmessage/llcurl.cpp | 2 +-
linden/indra/llmessage/lliohttpserver.cpp | 10 +-
linden/indra/llmessage/lliohttpserver.h | 2 +-
linden/indra/llmessage/lliosocket.cpp | 99 +++----
linden/indra/llmessage/lliosocket.h | 33 +--
linden/indra/llmessage/llmail.cpp | 17 +-
linden/indra/llmessage/llmail.h | 4 +-
linden/indra/llmessage/llpumpio.cpp | 74 ++---
linden/indra/llmessage/llpumpio.h | 31 +--
linden/indra/llmessage/llurlrequest.cpp | 25 +-
linden/indra/llmessage/message.cpp | 17 +-
linden/indra/llplugin/llplugininstance.cpp | 5 +-
linden/indra/llplugin/llpluginmessagepipe.cpp | 2 -
linden/indra/llplugin/llpluginprocesschild.cpp | 2 +-
linden/indra/llplugin/llpluginprocessparent.cpp | 55 ++--
linden/indra/llplugin/llpluginprocessparent.h | 2 +
linden/indra/llplugin/llpluginsharedmemory.cpp | 9 +-
linden/indra/llplugin/llpluginsharedmemory.h | 3 +
linden/indra/llplugin/slplugin/slplugin.cpp | 4 -
linden/indra/llvfs/llvfs.cpp | 2 +-
.../media_plugins/webkit/linux_volume_catcher.cpp | 14 +-
linden/indra/newview/llappviewer.cpp | 2 +-
linden/indra/newview/llappviewerlinux.cpp | 1 +
linden/indra/newview/llappviewermacosx.cpp | 1 +
linden/indra/newview/lltexturecache.cpp | 5 +-
linden/indra/newview/lltexturecache.h | 3 -
linden/indra/newview/lltexturefetch.cpp | 3 -
linden/indra/newview/llviewerprecompiledheaders.h | 2 +-
linden/indra/newview/llvoiceclient.cpp | 2 +-
linden/indra/newview/llwatchdog.cpp | 4 +-
linden/indra/test/lltemplatemessagebuilder_tut.cpp | 2 -
linden/indra/test/message_tut.cpp | 2 -
linden/indra/test/test.cpp | 16 +-
56 files changed, 878 insertions(+), 732 deletions(-)
create mode 100644 linden/indra/llcommon/aiaprpool.cpp
create mode 100644 linden/indra/llcommon/aiaprpool.h
create mode 100644 linden/indra/llcommon/llscopedvolatileaprpool.h
diff --git a/linden/doc/contributions.txt b/linden/doc/contributions.txt
index 6646a40..c8dd773 100644
--- a/linden/doc/contributions.txt
+++ b/linden/doc/contributions.txt
@@ -80,6 +80,7 @@ Aleric Inglewood
IMP-578
IMP-579
IMP-581
+ IMP-590
IMP-592
IMP-595
IMP-660
diff --git a/linden/indra/llcommon/CMakeLists.txt b/linden/indra/llcommon/CMakeLists.txt
index 5d590a9..a05ee6f 100644
--- a/linden/indra/llcommon/CMakeLists.txt
+++ b/linden/indra/llcommon/CMakeLists.txt
@@ -13,6 +13,7 @@ include_directories(
)
set(llcommon_SOURCE_FILES
+ aiaprpool.cpp
imageids.cpp
indra_constants.cpp
llapp.cpp
@@ -74,6 +75,7 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
+ aiaprpool.h
bitpack.h
ctype_workaround.h
doublelinkedlist.h
@@ -147,6 +149,7 @@ set(llcommon_HEADER_FILES
llqueuedthread.h
llrand.h
llrun.h
+ llscopedvolatileaprpool.h
llsd.h
llsdserialize.h
llsdserialize_xml.h
diff --git a/linden/indra/llcommon/aiaprpool.cpp b/linden/indra/llcommon/aiaprpool.cpp
new file mode 100644
index 0000000..d3748e9
--- /dev/null
+++ b/linden/indra/llcommon/aiaprpool.cpp
@@ -0,0 +1,198 @@
+/**
+ * @file aiaprpool.cpp
+ *
+ * Copyright (c) 2010, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 04/04/2010
+ * - Initial version, written by Aleric Inglewood @ SL
+ *
+ * 10/11/2010
+ * - Changed filename, class names and license to a more
+ * company-neutral format.
+ * - Added APR_HAS_THREADS #if's to allow creation and destruction
+ * of subpools by threads other than the parent pool owner.
+ */
+
+#include "linden_common.h"
+
+#include "llerror.h"
+#include "aiaprpool.h"
+#include "llthread.h"
+
+// Create a subpool from parent.
+void AIAPRPool::create(AIAPRPool& parent)
+{
+ llassert(!mPool); // Must be non-initialized.
+ mParent = &parent;
+ if (!mParent) // Using the default parameter?
+ {
+ // By default use the root pool of the current thread.
+ mParent = &AIThreadLocalData::tldata().mRootPool;
+ }
+ llassert(mParent->mPool); // Parent must be initialized.
+#if APR_HAS_THREADS
+ // As per the documentation of APR (ie http://apr.apache.org/docs/apr/1.4/apr__pools_8h.html):
+ //
+ // Note that most operations on pools are not thread-safe: a single pool should only be
+ // accessed by a single thread at any given time. The one exception to this rule is creating
+ // a subpool of a given pool: one or more threads can safely create subpools at the same
+ // time that another thread accesses the parent pool.
+ //
+ // In other words, it's safe for any thread to create a (sub)pool, independent of who
+ // owns the parent pool.
+ mOwner = apr_os_thread_current();
+#else
+ mOwner = mParent->mOwner;
+ llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
+#endif
+ apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
+ llassert_always(apr_pool_create_status == APR_SUCCESS);
+ llassert(mPool); // Initialized.
+ apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
+}
+
+// Destroy the (sub)pool, if any.
+void AIAPRPool::destroy(void)
+{
+ // Only do anything if we are not already (being) destroyed.
+ if (mPool)
+ {
+#if !APR_HAS_THREADS
+ // If we are a root pool, then every thread may destruct us: in that case
+ // we have to assume that no other thread will use this pool concurrently,
+ // of course. Otherwise, if we are a subpool, only the thread that owns
+ // the parent may destruct us, since that is the pool that is still alive,
+ // possibly being used by others and being altered here.
+ llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
+#endif
+ apr_pool_t* pool = mPool;
+ mPool = NULL; // Mark that we are BEING destructed.
+ apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
+ apr_pool_destroy(pool);
+ }
+}
+
+bool AIAPRPool::parent_is_being_destructed(void)
+{
+ return mParent && (!mParent->mPool || mParent->parent_is_being_destructed());
+}
+
+AIAPRInitialization::AIAPRInitialization(void)
+{
+ static bool apr_initialized = false;
+
+ if (!apr_initialized)
+ {
+ apr_initialize();
+ }
+
+ apr_initialized = true;
+}
+
+bool AIAPRRootPool::sCountInitialized = false;
+apr_uint32_t volatile AIAPRRootPool::sCount;
+
+extern apr_thread_mutex_t* gLogMutexp;
+extern apr_thread_mutex_t* gCallStacksLogMutexp;
+
+AIAPRRootPool::AIAPRRootPool(void) : AIAPRInitialization(), AIAPRPool(0)
+{
+ // sCountInitialized don't need locking because when we get here there is still only a single thread.
+ if (!sCountInitialized)
+ {
+ // Initialize the logging mutex
+ apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
+ apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
+
+ apr_status_t status = apr_atomic_init(mPool);
+ llassert_always(status == APR_SUCCESS);
+ apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool.
+ sCountInitialized = true;
+
+ // Initialize thread-local APR pool support.
+ // Because this recursively calls AIAPRRootPool::AIAPRRootPool(void)
+ // it must be done last, so that sCount is already initialized.
+ AIThreadLocalData::init();
+ }
+ apr_atomic_inc32(&sCount);
+}
+
+AIAPRRootPool::~AIAPRRootPool()
+{
+ if (!apr_atomic_dec32(&sCount))
+ {
+ // The last pool was destructed. Cleanup remainder of APR.
+ LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
+
+ if (gLogMutexp)
+ {
+ // Clean up the logging mutex
+
+ // All other threads NEED to be done before we clean up APR, so this is okay.
+ apr_thread_mutex_destroy(gLogMutexp);
+ gLogMutexp = NULL;
+ }
+ if (gCallStacksLogMutexp)
+ {
+ // Clean up the logging mutex
+
+ // All other threads NEED to be done before we clean up APR, so this is okay.
+ apr_thread_mutex_destroy(gCallStacksLogMutexp);
+ gCallStacksLogMutexp = NULL;
+ }
+
+ // Must destroy ALL, and therefore this last AIAPRRootPool, before terminating APR.
+ static_cast(this)->destroy();
+
+ apr_terminate();
+ }
+}
+
+//static
+AIAPRRootPool& AIAPRRootPool::get(void)
+{
+ static AIAPRRootPool global_APRpool(0); // This is what used to be gAPRPoolp.
+ return global_APRpool;
+}
+
+void AIVolatileAPRPool::clearVolatileAPRPool()
+{
+ llassert_always(mNumActiveRef > 0);
+ if (--mNumActiveRef == 0)
+ {
+ if (isOld())
+ {
+ destroy();
+ mNumTotalRef = 0 ;
+ }
+ else
+ {
+ // This does not actually free the memory,
+ // it just allows the pool to re-use this memory for the next allocation.
+ clear();
+ }
+ }
+
+ // Paranoia check if the pool is jammed.
+ llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
+}
diff --git a/linden/indra/llcommon/aiaprpool.h b/linden/indra/llcommon/aiaprpool.h
new file mode 100644
index 0000000..f6d7ffa
--- /dev/null
+++ b/linden/indra/llcommon/aiaprpool.h
@@ -0,0 +1,236 @@
+/**
+ * @file aiaprpool.h
+ * @brief Implementation of AIAPRPool.
+ *
+ * Copyright (c) 2010, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 04/04/2010
+ * - Initial version, written by Aleric Inglewood @ SL
+ *
+ * 10/11/2010
+ * - Changed filename, class names and license to a more
+ * company-neutral format.
+ * - Added APR_HAS_THREADS #if's to allow creation and destruction
+ * of subpools by threads other than the parent pool owner.
+ */
+
+#ifndef AIAPRPOOL_H
+#define AIAPRPOOL_H
+
+#ifdef LL_WINDOWS
+#include // Needed before including apr_portable.h
+#endif
+
+#include "apr_portable.h"
+#include "apr_pools.h"
+#include "llerror.h"
+
+extern void ll_init_apr();
+
+/**
+ * @brief A wrapper around the APR memory pool API.
+ *
+ * Usage of this class should be restricted to passing it to libapr-1 function calls that need it.
+ *
+ */
+class LL_COMMON_API AIAPRPool
+{
+protected:
+ apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized.
+ AIAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero.
+ apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
+
+public:
+ //! Construct an uninitialized (destructed) pool.
+ AIAPRPool(void) : mPool(NULL) { }
+
+ //! Construct a subpool from an existing pool.
+ // This is not a copy-constructor, this class doesn't have one!
+ AIAPRPool(AIAPRPool& parent) : mPool(NULL) { create(parent); }
+
+ //! Destruct the memory pool (free all of it's subpools and allocated memory).
+ ~AIAPRPool() { destroy(); }
+
+protected:
+ // Create a pool that is allocated from the Operating System. Only used by AIAPRRootPool.
+ AIAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
+ {
+ apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
+ llassert_always(apr_pool_create_status == APR_SUCCESS);
+ llassert(mPool);
+ apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
+ }
+
+public:
+ //! Create a subpool from parent. May only be called for an uninitialized/destroyed pool.
+ // The default parameter causes the root pool of the current thread to be used.
+ void create(AIAPRPool& parent = *static_cast(NULL));
+
+ //! Destroy the (sub)pool, if any.
+ void destroy(void);
+
+ // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
+ typedef apr_pool_t* const AIAPRPool::* const bool_type;
+ //! Return true if the pool is initialized.
+ operator bool_type() const { return mPool ? &AIAPRPool::mPool : 0; }
+
+ // Painful, but we have to either provide access to this, or wrap
+ // every APR function call that needs a apr_pool_t* to be passed.
+ // NEVER destroy a pool that is returned by this function!
+ apr_pool_t* operator()(void) const
+ {
+ llassert(mPool);
+ llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
+ return mPool;
+ }
+
+ // Free all memory without destructing the pool.
+ void clear(void)
+ {
+ llassert(mPool);
+ llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
+ apr_pool_clear(mPool);
+ }
+
+// These methods would make this class 'complete' (as wrapper around the libapr
+// pool functions), but we don't use memory pools in the viewer (only when
+// we are forced to pass one to a libapr call), so don't define them in order
+// not to encourage people to use them.
+#if 0
+ void* palloc(size_t size)
+ {
+ llassert(mPool);
+ llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
+ return apr_palloc(mPool, size);
+ }
+ void* pcalloc(size_t size)
+ {
+ llassert(mPool);
+ llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
+ return apr_pcalloc(mPool, size);
+ }
+#endif
+
+private:
+ bool parent_is_being_destructed(void);
+ static apr_status_t s_plain_cleanup(void* userdata) { return static_cast(userdata)->plain_cleanup(); }
+
+ apr_status_t plain_cleanup(void)
+ {
+ if (mPool && // We are not being destructed,
+ parent_is_being_destructed()) // but our parent is.
+ // This means the pool is being destructed recursively by libapr
+ // because one of it's parents is being destructed.
+ {
+ mPool = NULL; // Stop destroy() from destructing the pool again.
+ }
+ return APR_SUCCESS;
+ }
+};
+
+class AIAPRInitialization
+{
+public:
+ AIAPRInitialization(void);
+};
+
+/**
+ * @brief Root memory pool (allocates memory from the operating system).
+ *
+ * This class should only be used by AIThreadLocalData (and LLMutexRootPool
+ * when APR_HAS_THREADS isn't defined).
+ */
+class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool
+{
+private:
+ friend class AIThreadLocalData;
+#if !APR_HAS_THREADS
+ friend class LLMutexRootPool;
+#endif
+ //! Construct a root memory pool. Should only be used by AIThreadLocalData.
+ AIAPRRootPool(void);
+ ~AIAPRRootPool();
+
+private:
+ // Keep track of how many root pools exist and when the last one is destructed.
+ static bool sCountInitialized;
+ static apr_uint32_t volatile sCount;
+
+public:
+ // Return a global root pool that is independent of AIThreadLocalData.
+ // Normally you should not use this. Only use for early initialization
+ // (before main) and deinitialization (after main).
+ static AIAPRRootPool& get(void);
+
+#if APR_POOL_DEBUG
+ void grab_ownership(void)
+ {
+ // You need a patched libapr to use this.
+ // See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI
+ apr_pool_owner_set(mPool);
+ }
+#endif
+
+private:
+ // Used for constructing the Special Global Root Pool (returned by AIAPRRootPool::get).
+ // It is the same as the default constructor but omits to increment sCount. As a result,
+ // we must be sure that at least one other AIAPRRootPool is created before termination
+ // of the application (which is the case: we create one AIAPRRootPool per thread).
+ AIAPRRootPool(int) : AIAPRInitialization(), AIAPRPool(0) { }
+};
+
+//! Volatile memory pool
+//
+// 'Volatile' APR memory pool which normally only clears memory,
+// and does not destroy the pool (the same pool is reused) for
+// greater efficiency. However, as a safe guard the apr pool
+// is destructed every FULL_VOLATILE_APR_POOL uses to allow
+// the system memory to be allocated more efficiently and not
+// get scattered through RAM.
+//
+class LL_COMMON_API AIVolatileAPRPool : protected AIAPRPool
+{
+public:
+ AIVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { }
+
+ apr_pool_t* getVolatileAPRPool(void)
+ {
+ if (!mPool) create();
+ ++mNumActiveRef;
+ ++mNumTotalRef;
+ return AIAPRPool::operator()();
+ }
+ void clearVolatileAPRPool(void);
+
+ bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; }
+ bool isUnused() const { return mNumActiveRef == 0; }
+
+private:
+ S32 mNumActiveRef; // Number of active uses of the pool.
+ S32 mNumTotalRef; // Number of total uses of the pool since last creation.
+
+ // Maximum number of references to AIVolatileAPRPool until the pool is recreated.
+ static S32 const FULL_VOLATILE_APR_POOL = 1024;
+};
+
+#endif // AIAPRPOOL_H
diff --git a/linden/indra/llcommon/llapp.cpp b/linden/indra/llcommon/llapp.cpp
index e269f59..eedc503 100644
--- a/linden/indra/llcommon/llapp.cpp
+++ b/linden/indra/llcommon/llapp.cpp
@@ -119,13 +119,8 @@ void LLApp::commonCtor()
mOptions.append(sd);
}
- // Make sure we clean up APR when we exit
- // Don't need to do this if we're cleaning up APR in the destructor
- //atexit(ll_cleanup_apr);
-
// Set the application to this instance.
sApplication = this;
-
}
LLApp::LLApp(LLErrorThread *error_thread) :
diff --git a/linden/indra/llcommon/llapr.cpp b/linden/indra/llcommon/llapr.cpp
index 7e3a26c..a013d9c 100644
--- a/linden/indra/llcommon/llapr.cpp
+++ b/linden/indra/llcommon/llapr.cpp
@@ -34,220 +34,7 @@
#include "linden_common.h"
#include "llapr.h"
-
-apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
-apr_thread_mutex_t *gLogMutexp = NULL;
-apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
-
-const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
-
-void ll_init_apr()
-{
- if (!gAPRPoolp)
- {
- // Initialize APR and create the global pool
- apr_initialize();
- apr_pool_create(&gAPRPoolp, NULL);
-
- // Initialize the logging mutex
- apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
- apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
-
- // Initialize thread-local APR pool support.
- LLVolatileAPRPool::initLocalAPRFilePool();
- }
-}
-
-
-void ll_cleanup_apr()
-{
- LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
-
- if (gLogMutexp)
- {
- // Clean up the logging mutex
-
- // All other threads NEED to be done before we clean up APR, so this is okay.
- apr_thread_mutex_destroy(gLogMutexp);
- gLogMutexp = NULL;
- }
- if (gCallStacksLogMutexp)
- {
- // Clean up the logging mutex
-
- // All other threads NEED to be done before we clean up APR, so this is okay.
- apr_thread_mutex_destroy(gCallStacksLogMutexp);
- gCallStacksLogMutexp = NULL;
- }
- if (gAPRPoolp)
- {
- apr_pool_destroy(gAPRPoolp);
- gAPRPoolp = NULL;
- }
- apr_terminate();
-}
-
-//
-//
-//LLAPRPool
-//
-LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
-{
- mParent = parent ;
- mReleasePoolFlag = releasePoolFlag ;
- mMaxSize = size ;
- mPool = NULL ;
-
- createAPRPool() ;
-}
-
-LLAPRPool::~LLAPRPool()
-{
- releaseAPRPool() ;
-}
-
-void LLAPRPool::createAPRPool()
-{
- if(mPool)
- {
- return ;
- }
-
- mStatus = apr_pool_create(&mPool, mParent);
- ll_apr_warn_status(mStatus) ;
-
- if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes.
- {
- apr_allocator_t *allocator = apr_pool_allocator_get(mPool);
- if (allocator)
- {
- apr_allocator_max_free_set(allocator, mMaxSize) ;
- }
- }
-}
-
-void LLAPRPool::releaseAPRPool()
-{
- if(!mPool)
- {
- return ;
- }
-
- if(!mParent || mReleasePoolFlag)
- {
- apr_pool_destroy(mPool) ;
- mPool = NULL ;
- }
-}
-
-apr_pool_t* LLAPRPool::getAPRPool()
-{
- if(!mPool)
- {
- createAPRPool() ;
- }
-
- return mPool ;
-}
-LLVolatileAPRPool::LLVolatileAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
- : LLAPRPool(parent, size, releasePoolFlag)
-{
- mNumActiveRef = 0 ;
- mNumTotalRef = 0 ;
-}
-
-apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
-{
- mNumTotalRef++ ;
- mNumActiveRef++ ;
- return getAPRPool() ;
-}
-
-void LLVolatileAPRPool::clearVolatileAPRPool()
-{
- if(mNumActiveRef > 0)
- {
- mNumActiveRef--;
- if(mNumActiveRef < 1)
- {
- if(isFull())
- {
- mNumTotalRef = 0 ;
-
- //destroy the apr_pool.
- releaseAPRPool() ;
- }
- else
- {
- //This does not actually free the memory,
- //it just allows the pool to re-use this memory for the next allocation.
- apr_pool_clear(mPool) ;
- }
- }
- }
- else
- {
- llassert_always(mNumActiveRef > 0) ;
- }
-
- //paranoia check if the pool is jammed.
- //will remove the check before going to release.
- llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
-}
-
-BOOL LLVolatileAPRPool::isFull()
-{
- return mNumTotalRef > FULL_VOLATILE_APR_POOL ;
-}
-
-#ifdef SHOW_ASSERT
-// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
-static void* gIsMainThread;
-bool is_main_thread() { return gIsMainThread == LLVolatileAPRPool::getLocalAPRFilePool(); }
-#endif
-
-// The thread private handle to access the LocalAPRFilePool.
-apr_threadkey_t* LLVolatileAPRPool::sLocalAPRFilePoolKey;
-
-// This should be called exactly once, before the first call to createLocalAPRFilePool.
-// static
-void LLVolatileAPRPool::initLocalAPRFilePool()
-{
- apr_status_t status = apr_threadkey_private_create(&sLocalAPRFilePoolKey, &destroyLocalAPRFilePool, gAPRPoolp);
- ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
- // total number of keys per process {PTHREAD_KEYS_MAX}
- // has been exceeded.
- // Create the thread-local pool for the main thread (this function is called by the main thread).
- createLocalAPRFilePool();
-#ifdef SHOW_ASSERT
- gIsMainThread = getLocalAPRFilePool();
-#endif
-}
-
-// This should be called once for every thread, before it uses getLocalAPRFilePool.
-// static
-void LLVolatileAPRPool::createLocalAPRFilePool()
-{
- void* thread_local_data = new LLVolatileAPRPool;
- apr_status_t status = apr_threadkey_private_set(thread_local_data, sLocalAPRFilePoolKey);
- llassert_always(status == APR_SUCCESS);
-}
-
-// This is called once for every thread when the thread is destructed.
-// static
-void LLVolatileAPRPool::destroyLocalAPRFilePool(void* thread_local_data)
-{
- delete reinterpret_cast(thread_local_data);
-}
-
-// static
-LLVolatileAPRPool* LLVolatileAPRPool::getLocalAPRFilePool()
-{
- void* thread_local_data;
- apr_status_t status = apr_threadkey_private_get(&thread_local_data, sLocalAPRFilePoolKey);
- llassert_always(status == APR_SUCCESS);
- return reinterpret_cast(thread_local_data);
-}
+#include "llscopedvolatileaprpool.h"
//---------------------------------------------------------------------
//
@@ -310,13 +97,15 @@ void ll_apr_assert_status(apr_status_t status)
//
LLAPRFile::LLAPRFile()
: mFile(NULL),
- mCurrentFilePoolp(NULL)
+ mVolatileFilePoolp(NULL),
+ mRegularFilePoolp(NULL)
{
}
LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type)
: mFile(NULL),
- mCurrentFilePoolp(NULL)
+ mVolatileFilePoolp(NULL),
+ mRegularFilePoolp(NULL)
{
open(filename, flags, access_type);
}
@@ -335,10 +124,16 @@ apr_status_t LLAPRFile::close()
mFile = NULL ;
}
- if(mCurrentFilePoolp)
+ if (mVolatileFilePoolp)
{
- mCurrentFilePoolp->clearVolatileAPRPool() ;
- mCurrentFilePoolp = NULL ;
+ mVolatileFilePoolp->clearVolatileAPRPool() ;
+ mVolatileFilePoolp = NULL ;
+ }
+
+ if (mRegularFilePoolp)
+ {
+ delete mRegularFilePoolp;
+ mRegularFilePoolp = NULL;
}
return ret ;
@@ -347,25 +142,28 @@ apr_status_t LLAPRFile::close()
apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep)
{
llassert_always(!mFile);
- llassert_always(!mCurrentFilePoolp);
+ llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp);
- // Access the pool and increment it's reference count.
- // The reference count of LLVolatileAPRPool objects will be decremented
- // again in LLAPRFile::close by calling mCurrentFilePoolp->clearVolatileAPRPool().
- apr_pool_t* pool;
- if (access_type == local)
- {
- // Use a "volatile" thread-local pool.
- mCurrentFilePoolp = LLVolatileAPRPool::getLocalAPRFilePool();
- pool = mCurrentFilePoolp->getVolatileAPRPool();
- }
- else
+ apr_status_t status;
{
- llassert(is_main_thread());
- pool = gAPRPoolp;
+ apr_pool_t* apr_file_open_pool;
+ if (access_type == local)
+ {
+ // Use a "volatile" thread-local pool.
+ mVolatileFilePoolp = &AIThreadLocalData::tldata().mVolatileAPRPool;
+ // Access the pool and increment it's reference count.
+ // The reference count of AIVolatileAPRPool objects will be decremented
+ // again in LLAPRFile::close by calling mVolatileFilePoolp->clearVolatileAPRPool().
+ apr_file_open_pool = mVolatileFilePoolp->getVolatileAPRPool();
+ }
+ else
+ {
+ mRegularFilePoolp = new AIAPRPool(AIThreadLocalData::tldata().mRootPool);
+ apr_file_open_pool = (*mRegularFilePoolp)();
+ }
+ status = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_file_open_pool);
}
- apr_status_t s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, pool);
- if (s != APR_SUCCESS || !mFile)
+ if (status != APR_SUCCESS || !mFile)
{
mFile = NULL ;
close() ;
@@ -373,7 +171,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
{
*sizep = 0;
}
- return s;
+ return status;
}
if (sizep)
@@ -390,7 +188,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
*sizep = file_size;
}
- return s;
+ return status;
}
// File I/O
@@ -440,17 +238,6 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
//static components of LLAPRFile
//
-// Used in the static functions below.
-class LLScopedVolatileAPRFilePool {
-private:
- LLVolatileAPRPool* mPool;
- apr_pool_t* apr_pool;
-public:
- LLScopedVolatileAPRFilePool() : mPool(LLVolatileAPRPool::getLocalAPRFilePool()), apr_pool(mPool->getVolatileAPRPool()) { }
- ~LLScopedVolatileAPRFilePool() { mPool->clearVolatileAPRPool(); }
- operator apr_pool_t*() const { return apr_pool; }
-};
-
//static
S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
{
@@ -487,7 +274,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
{
apr_file_t* file_handle;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
apr_status_t s = apr_file_open(&file_handle, filename.c_str(), APR_READ|APR_BINARY, APR_OS_DEFAULT, pool);
if (s != APR_SUCCESS || !file_handle)
{
@@ -539,7 +326,7 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
}
apr_file_t* file_handle;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
apr_status_t s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
if (s != APR_SUCCESS || !file_handle)
{
@@ -584,7 +371,7 @@ bool LLAPRFile::remove(const std::string& filename)
{
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_file_remove(filename.c_str(), pool);
if (s != APR_SUCCESS)
@@ -601,7 +388,7 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
{
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
if (s != APR_SUCCESS)
@@ -619,7 +406,7 @@ bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags)
apr_file_t* file_handle;
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
if (s != APR_SUCCESS || !file_handle)
@@ -640,7 +427,7 @@ S32 LLAPRFile::size(const std::string& filename)
apr_finfo_t info;
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
if (s != APR_SUCCESS || !file_handle)
@@ -669,7 +456,7 @@ bool LLAPRFile::makeDir(const std::string& dirname)
{
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
if (s != APR_SUCCESS)
@@ -686,7 +473,7 @@ bool LLAPRFile::removeDir(const std::string& dirname)
{
apr_status_t s;
- LLScopedVolatileAPRFilePool pool;
+ LLScopedVolatileAPRPool pool;
s = apr_file_remove(dirname.c_str(), pool);
if (s != APR_SUCCESS)
diff --git a/linden/indra/llcommon/llapr.h b/linden/indra/llcommon/llapr.h
index 2aed515..ded15f5 100644
--- a/linden/indra/llcommon/llapr.h
+++ b/linden/indra/llcommon/llapr.h
@@ -48,73 +48,8 @@
#include "apr_atomic.h"
#include "llstring.h"
-extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
-
-/**
- * @brief initialize the common apr constructs -- apr itself, the
- * global pool, and a mutex.
- */
-void LL_COMMON_API ll_init_apr();
-
-/**
- * @brief Cleanup those common apr constructs.
- */
-void LL_COMMON_API ll_cleanup_apr();
-
-//
-//LL apr_pool
-//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
-//
-class LL_COMMON_API LLAPRPool
-{
-public:
- LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
- ~LLAPRPool() ;
-
- apr_pool_t* getAPRPool() ;
- apr_status_t getStatus() {return mStatus ; }
-
-protected:
- void releaseAPRPool() ;
- void createAPRPool() ;
-
-protected:
- apr_pool_t* mPool ; //pointing to an apr_pool
- apr_pool_t* mParent ; //parent pool
- apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
- apr_status_t mStatus ; //status when creating the pool
- BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
-};
-
-//
-//volatile LL apr_pool
-//which clears memory automatically.
-//so it can not hold static data or data after memory is cleared
-//
-class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool
-{
-public:
- LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
- ~LLVolatileAPRPool(){}
-
- apr_pool_t* getVolatileAPRPool() ;
-
- void clearVolatileAPRPool() ;
-
- BOOL isFull() ;
- BOOL isEmpty() {return !mNumActiveRef ;}
-
- static void initLocalAPRFilePool();
- static void createLocalAPRFilePool();
- static void destroyLocalAPRFilePool(void* thread_local_data);
- static LLVolatileAPRPool* getLocalAPRFilePool();
-
-private:
- S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
- S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
-
- static apr_threadkey_t* sLocalAPRFilePoolKey;
-} ;
+class AIAPRPool;
+class AIVolatileAPRPool;
/**
* @class LLScopedLock
@@ -205,7 +140,8 @@ class LL_COMMON_API LLAPRFile : boost::noncopyable
// make this non copyable since a copy closes the file
private:
apr_file_t* mFile ;
- LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
+ AIVolatileAPRPool* mVolatileFilePoolp; // (Thread local) APR pool currently in use.
+ AIAPRPool* mRegularFilePoolp; // ...or a regular pool.
public:
enum access_t {
@@ -260,6 +196,4 @@ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
-extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
-
#endif // LL_LLAPR_H
diff --git a/linden/indra/llcommon/llcommon.cpp b/linden/indra/llcommon/llcommon.cpp
index 2cbb718..298dd46 100644
--- a/linden/indra/llcommon/llcommon.cpp
+++ b/linden/indra/llcommon/llcommon.cpp
@@ -35,17 +35,9 @@
#include "llthread.h"
//static
-BOOL LLCommon::sAprInitialized = FALSE;
-
-//static
void LLCommon::initClass()
{
LLMemory::initClass();
- if (!sAprInitialized)
- {
- ll_init_apr();
- sAprInitialized = TRUE;
- }
LLTimer::initClass();
LLThreadSafeRefCount::initThreadSafeRefCount();
// LLWorkerThread::initClass();
@@ -59,10 +51,5 @@ void LLCommon::cleanupClass()
// LLWorkerThread::cleanupClass();
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
LLTimer::cleanupClass();
- if (sAprInitialized)
- {
- ll_cleanup_apr();
- sAprInitialized = FALSE;
- }
LLMemory::cleanupClass();
}
diff --git a/linden/indra/llcommon/llcommon.h b/linden/indra/llcommon/llcommon.h
index 851d4ac..300ebe2 100644
--- a/linden/indra/llcommon/llcommon.h
+++ b/linden/indra/llcommon/llcommon.h
@@ -43,8 +43,6 @@ class LL_COMMON_API LLCommon
public:
static void initClass();
static void cleanupClass();
-private:
- static BOOL sAprInitialized;
};
#endif
diff --git a/linden/indra/llcommon/llerror.cpp b/linden/indra/llcommon/llerror.cpp
index edc570f..b9be370 100644
--- a/linden/indra/llcommon/llerror.cpp
+++ b/linden/indra/llcommon/llerror.cpp
@@ -871,6 +871,9 @@ You get:
*/
+apr_thread_mutex_t* gLogMutexp;
+apr_thread_mutex_t* gCallStacksLogMutexp;
+
namespace {
bool checkLevelMap(const LevelMap& map, const std::string& key,
LLError::ELevel& level)
diff --git a/linden/indra/llcommon/llfixedbuffer.cpp b/linden/indra/llcommon/llfixedbuffer.cpp
index e9d6029..37a12ad 100644
--- a/linden/indra/llcommon/llfixedbuffer.cpp
+++ b/linden/indra/llcommon/llfixedbuffer.cpp
@@ -33,9 +33,8 @@
#include "llfixedbuffer.h"
LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
- : mMutex(NULL)
+ : mMaxLines(max_lines)
{
- mMaxLines = max_lines;
mTimer.reset();
}
diff --git a/linden/indra/llcommon/llscopedvolatileaprpool.h b/linden/indra/llcommon/llscopedvolatileaprpool.h
new file mode 100644
index 0000000..724dc7f
--- /dev/null
+++ b/linden/indra/llcommon/llscopedvolatileaprpool.h
@@ -0,0 +1,58 @@
+/**
+ * @file llscopedvolatileaprpool.h
+ * @brief Implementation of LLScopedVolatileAPRPool
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSCOPEDVOLATILEAPRPOOL_H
+#define LL_LLSCOPEDVOLATILEAPRPOOL_H
+
+#include "llthread.h"
+
+//! Scoped volatile memory pool.
+//
+// As the AIVolatileAPRPool should never keep allocations very
+// long, it's most common use is for allocations with a lifetime
+// equal to it's scope.
+//
+// This is a convenience class that makes just a little easier to type.
+//
+class LLScopedVolatileAPRPool
+{
+private:
+ AIVolatileAPRPool& mPool;
+ apr_pool_t* mScopedAPRpool;
+public:
+ LLScopedVolatileAPRPool() : mPool(AIThreadLocalData::tldata().mVolatileAPRPool), mScopedAPRpool(mPool.getVolatileAPRPool()) { }
+ ~LLScopedVolatileAPRPool() { mPool.clearVolatileAPRPool(); }
+ // Only use this to pass the pointer to a libapr-1 function that requires it.
+ operator apr_pool_t*() const { return mScopedAPRpool; }
+};
+
+#endif
diff --git a/linden/indra/llcommon/llthread.cpp b/linden/indra/llcommon/llthread.cpp
index 692d6c4..f7732cb 100644
--- a/linden/indra/llcommon/llthread.cpp
+++ b/linden/indra/llcommon/llthread.cpp
@@ -72,8 +72,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
// Set thread state to running
threadp->mStatus = RUNNING;
- // Create a thread local APRFile pool.
- LLVolatileAPRPool::createLocalAPRFilePool();
+ // Create a thread local data.
+ AIThreadLocalData::create(threadp);
// Run the user supplied function
threadp->run();
@@ -87,24 +87,14 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
}
-LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
+LLThread::LLThread(std::string const& name) :
mPaused(false),
mName(name),
mAPRThreadp(NULL),
- mStatus(STOPPED)
+ mStatus(STOPPED),
+ mThreadLocalData(NULL)
{
- // Thread creation probably CAN be paranoid about APR being initialized, if necessary
- if (poolp)
- {
- mIsLocalPool = false;
- mAPRPoolp = poolp;
- }
- else
- {
- mIsLocalPool = true;
- apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
- }
- mRunCondition = new LLCondition(mAPRPoolp);
+ mRunCondition = new LLCondition;
}
@@ -147,24 +137,18 @@ void LLThread::shutdown()
if (!isStopped())
{
// This thread just wouldn't stop, even though we gave it time
- llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+ llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl;
return;
}
mAPRThreadp = NULL;
}
delete mRunCondition;
-
- if (mIsLocalPool)
- {
- apr_pool_destroy(mAPRPoolp);
- }
}
-
void LLThread::start()
{
- apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+ apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool());
// We won't bother joining
apr_thread_detach(mAPRThreadp);
@@ -265,38 +249,72 @@ void LLThread::wakeLocked()
}
}
-//============================================================================
+#ifdef SHOW_ASSERT
+// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
+static apr_os_thread_t main_thread_id;
+bool is_main_thread() { 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 AIThreadLocalData instance.
+apr_threadkey_t* AIThreadLocalData::sThreadLocalDataKey;
+
+//static
+void AIThreadLocalData::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, &AIThreadLocalData::destroy, AIAPRRootPool::get()());
+ ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
+ // total number of keys per process {PTHREAD_KEYS_MAX}
+ // has been exceeded.
+
+ // Create the thread-local data for the main thread (this function is called by the main thread).
+ AIThreadLocalData::create(NULL);
+
+#ifdef SHOW_ASSERT
+ // This function is called by the main thread.
+ main_thread_id = apr_os_thread_current();
+#endif
}
-LLMutex::~LLMutex()
+// This is called once for every thread when the thread is destructed.
+//static
+void AIThreadLocalData::destroy(void* thread_local_data)
{
-#if _DEBUG
- llassert(!isLocked()); // better not be locked!
-#endif
- apr_thread_mutex_destroy(mAPRMutexp);
- mAPRMutexp = NULL;
- if (mIsLocalPool)
+ delete reinterpret_cast(thread_local_data);
+}
+
+//static
+void AIThreadLocalData::create(LLThread* threadp)
+{
+ AIThreadLocalData* new_tld = new AIThreadLocalData;
+ if (threadp)
{
- apr_pool_destroy(mAPRPoolp);
+ threadp->mThreadLocalData = new_tld;
}
+ apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey);
+ llassert_always(status == APR_SUCCESS);
}
-bool LLMutex::isLocked()
+//static
+AIThreadLocalData& AIThreadLocalData::tldata(void)
+{
+ if (!sThreadLocalDataKey)
+ AIThreadLocalData::init();
+
+ void* data;
+ apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey);
+ llassert_always(status == APR_SUCCESS);
+ return *static_cast(data);
+}
+
+//============================================================================
+
+bool LLMutexBase::isLocked()
{
if (!tryLock())
{
@@ -308,12 +326,9 @@ bool LLMutex::isLocked()
//============================================================================
-LLCondition::LLCondition(apr_pool_t *poolp) :
- LLMutex(poolp)
+LLCondition::LLCondition(AIAPRPool& parent) : LLMutex(parent)
{
- // base class (LLMutex) has already ensured that mAPRPoolp is set up.
-
- apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
+ apr_thread_cond_create(&mAPRCondp, mPool());
}
LLCondition::~LLCondition()
@@ -349,7 +364,7 @@ void LLThreadSafeRefCount::initThreadSafeRefCount()
{
if (!sMutex)
{
- sMutex = new LLMutex(0);
+ sMutex = new LLMutex;
}
}
diff --git a/linden/indra/llcommon/llthread.h b/linden/indra/llcommon/llthread.h
index 98d64ef..8708929 100644
--- a/linden/indra/llcommon/llthread.h
+++ b/linden/indra/llcommon/llthread.h
@@ -38,11 +38,28 @@
#include "llmemory.h"
#include "apr_thread_cond.h"
+#include "aiaprpool.h"
class LLThread;
class LLMutex;
class LLCondition;
+class LL_COMMON_API AIThreadLocalData
+{
+private:
+ static apr_threadkey_t* sThreadLocalDataKey;
+
+public:
+ // Thread-local memory pool.
+ AIAPRRootPool mRootPool;
+ AIVolatileAPRPool mVolatileAPRPool;
+
+ static void init(void);
+ static void destroy(void* thread_local_data);
+ static void create(LLThread* pthread);
+ static AIThreadLocalData& tldata(void);
+};
+
class LL_COMMON_API LLThread
{
public:
@@ -53,7 +70,7 @@ public:
QUITTING= 2 // Someone wants this thread to quit
} EThreadStatus;
- LLThread(const std::string& name, apr_pool_t *poolp = NULL);
+ LLThread(std::string const& name);
virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state.
virtual void shutdown(); // stops the thread
@@ -82,7 +99,8 @@ public:
// this kicks off the apr thread
void start(void);
- apr_pool_t *getAPRPool() { return mAPRPoolp; }
+ // Return thread-local data for the current thread.
+ static AIThreadLocalData& tldata(void) { return AIThreadLocalData::tldata(); }
private:
bool mPaused;
@@ -95,10 +113,11 @@ protected:
LLCondition* mRunCondition;
apr_thread_t *mAPRThreadp;
- apr_pool_t *mAPRPoolp;
- bool mIsLocalPool;
EThreadStatus mStatus;
+ friend void AIThreadLocalData::create(LLThread* threadp);
+ AIThreadLocalData* mThreadLocalData;
+
void setQuitting();
// virtual function overridden by subclass -- this will be called when the thread runs
@@ -125,12 +144,9 @@ protected:
//============================================================================
-class LL_COMMON_API LLMutex
+class LL_COMMON_API LLMutexBase
{
public:
- LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
- ~LLMutex();
-
void lock() { apr_thread_mutex_lock(mAPRMutexp); }
void unlock() { apr_thread_mutex_unlock(mAPRMutexp); }
// Returns true if lock was obtained successfully.
@@ -139,16 +155,60 @@ public:
bool isLocked(); // non-blocking, but does do a lock/unlock so not free
protected:
- apr_thread_mutex_t *mAPRMutexp;
- apr_pool_t *mAPRPoolp;
- bool mIsLocalPool;
+ // mAPRMutexp is initialized and uninitialized in the derived class.
+ apr_thread_mutex_t* mAPRMutexp;
+};
+
+class LL_COMMON_API LLMutex : public LLMutexBase
+{
+public:
+ LLMutex(AIAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent)
+ {
+ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mPool());
+ }
+ ~LLMutex()
+ {
+ llassert(!isLocked()); // better not be locked!
+ apr_thread_mutex_destroy(mAPRMutexp);
+ mAPRMutexp = NULL;
+ }
+
+protected:
+ AIAPRPool mPool;
+};
+
+#if APR_HAS_THREADS
+// No need to use a root pool in this case.
+typedef LLMutex LLMutexRootPool;
+#else // APR_HAS_THREADS
+class LL_COMMON_API LLMutexRootPool : public LLMutexBase
+{
+public:
+ LLMutexRootPool(void)
+ {
+ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mRootPool());
+ }
+ ~LLMutexRootPool()
+ {
+#if APR_POOL_DEBUG
+ // It is allowed to destruct root pools from a different thread.
+ mRootPool.grab_ownership();
+#endif
+ llassert(!isLocked()); // better not be locked!
+ apr_thread_mutex_destroy(mAPRMutexp);
+ mAPRMutexp = NULL;
+ }
+
+protected:
+ AIAPRRootPool mRootPool;
};
+#endif // APR_HAS_THREADS
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
class LL_COMMON_API LLCondition : public LLMutex
{
public:
- LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+ LLCondition(AIAPRPool& parent = LLThread::tldata().mRootPool);
~LLCondition();
void wait(); // blocks
@@ -162,7 +222,7 @@ protected:
class LL_COMMON_API LLMutexLock
{
public:
- LLMutexLock(LLMutex* mutex)
+ LLMutexLock(LLMutexBase* mutex)
{
mMutex = mutex;
mMutex->lock();
@@ -172,7 +232,7 @@ public:
mMutex->unlock();
}
private:
- LLMutex* mMutex;
+ LLMutexBase* mMutex;
};
//============================================================================
diff --git a/linden/indra/llcommon/llworkerthread.cpp b/linden/indra/llcommon/llworkerthread.cpp
index 8195e1c..e238a89 100644
--- a/linden/indra/llcommon/llworkerthread.cpp
+++ b/linden/indra/llcommon/llworkerthread.cpp
@@ -43,7 +43,7 @@
LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
LLQueuedThread(name, threaded)
{
- mDeleteMutex = new LLMutex(NULL);
+ mDeleteMutex = new LLMutex;
}
LLWorkerThread::~LLWorkerThread()
@@ -183,7 +183,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
: mWorkerThread(workerthread),
mWorkerClassName(name),
mRequestHandle(LLWorkerThread::nullHandle()),
- mMutex(NULL),
mWorkFlags(0)
{
if (!mWorkerThread)
diff --git a/linden/indra/llcommon/llworkerthread.h b/linden/indra/llcommon/llworkerthread.h
index 33d4c40..8e0dd8a 100644
--- a/linden/indra/llcommon/llworkerthread.h
+++ b/linden/indra/llcommon/llworkerthread.h
@@ -194,7 +194,7 @@ protected:
U32 mRequestPriority; // last priority set
private:
- LLMutex mMutex;
+ LLMutexRootPool mMutex; // Use LLMutexRootPool since this object is created and destructed by multiple threads.
LLAtomicU32 mWorkFlags;
};
diff --git a/linden/indra/llcrashlogger/llcrashlogger.cpp b/linden/indra/llcrashlogger/llcrashlogger.cpp
index d25be55..b6afbc6 100755
--- a/linden/indra/llcrashlogger/llcrashlogger.cpp
+++ b/linden/indra/llcrashlogger/llcrashlogger.cpp
@@ -387,8 +387,7 @@ bool LLCrashLogger::init()
return false;
}
- gServicePump = new LLPumpIO(gAPRPoolp);
- gServicePump->prime(gAPRPoolp);
+ gServicePump = new LLPumpIO;
LLHTTPClient::setPump(*gServicePump);
//If we've opened the crash logger, assume we can delete the marker file if it exists
diff --git a/linden/indra/llimage/llimage.cpp b/linden/indra/llimage/llimage.cpp
index 776c481..e933750 100644
--- a/linden/indra/llimage/llimage.cpp
+++ b/linden/indra/llimage/llimage.cpp
@@ -57,7 +57,7 @@ LLMutex* LLImage::sMutex = NULL;
//static
void LLImage::initClass(const bool& useDSO)
{
- sMutex = new LLMutex(NULL);
+ sMutex = new LLMutex;
if (useDSO)
{
LLImageJ2C::openDSO();
diff --git a/linden/indra/llimage/llimagej2c.cpp b/linden/indra/llimage/llimagej2c.cpp
index b99ebff..9e88bcd 100644
--- a/linden/indra/llimage/llimagej2c.cpp
+++ b/linden/indra/llimage/llimagej2c.cpp
@@ -46,7 +46,7 @@ typedef const char* (*EngineInfoLLImageJ2CFunction)();
CreateLLImageJ2CFunction j2cimpl_create_func;
DestroyLLImageJ2CFunction j2cimpl_destroy_func;
EngineInfoLLImageJ2CFunction j2cimpl_engineinfo_func;
-apr_pool_t *j2cimpl_dso_memory_pool;
+AIAPRPool j2cimpl_dso_memory_pool;
apr_dso_handle_t *j2cimpl_dso_handle;
//Declare the prototype for theses functions here, their functionality
@@ -81,13 +81,12 @@ void LLImageJ2C::openDSO()
gDirUtilp->getExecutableDir());
j2cimpl_dso_handle = NULL;
- j2cimpl_dso_memory_pool = NULL;
+ j2cimpl_dso_memory_pool.create();
//attempt to load the shared library
- apr_pool_create(&j2cimpl_dso_memory_pool, NULL);
rv = apr_dso_load(&j2cimpl_dso_handle,
dso_path.c_str(),
- j2cimpl_dso_memory_pool);
+ j2cimpl_dso_memory_pool());
//now, check for success
if ( rv == APR_SUCCESS )
@@ -151,11 +150,7 @@ void LLImageJ2C::openDSO()
j2cimpl_dso_handle = NULL;
}
- if ( j2cimpl_dso_memory_pool )
- {
- apr_pool_destroy(j2cimpl_dso_memory_pool);
- j2cimpl_dso_memory_pool = NULL;
- }
+ j2cimpl_dso_memory_pool.destroy();
}
}
@@ -163,7 +158,7 @@ void LLImageJ2C::openDSO()
void LLImageJ2C::closeDSO()
{
if ( j2cimpl_dso_handle ) apr_dso_unload(j2cimpl_dso_handle);
- if (j2cimpl_dso_memory_pool) apr_pool_destroy(j2cimpl_dso_memory_pool);
+ j2cimpl_dso_memory_pool.destroy();
}
//static
diff --git a/linden/indra/llimage/llimageworker.cpp b/linden/indra/llimage/llimageworker.cpp
index 558a968..dc989e5 100644
--- a/linden/indra/llimage/llimageworker.cpp
+++ b/linden/indra/llimage/llimageworker.cpp
@@ -41,14 +41,13 @@
LLImageDecodeThread::LLImageDecodeThread(bool threaded)
: LLQueuedThread("imagedecode", threaded)
{
- mCreationMutex = new LLMutex(getAPRPool());
}
// MAIN THREAD
// virtual
S32 LLImageDecodeThread::update(U32 max_time_ms)
{
- LLMutexLock lock(mCreationMutex);
+ LLMutexLock lock(&mCreationMutex);
for (creation_list_t::iterator iter = mCreationList.begin();
iter != mCreationList.end(); ++iter)
{
@@ -71,7 +70,7 @@ S32 LLImageDecodeThread::update(U32 max_time_ms)
LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
{
- LLMutexLock lock(mCreationMutex);
+ LLMutexLock lock(&mCreationMutex);
handle_t handle = generateHandle();
mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder));
return handle;
@@ -81,7 +80,7 @@ LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted*
// Returns the size of the mutex guarded list as an indication of sanity
S32 LLImageDecodeThread::tut_size()
{
- LLMutexLock lock(mCreationMutex);
+ LLMutexLock lock(&mCreationMutex);
S32 res = mCreationList.size();
return res;
}
diff --git a/linden/indra/llimage/llimageworker.h b/linden/indra/llimage/llimageworker.h
index 0260206..fa2a8fa 100644
--- a/linden/indra/llimage/llimageworker.h
+++ b/linden/indra/llimage/llimageworker.h
@@ -101,7 +101,7 @@ private:
};
typedef std::list creation_list_t;
creation_list_t mCreationList;
- LLMutex* mCreationMutex;
+ LLMutex mCreationMutex;
};
#endif
diff --git a/linden/indra/llmath/llvolumemgr.cpp b/linden/indra/llmath/llvolumemgr.cpp
index 53641fc..8bfebcb 100644
--- a/linden/indra/llmath/llvolumemgr.cpp
+++ b/linden/indra/llmath/llvolumemgr.cpp
@@ -55,7 +55,7 @@ LLVolumeMgr::LLVolumeMgr()
{
// the LLMutex magic interferes with easy unit testing,
// so you now must manually call useMutex() to use it
- //mDataMutex = new LLMutex(gAPRPoolp);
+ //mDataMutex = new LLMutex;
}
LLVolumeMgr::~LLVolumeMgr()
@@ -222,7 +222,7 @@ void LLVolumeMgr::useMutex()
{
if (!mDataMutex)
{
- mDataMutex = new LLMutex(gAPRPoolp);
+ mDataMutex = new LLMutex;
}
}
diff --git a/linden/indra/llmessage/llares.cpp b/linden/indra/llmessage/llares.cpp
index fe37fe8..5a6794e 100644
--- a/linden/indra/llmessage/llares.cpp
+++ b/linden/indra/llmessage/llares.cpp
@@ -43,6 +43,7 @@
#include "llapr.h"
#include "llares.h"
+#include "llscopedvolatileaprpool.h"
#if defined(LL_WINDOWS)
# define ns_c_in 1
@@ -463,11 +464,6 @@ void LLAres::search(const std::string &query, LLResType type,
bool LLAres::process(U64 timeout)
{
- if (!gAPRPoolp)
- {
- ll_init_apr();
- }
-
int socks[ARES_GETSOCK_MAXNUM];
apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM];
apr_int32_t nsds = 0;
@@ -481,10 +477,7 @@ bool LLAres::process(U64 timeout)
return nsds > 0;
}
- apr_status_t status;
- LLAPRPool pool;
- status = pool.getStatus() ;
- ll_apr_assert_status(status);
+ LLScopedVolatileAPRPool scoped_pool;
for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++)
{
@@ -501,7 +494,7 @@ bool LLAres::process(U64 timeout)
apr_socket_t *aprSock = NULL;
- status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool());
+ apr_status_t status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], scoped_pool);
if (status != APR_SUCCESS)
{
ll_apr_warn_status(status);
@@ -510,7 +503,7 @@ bool LLAres::process(U64 timeout)
aprFds[nactive].desc.s = aprSock;
aprFds[nactive].desc_type = APR_POLL_SOCKET;
- aprFds[nactive].p = pool.getAPRPool();
+ aprFds[nactive].p = scoped_pool;
aprFds[nactive].rtnevents = 0;
aprFds[nactive].client_data = &socks[i];
@@ -519,7 +512,7 @@ bool LLAres::process(U64 timeout)
if (nactive > 0)
{
- status = apr_poll(aprFds, nactive, &nsds, timeout);
+ apr_status_t status = apr_poll(aprFds, nactive, &nsds, timeout);
if (status != APR_SUCCESS && status != APR_TIMEUP)
{
diff --git a/linden/indra/llmessage/llcurl.cpp b/linden/indra/llmessage/llcurl.cpp
index 202332c..9ecfee4 100644
--- a/linden/indra/llmessage/llcurl.cpp
+++ b/linden/indra/llmessage/llcurl.cpp
@@ -1020,7 +1020,7 @@ void LLCurl::initClass()
S32 mutex_count = CRYPTO_num_locks();
for (S32 i=0; i factory_ptr(factory);
- LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr);
+ LLIOServerSocket* server = new LLIOServerSocket(socket, factory_ptr);
LLPumpIO::chain_t chain;
chain.push_back(LLIOPipe::ptr_t(server));
diff --git a/linden/indra/llmessage/lliohttpserver.h b/linden/indra/llmessage/lliohttpserver.h
index d1c9bdd..d2a8e2a 100644
--- a/linden/indra/llmessage/lliohttpserver.h
+++ b/linden/indra/llmessage/lliohttpserver.h
@@ -57,7 +57,7 @@ class LLIOHTTPServer
public:
typedef void (*timing_callback_t)(const char* hashed_name, F32 time, void* data);
- static LLHTTPNode& create(apr_pool_t* pool, LLPumpIO& pump, U16 port);
+ static LLHTTPNode& create(LLPumpIO& pump, U16 port);
/**< Creates an HTTP wire server on the pump for the given TCP port.
*
* Returns the root node of the new server. Add LLHTTPNode instances
diff --git a/linden/indra/llmessage/lliosocket.cpp b/linden/indra/llmessage/lliosocket.cpp
index 7ec577c..686c037 100644
--- a/linden/indra/llmessage/lliosocket.cpp
+++ b/linden/indra/llmessage/lliosocket.cpp
@@ -41,6 +41,7 @@
#include "llhost.h"
#include "llmemtype.h"
#include "llpumpio.h"
+#include "llthread.h"
//
// constants
@@ -104,51 +105,31 @@ void ll_debug_socket(const char* msg, apr_socket_t* apr_sock)
///
// static
-LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
+LLSocket::ptr_t LLSocket::create(EType type, U16 port)
{
LLMemType m1(LLMemType::MTYPE_IO_TCP);
- LLSocket::ptr_t rv;
- apr_socket_t* socket = NULL;
- apr_pool_t* new_pool = NULL;
apr_status_t status = APR_EGENERAL;
-
- // create a pool for the socket
- status = apr_pool_create(&new_pool, pool);
- if(ll_apr_warn_status(status))
- {
- if(new_pool) apr_pool_destroy(new_pool);
- return rv;
- }
+ LLSocket::ptr_t rv(new LLSocket);
if(STREAM_TCP == type)
{
- status = apr_socket_create(
- &socket,
- APR_INET,
- SOCK_STREAM,
- APR_PROTO_TCP,
- new_pool);
+ status = apr_socket_create(&rv->mSocket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, rv->mPool());
}
else if(DATAGRAM_UDP == type)
{
- status = apr_socket_create(
- &socket,
- APR_INET,
- SOCK_DGRAM,
- APR_PROTO_UDP,
- new_pool);
+ status = apr_socket_create(&rv->mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, rv->mPool());
}
else
{
- if(new_pool) apr_pool_destroy(new_pool);
+ rv.reset();
return rv;
}
if(ll_apr_warn_status(status))
{
- if(new_pool) apr_pool_destroy(new_pool);
+ rv->mSocket = NULL;
+ rv.reset();
return rv;
}
- rv = ptr_t(new LLSocket(socket, new_pool));
if(port > 0)
{
apr_sockaddr_t* sa = NULL;
@@ -158,7 +139,7 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
APR_UNSPEC,
port,
0,
- new_pool);
+ rv->mPool());
if(ll_apr_warn_status(status))
{
rv.reset();
@@ -166,8 +147,8 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
}
// This allows us to reuse the address on quick down/up. This
// is unlikely to create problems.
- ll_apr_warn_status(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1));
- status = apr_socket_bind(socket, sa);
+ ll_apr_warn_status(apr_socket_opt_set(rv->mSocket, APR_SO_REUSEADDR, 1));
+ status = apr_socket_bind(rv->mSocket, sa);
if(ll_apr_warn_status(status))
{
rv.reset();
@@ -181,7 +162,7 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
// to keep a queue of incoming connections for ACCEPT.
lldebugs << "Setting listen state for socket." << llendl;
status = apr_socket_listen(
- socket,
+ rv->mSocket,
LL_DEFAULT_LISTEN_BACKLOG);
if(ll_apr_warn_status(status))
{
@@ -202,21 +183,28 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
}
// static
-LLSocket::ptr_t LLSocket::create(apr_socket_t* socket, apr_pool_t* pool)
+LLSocket::ptr_t LLSocket::create(apr_status_t& status, LLSocket::ptr_t& listen_socket)
{
LLMemType m1(LLMemType::MTYPE_IO_TCP);
- LLSocket::ptr_t rv;
- if(!socket)
+ if (!listen_socket->getSocket())
+ {
+ status = APR_ENOSOCKET;
+ return LLSocket::ptr_t();
+ }
+ LLSocket::ptr_t rv(new LLSocket);
+ lldebugs << "accepting socket" << llendl;
+ status = apr_socket_accept(&rv->mSocket, listen_socket->getSocket(), rv->mPool());
+ if (status != APR_SUCCESS)
{
+ rv->mSocket = NULL;
+ rv.reset();
return rv;
}
- rv = ptr_t(new LLSocket(socket, pool));
rv->mPort = PORT_EPHEMERAL;
rv->setOptions();
return rv;
}
-
bool LLSocket::blockingConnect(const LLHost& host)
{
if(!mSocket) return false;
@@ -229,7 +217,7 @@ bool LLSocket::blockingConnect(const LLHost& host)
APR_UNSPEC,
host.getPort(),
0,
- mPool)))
+ mPool())))
{
return false;
}
@@ -240,13 +228,11 @@ bool LLSocket::blockingConnect(const LLHost& host)
return true;
}
-LLSocket::LLSocket(apr_socket_t* socket, apr_pool_t* pool) :
- mSocket(socket),
- mPool(pool),
+LLSocket::LLSocket() :
+ mSocket(NULL),
+ mPool(LLThread::tldata().mRootPool),
mPort(PORT_INVALID)
{
- ll_debug_socket("Constructing wholely formed socket", mSocket);
- LLMemType m1(LLMemType::MTYPE_IO_TCP);
}
LLSocket::~LLSocket()
@@ -258,10 +244,6 @@ LLSocket::~LLSocket()
ll_debug_socket("Destroying socket", mSocket);
apr_socket_close(mSocket);
}
- if(mPool)
- {
- apr_pool_destroy(mPool);
- }
}
void LLSocket::setOptions()
@@ -522,10 +504,8 @@ LLIOPipe::EStatus LLIOSocketWriter::process_impl(
///
LLIOServerSocket::LLIOServerSocket(
- apr_pool_t* pool,
LLIOServerSocket::socket_t listener,
factory_t factory) :
- mPool(pool),
mListenSocket(listener),
mReactor(factory),
mInitialized(false),
@@ -585,21 +565,15 @@ LLIOPipe::EStatus LLIOServerSocket::process_impl(
lldebugs << "accepting socket" << llendl;
PUMP_DEBUG;
- apr_pool_t* new_pool = NULL;
- apr_status_t status = apr_pool_create(&new_pool, mPool);
- apr_socket_t* socket = NULL;
- status = apr_socket_accept(
- &socket,
- mListenSocket->getSocket(),
- new_pool);
- LLSocket::ptr_t llsocket(LLSocket::create(socket, new_pool));
+ apr_status_t status;
+ LLSocket::ptr_t llsocket(LLSocket::create(status, mListenSocket));
//EStatus rv = STATUS_ERROR;
- if(llsocket)
+ if(llsocket && status == APR_SUCCESS)
{
PUMP_DEBUG;
apr_sockaddr_t* remote_addr;
- apr_socket_addr_get(&remote_addr, APR_REMOTE, socket);
+ apr_socket_addr_get(&remote_addr, APR_REMOTE, llsocket->getSocket());
char* remote_host_string;
apr_sockaddr_ip_get(&remote_host_string, remote_addr);
@@ -614,7 +588,6 @@ LLIOPipe::EStatus LLIOServerSocket::process_impl(
{
chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(llsocket)));
pump->addChain(chain, mResponseTimeout);
- status = STATUS_OK;
}
else
{
@@ -623,7 +596,8 @@ LLIOPipe::EStatus LLIOServerSocket::process_impl(
}
else
{
- llwarns << "Unable to create linden socket." << llendl;
+ char buf[256];
+ llwarns << "Unable to accept linden socket: " << apr_strerror(status, buf, sizeof(buf)) << llendl;
}
PUMP_DEBUG;
@@ -636,11 +610,10 @@ LLIOPipe::EStatus LLIOServerSocket::process_impl(
#if 0
LLIODataSocket::LLIODataSocket(
U16 suggested_port,
- U16 start_discovery_port,
- apr_pool_t* pool) :
+ U16 start_discovery_port) :
mSocket(NULL)
{
- if(!pool || (PORT_INVALID == suggested_port)) return;
+ if(PORT_INVALID == suggested_port) return;
if(ll_apr_warn_status(apr_socket_create(&mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, pool))) return;
apr_sockaddr_t* sa = NULL;
if(ll_apr_warn_status(apr_sockaddr_info_get(&sa, APR_ANYADDR, APR_UNSPEC, suggested_port, 0, pool))) return;
diff --git a/linden/indra/llmessage/lliosocket.h b/linden/indra/llmessage/lliosocket.h
index ec09ad8..5ad75e9 100644
--- a/linden/indra/llmessage/lliosocket.h
+++ b/linden/indra/llmessage/lliosocket.h
@@ -44,7 +44,6 @@
*/
#include "lliopipe.h"
-#include "apr_pools.h"
#include "apr_network_io.h"
#include "llchainio.h"
@@ -94,34 +93,22 @@ public:
* socket. If you intend the socket to be known to external
* clients without prior port notification, do not use
* PORT_EPHEMERAL.
- * @param pool The apr pool to use. A child pool will be created
- * and associated with the socket.
* @param type The type of socket to create
* @param port The port for the socket
* @return A valid socket shared pointer if the call worked.
*/
static ptr_t create(
- apr_pool_t* pool,
EType type,
U16 port = PORT_EPHEMERAL);
/**
- * @brief Create a LLSocket when you already have an apr socket.
+ * @brief Create a LLSocket by accepting a connection from a listen socket.
*
- * This method assumes an ephemeral port. This is typically used
- * by calls which spawn a socket such as a call to
- * accept()
as in the server socket. This call should
- * not fail if you have a valid apr socket.
- * Because of the nature of how accept() works, you are expected
- * to create a new pool for the socket, use that pool for the
- * accept, and pass it in here where it will be bound with the
- * socket and destroyed at the same time.
- * @param socket The apr socket to use
- * @param pool The pool used to create the socket. *NOTE: The pool
- * passed in will be DESTROYED.
+ * @param status Output. Status of the accept if a valid listen socket was passed.
+ * @param listen_socket The listen socket to use.
* @return A valid socket shared pointer if the call worked.
*/
- static ptr_t create(apr_socket_t* socket, apr_pool_t* pool);
+ static ptr_t create(apr_status_t& status, ptr_t& listen_socket);
/**
* @brief Perform a blocking connect to a host. Do not use in production.
@@ -156,7 +143,7 @@ protected:
* @brief Protected constructor since should only make sockets
* with one of the two create()
calls.
*/
- LLSocket(apr_socket_t* socket, apr_pool_t* pool);
+ LLSocket(void);
/**
* @brief Set default socket options.
@@ -173,8 +160,8 @@ protected:
// The apr socket.
apr_socket_t* mSocket;
- // our memory pool
- apr_pool_t* mPool;
+ // Our memory pool.
+ AIAPRPool mPool;
// The port if we know it.
U16 mPort;
@@ -299,7 +286,7 @@ class LLIOServerSocket : public LLIOPipe
public:
typedef LLSocket::ptr_t socket_t;
typedef boost::shared_ptr factory_t;
- LLIOServerSocket(apr_pool_t* pool, socket_t listener, factory_t reactor);
+ LLIOServerSocket(socket_t listener, factory_t reactor);
virtual ~LLIOServerSocket();
/**
@@ -331,7 +318,6 @@ protected:
//@}
protected:
- apr_pool_t* mPool;
socket_t mListenSocket;
factory_t mReactor;
bool mInitialized;
@@ -365,8 +351,7 @@ public:
*/
LLIODataSocket(
U16 suggested_port,
- U16 start_discovery_port,
- apr_pool_t* pool);
+ U16 start_discovery_port);
virtual ~LLIODataSocket();
protected:
diff --git a/linden/indra/llmessage/llmail.cpp b/linden/indra/llmessage/llmail.cpp
index d52ff6c..8850e29 100644
--- a/linden/indra/llmessage/llmail.cpp
+++ b/linden/indra/llmessage/llmail.cpp
@@ -56,6 +56,7 @@
#include "llstring.h"
#include "lluuid.h"
#include "net.h"
+#include "aiaprpool.h"
//
// constants
@@ -63,7 +64,7 @@
const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE = 4096;
static bool gMailEnabled = true;
-static apr_pool_t* gMailPool;
+static AIAPRPool gMailPool;
static apr_sockaddr_t* gSockAddr;
static apr_socket_t* gMailSocket;
@@ -88,7 +89,7 @@ bool connect_smtp()
gSockAddr->sa.sin.sin_family,
SOCK_STREAM,
APR_PROTO_TCP,
- gMailPool);
+ gMailPool());
if(ll_apr_warn_status(status)) return false;
status = apr_socket_connect(gMailSocket, gSockAddr);
if(ll_apr_warn_status(status))
@@ -145,19 +146,19 @@ BOOL LLMail::send(
}
// static
-void LLMail::init(const std::string& hostname, apr_pool_t* pool)
+void LLMail::init(const std::string& hostname)
{
gMailSocket = NULL;
- if(hostname.empty() || !pool)
+ if (hostname.empty())
{
- gMailPool = NULL;
gSockAddr = NULL;
+ gMailPool.destroy();
}
else
{
- gMailPool = pool;
+ gMailPool.create();
- // collect all the information into a socaddr sturcture. the
+ // Collect all the information into a sockaddr structure. the
// documentation is a bit unclear, but I either have to
// specify APR_UNSPEC or not specify any flags. I am not sure
// which option is better.
@@ -167,7 +168,7 @@ void LLMail::init(const std::string& hostname, apr_pool_t* pool)
APR_UNSPEC,
25,
APR_IPV4_ADDR_OK,
- gMailPool);
+ gMailPool());
ll_apr_warn_status(status);
}
}
diff --git a/linden/indra/llmessage/llmail.h b/linden/indra/llmessage/llmail.h
index 7effb84..7026d93 100644
--- a/linden/indra/llmessage/llmail.h
+++ b/linden/indra/llmessage/llmail.h
@@ -33,15 +33,13 @@
#ifndef LL_LLMAIL_H
#define LL_LLMAIL_H
-typedef struct apr_pool_t apr_pool_t;
-
#include "llsd.h"
class LLMail
{
public:
// if hostname is NULL, then the host is resolved as 'mail'
- static void init(const std::string& hostname, apr_pool_t* pool);
+ static void init(const std::string& hostname);
// Allow all email transmission to be disabled/enabled.
static void enable(bool mail_enabled);
diff --git a/linden/indra/llmessage/llpumpio.cpp b/linden/indra/llmessage/llpumpio.cpp
index 8ef2b16..6f10c6b 100644
--- a/linden/indra/llmessage/llpumpio.cpp
+++ b/linden/indra/llmessage/llpumpio.cpp
@@ -43,6 +43,7 @@
#include "llmemtype.h"
#include "llstl.h"
#include "llstat.h"
+#include "llthread.h"
#include "llfasttimer.h"
// These should not be enabled in production, but they can be
@@ -169,14 +170,12 @@ struct ll_delete_apr_pollset_fd_client_data
/**
* LLPumpIO
*/
-LLPumpIO::LLPumpIO(apr_pool_t* pool) :
+LLPumpIO::LLPumpIO(void) :
mState(LLPumpIO::NORMAL),
mRebuildPollset(false),
mPollset(NULL),
mPollsetClientID(0),
mNextLock(0),
- mPool(NULL),
- mCurrentPool(NULL),
mCurrentPoolReallocCount(0),
mChainsMutex(NULL),
mCallbackMutex(NULL),
@@ -185,21 +184,24 @@ LLPumpIO::LLPumpIO(apr_pool_t* pool) :
mCurrentChain = mRunningChains.end();
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
- initialize(pool);
+ initialize();
}
LLPumpIO::~LLPumpIO()
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
- cleanup();
-}
-
-bool LLPumpIO::prime(apr_pool_t* pool)
-{
- LLMemType m1(LLMemType::MTYPE_IO_PUMP);
- cleanup();
- initialize(pool);
- return ((pool == NULL) ? false : true);
+#if LL_THREADS_APR
+ if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
+ if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
+#endif
+ mChainsMutex = NULL;
+ mCallbackMutex = NULL;
+ if(mPollset)
+ {
+// lldebugs << "cleaning up pollset" << llendl;
+ apr_pollset_destroy(mPollset);
+ mPollset = NULL;
+ }
}
bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
@@ -359,8 +361,7 @@ bool LLPumpIO::setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll)
{
// each fd needs a pool to work with, so if one was
// not specified, use this pool.
- // *FIX: Should it always be this pool?
- value.second.p = mPool;
+ value.second.p = (*mCurrentChain).mDescriptorsPool->operator()();
}
value.second.client_data = new S32(++mPollsetClientID);
(*mCurrentChain).mDescriptors.push_back(value);
@@ -827,39 +828,15 @@ void LLPumpIO::control(LLPumpIO::EControl op)
}
}
-void LLPumpIO::initialize(apr_pool_t* pool)
+void LLPumpIO::initialize(void)
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
- if(!pool) return;
+ mPool.create();
#if LL_THREADS_APR
// SJB: Windows defaults to NESTED and OSX defaults to UNNESTED, so use UNNESTED explicitly.
- apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_UNNESTED, pool);
- apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, pool);
-#endif
- mPool = pool;
-}
-
-void LLPumpIO::cleanup()
-{
- LLMemType m1(LLMemType::MTYPE_IO_PUMP);
-#if LL_THREADS_APR
- if(mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
- if(mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
+ apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
+ apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
#endif
- mChainsMutex = NULL;
- mCallbackMutex = NULL;
- if(mPollset)
- {
-// lldebugs << "cleaning up pollset" << llendl;
- apr_pollset_destroy(mPollset);
- mPollset = NULL;
- }
- if(mCurrentPool)
- {
- apr_pool_destroy(mCurrentPool);
- mCurrentPool = NULL;
- }
- mPool = NULL;
}
void LLPumpIO::rebuildPollset()
@@ -887,21 +864,19 @@ void LLPumpIO::rebuildPollset()
if(mCurrentPool
&& (0 == (++mCurrentPoolReallocCount % POLLSET_POOL_RECYCLE_COUNT)))
{
- apr_pool_destroy(mCurrentPool);
- mCurrentPool = NULL;
+ mCurrentPool.destroy();
mCurrentPoolReallocCount = 0;
}
if(!mCurrentPool)
{
- apr_status_t status = apr_pool_create(&mCurrentPool, mPool);
- (void)ll_apr_warn_status(status);
+ mCurrentPool.create(mPool);
}
// add all of the file descriptors
run_it = mRunningChains.begin();
LLChainInfo::conditionals_t::iterator fd_it;
LLChainInfo::conditionals_t::iterator fd_end;
- apr_pollset_create(&mPollset, size, mCurrentPool, 0);
+ apr_pollset_create(&mPollset, size, mCurrentPool(), 0);
for(; run_it != run_end; ++run_it)
{
fd_it = (*run_it).mDescriptors.begin();
@@ -1159,7 +1134,8 @@ bool LLPumpIO::handleChainError(
LLPumpIO::LLChainInfo::LLChainInfo() :
mInit(false),
mLock(0),
- mEOS(false)
+ mEOS(false),
+ mDescriptorsPool(new AIAPRPool(LLThread::tldata().mRootPool))
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS);
diff --git a/linden/indra/llmessage/llpumpio.h b/linden/indra/llmessage/llpumpio.h
index fc0bfab..2666be0 100644
--- a/linden/indra/llmessage/llpumpio.h
+++ b/linden/indra/llmessage/llpumpio.h
@@ -36,11 +36,12 @@
#define LL_LLPUMPIO_H
#include
+#include
#if LL_LINUX // needed for PATH_MAX in APR.
#include
#endif
-#include "apr_pools.h"
+#include "aiaprpool.h"
#include "llbuffer.h"
#include "llframetimer.h"
#include "lliopipe.h"
@@ -64,9 +65,8 @@ extern const F32 NEVER_CHAIN_EXPIRY_SECS;
* pump()
on a thread used for IO and call
* respond()
on a thread that is expected to do higher
* level processing. You can call almost any other method from any
- * thread - see notes for each method for details. In order for the
- * threading abstraction to work, you need to call prime()
- * with a valid apr pool.
+ * thread - see notes for each method for details.
+ *
* A pump instance manages much of the state for the pipe, including
* the list of pipes in the chain, the channel for each element in the
* chain, the buffer, and if any pipe has marked the stream or process
@@ -85,7 +85,7 @@ public:
/**
* @brief Constructor.
*/
- LLPumpIO(apr_pool_t* pool);
+ LLPumpIO(void);
/**
* @brief Destructor.
@@ -93,17 +93,6 @@ public:
~LLPumpIO();
/**
- * @brief Prepare this pump for usage.
- *
- * If you fail to call this method prior to use, the pump will
- * try to work, but will not come with any thread locking
- * mechanisms.
- * @param pool The apr pool to use.
- * @return Returns true if the pump is primed.
- */
- bool prime(apr_pool_t* pool);
-
- /**
* @brief Typedef for having a chain of pipes.
*/
typedef std::vector chain_t;
@@ -374,6 +363,7 @@ protected:
typedef std::pair pipe_conditional_t;
typedef std::vector conditionals_t;
conditionals_t mDescriptors;
+ boost::shared_ptr mDescriptorsPool;
};
// All the running chains & info
@@ -392,9 +382,9 @@ protected:
callbacks_t mPendingCallbacks;
callbacks_t mCallbacks;
- // memory allocator for pollsets & mutexes.
- apr_pool_t* mPool;
- apr_pool_t* mCurrentPool;
+ // Memory pool for pollsets & mutexes.
+ AIAPRPool mPool;
+ AIAPRPool mCurrentPool;
S32 mCurrentPoolReallocCount;
#if LL_THREADS_APR
@@ -406,8 +396,7 @@ protected:
#endif
protected:
- void initialize(apr_pool_t* pool);
- void cleanup();
+ void initialize();
/**
* @brief Given the internal state of the chains, rebuild the pollset
diff --git a/linden/indra/llmessage/llurlrequest.cpp b/linden/indra/llmessage/llurlrequest.cpp
index 46e976f..87f0116 100644
--- a/linden/indra/llmessage/llurlrequest.cpp
+++ b/linden/indra/llmessage/llurlrequest.cpp
@@ -45,6 +45,7 @@
#include "llstring.h"
#include "apr_env.h"
#include "llapr.h"
+#include "llscopedvolatileaprpool.h"
static const U32 HTTP_STATUS_PIPE_ERROR = 499;
/**
@@ -161,27 +162,31 @@ void LLURLRequest::setCallback(LLURLRequestComplete* callback)
// is called with use_proxy = FALSE
void LLURLRequest::useProxy(bool use_proxy)
{
- static char *env_proxy;
+ static std::string env_proxy;
- if (use_proxy && (env_proxy == NULL))
+ if (use_proxy && env_proxy.empty())
{
- apr_status_t status;
- LLAPRPool pool;
- status = apr_env_get(&env_proxy, "ALL_PROXY", pool.getAPRPool());
+ char* env_proxy_str;
+ LLScopedVolatileAPRPool scoped_pool;
+ apr_status_t status = apr_env_get(&env_proxy_str, "ALL_PROXY", scoped_pool);
if (status != APR_SUCCESS)
{
- status = apr_env_get(&env_proxy, "http_proxy", pool.getAPRPool());
+ status = apr_env_get(&env_proxy_str, "http_proxy", scoped_pool);
}
if (status != APR_SUCCESS)
{
- use_proxy = FALSE;
+ use_proxy = false;
}
+ else
+ {
+ // env_proxy_str is stored in the scoped_pool, so we have to make a copy.
+ env_proxy = env_proxy_str;
+ }
}
+ lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = \"" << env_proxy << "\"" << llendl;
- lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << llendl;
-
- if (env_proxy && use_proxy)
+ if (use_proxy)
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy);
}
diff --git a/linden/indra/llmessage/message.cpp b/linden/indra/llmessage/message.cpp
index 7e8aff1..53036bc 100644
--- a/linden/indra/llmessage/message.cpp
+++ b/linden/indra/llmessage/message.cpp
@@ -103,8 +103,10 @@ std::string get_shared_secret();
class LLMessagePollInfo
{
public:
+ LLMessagePollInfo(void) : mPool(LLThread::tldata().mRootPool) { }
apr_socket_t *mAPRSocketp;
apr_pollfd_t mPollFD;
+ AIAPRPool mPool;
};
namespace
@@ -291,20 +293,13 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port,
}
// LL_DEBUGS("Messaging") << << "*** port: " << mPort << llendl;
- //
- // Create the data structure that we can poll on
- //
- if (!gAPRPoolp)
- {
- LL_ERRS("Messaging") << "No APR pool before message system initialization!" << llendl;
- ll_init_apr();
- }
+ mPollInfop = new LLMessagePollInfo;
+
apr_socket_t *aprSocketp = NULL;
- apr_os_sock_put(&aprSocketp, (apr_os_sock_t*)&mSocket, gAPRPoolp);
+ apr_os_sock_put(&aprSocketp, (apr_os_sock_t*)&mSocket, mPollInfop->mPool());
- mPollInfop = new LLMessagePollInfo;
mPollInfop->mAPRSocketp = aprSocketp;
- mPollInfop->mPollFD.p = gAPRPoolp;
+ mPollInfop->mPollFD.p = mPollInfop->mPool();
mPollInfop->mPollFD.desc_type = APR_POLL_SOCKET;
mPollInfop->mPollFD.reqevents = APR_POLLIN;
mPollInfop->mPollFD.rtnevents = 0;
diff --git a/linden/indra/llplugin/llplugininstance.cpp b/linden/indra/llplugin/llplugininstance.cpp
index b822b9e..3a1395c 100755
--- a/linden/indra/llplugin/llplugininstance.cpp
+++ b/linden/indra/llplugin/llplugininstance.cpp
@@ -36,8 +36,7 @@
#include "linden_common.h"
#include "llplugininstance.h"
-
-#include "llapr.h"
+#include "aiaprpool.h"
/** Virtual destructor. */
LLPluginInstanceMessageListener::~LLPluginInstanceMessageListener()
@@ -86,7 +85,7 @@ int LLPluginInstance::load(std::string &plugin_file)
int result = apr_dso_load(&mDSOHandle,
plugin_file.c_str(),
- gAPRPoolp);
+ AIAPRRootPool::get()());
if(result != APR_SUCCESS)
{
char buf[1024];
diff --git a/linden/indra/llplugin/llpluginmessagepipe.cpp b/linden/indra/llplugin/llpluginmessagepipe.cpp
index 8168b32..ebac3c5 100755
--- a/linden/indra/llplugin/llpluginmessagepipe.cpp
+++ b/linden/indra/llplugin/llpluginmessagepipe.cpp
@@ -99,8 +99,6 @@ void LLPluginMessagePipeOwner::killMessagePipe(void)
}
LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket):
- mInputMutex(gAPRPoolp),
- mOutputMutex(gAPRPoolp),
mOwner(owner),
mSocket(socket)
{
diff --git a/linden/indra/llplugin/llpluginprocesschild.cpp b/linden/indra/llplugin/llpluginprocesschild.cpp
index 8dbf2b3..d223823 100755
--- a/linden/indra/llplugin/llpluginprocesschild.cpp
+++ b/linden/indra/llplugin/llpluginprocesschild.cpp
@@ -47,7 +47,7 @@ LLPluginProcessChild::LLPluginProcessChild()
{
mState = STATE_UNINITIALIZED;
mInstance = NULL;
- mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ mSocket = LLSocket::create(LLSocket::STREAM_TCP);
mSleepTime = PLUGIN_IDLE_SECONDS; // default: send idle messages at 100Hz
mCPUElapsed = 0.0f;
mBlockingRequest = false;
diff --git a/linden/indra/llplugin/llpluginprocessparent.cpp b/linden/indra/llplugin/llpluginprocessparent.cpp
index 8fd18ef..2cb6b28 100755
--- a/linden/indra/llplugin/llpluginprocessparent.cpp
+++ b/linden/indra/llplugin/llpluginprocessparent.cpp
@@ -49,6 +49,7 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
bool LLPluginProcessParent::sUseReadThread = false;
apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
+AIAPRPool LLPluginProcessParent::sPollSetPool;
bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
LLMutex *LLPluginProcessParent::sInstancesMutex;
std::list LLPluginProcessParent::sInstances;
@@ -59,7 +60,7 @@ class LLPluginProcessParentPollThread: public LLThread
{
public:
LLPluginProcessParentPollThread() :
- LLThread("LLPluginProcessParentPollThread", gAPRPoolp)
+ LLThread("LLPluginProcessParentPollThread")
{
}
protected:
@@ -84,12 +85,11 @@ protected:
};
-LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
- mIncomingQueueMutex(gAPRPoolp)
+LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
{
if(!sInstancesMutex)
{
- sInstancesMutex = new LLMutex(gAPRPoolp);
+ sInstancesMutex = new LLMutex;
}
mOwner = owner;
@@ -102,6 +102,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
mBlocked = false;
mPolledInput = false;
mPollFD.client_data = NULL;
+ mPollFDPool.create();
mPluginLaunchTimeout = 60.0f;
mPluginLockupTimeout = 15.0f;
@@ -177,44 +178,28 @@ void LLPluginProcessParent::init(const std::string &launcher_filename, const std
bool LLPluginProcessParent::accept()
{
bool result = false;
-
apr_status_t status = APR_EGENERAL;
- apr_socket_t *new_socket = NULL;
-
- status = apr_socket_accept(
- &new_socket,
- mListenSocket->getSocket(),
- gAPRPoolp);
+ mSocket = LLSocket::create(status, mListenSocket);
if(status == APR_SUCCESS)
{
// llinfos << "SUCCESS" << llendl;
// Success. Create a message pipe on the new socket
-
- // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor!
- apr_pool_t* new_pool = NULL;
- status = apr_pool_create(&new_pool, gAPRPoolp);
-
- mSocket = LLSocket::create(new_socket, new_pool);
new LLPluginMessagePipe(this, mSocket);
result = true;
}
- else if(APR_STATUS_IS_EAGAIN(status))
- {
-// llinfos << "EAGAIN" << llendl;
-
- // No incoming connections. This is not an error.
- status = APR_SUCCESS;
- }
else
{
-// llinfos << "Error:" << llendl;
- ll_apr_warn_status(status);
-
- // Some other error.
- errorState();
+ mSocket.reset();
+ // EAGAIN means "No incoming connections". This is not an error.
+ if (!APR_STATUS_IS_EAGAIN(status))
+ {
+ // Some other error.
+ ll_apr_warn_status(status);
+ errorState();
+ }
}
return result;
@@ -283,7 +268,7 @@ void LLPluginProcessParent::idle(void)
apr_status_t status = APR_SUCCESS;
apr_sockaddr_t* addr = NULL;
- mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ mListenSocket = LLSocket::create(LLSocket::STREAM_TCP);
mBoundPort = 0;
// This code is based on parts of LLSocket::create() in lliosocket.cpp.
@@ -294,7 +279,7 @@ void LLPluginProcessParent::idle(void)
APR_INET,
0, // port 0 = ephemeral ("find me a port")
0,
- gAPRPoolp);
+ AIAPRRootPool::get()());
if(ll_apr_warn_status(status))
{
@@ -617,7 +602,8 @@ void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe)
if(message_pipe != NULL)
{
// Set up the apr_pollfd_t
- mPollFD.p = gAPRPoolp;
+
+ mPollFD.p = mPollFDPool();
mPollFD.desc_type = APR_POLL_SOCKET;
mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP;
mPollFD.rtnevents = 0;
@@ -664,6 +650,7 @@ void LLPluginProcessParent::updatePollset()
// delete the existing pollset.
apr_pollset_destroy(sPollSet);
sPollSet = NULL;
+ sPollSetPool.destroy();
}
std::list::iterator iter;
@@ -686,12 +673,14 @@ void LLPluginProcessParent::updatePollset()
{
#ifdef APR_POLLSET_NOCOPY
// The pollset doesn't exist yet. Create it now.
- apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY);
+ sPollSetPool.create();
+ apr_status_t status = apr_pollset_create(&sPollSet, count, sPollSetPool(), APR_POLLSET_NOCOPY);
if(status != APR_SUCCESS)
{
#endif // APR_POLLSET_NOCOPY
LL_WARNS("PluginPoll") << "Couldn't create pollset. Falling back to non-pollset mode." << LL_ENDL;
sPollSet = NULL;
+ sPollSetPool.destroy();
#ifdef APR_POLLSET_NOCOPY
}
else
diff --git a/linden/indra/llplugin/llpluginprocessparent.h b/linden/indra/llplugin/llpluginprocessparent.h
index 95f5f70..bba3643 100755
--- a/linden/indra/llplugin/llpluginprocessparent.h
+++ b/linden/indra/llplugin/llpluginprocessparent.h
@@ -186,7 +186,9 @@ private:
static bool sUseReadThread;
apr_pollfd_t mPollFD;
+ AIAPRPool mPollFDPool;
static apr_pollset_t *sPollSet;
+ static AIAPRPool sPollSetPool;
static bool sPollsetNeedsRebuild;
static LLMutex *sInstancesMutex;
static std::list sInstances;
diff --git a/linden/indra/llplugin/llpluginsharedmemory.cpp b/linden/indra/llplugin/llpluginsharedmemory.cpp
index e8a411a..883d7b6 100755
--- a/linden/indra/llplugin/llpluginsharedmemory.cpp
+++ b/linden/indra/llplugin/llpluginsharedmemory.cpp
@@ -201,7 +201,8 @@ bool LLPluginSharedMemory::create(size_t size)
mName += createName();
mSize = size;
- apr_status_t status = apr_shm_create( &(mImpl->mAprSharedMemory), mSize, mName.c_str(), gAPRPoolp );
+ mPool.create();
+ apr_status_t status = apr_shm_create( &(mImpl->mAprSharedMemory), mSize, mName.c_str(), mPool());
if(ll_apr_warn_status(status))
{
@@ -224,7 +225,7 @@ bool LLPluginSharedMemory::destroy(void)
}
mImpl->mAprSharedMemory = NULL;
}
-
+ mPool.destroy();
return true;
}
@@ -233,7 +234,8 @@ bool LLPluginSharedMemory::attach(const std::string &name, size_t size)
mName = name;
mSize = size;
- apr_status_t status = apr_shm_attach( &(mImpl->mAprSharedMemory), mName.c_str(), gAPRPoolp );
+ mPool.create();
+ apr_status_t status = apr_shm_attach( &(mImpl->mAprSharedMemory), mName.c_str(), mPool() );
if(ll_apr_warn_status(status))
{
@@ -255,6 +257,7 @@ bool LLPluginSharedMemory::detach(void)
}
mImpl->mAprSharedMemory = NULL;
}
+ mPool.destroy();
return true;
}
diff --git a/linden/indra/llplugin/llpluginsharedmemory.h b/linden/indra/llplugin/llpluginsharedmemory.h
index 081d311..669a3e4 100755
--- a/linden/indra/llplugin/llpluginsharedmemory.h
+++ b/linden/indra/llplugin/llpluginsharedmemory.h
@@ -35,6 +35,8 @@
#ifndef LL_LLPLUGINSHAREDMEMORY_H
#define LL_LLPLUGINSHAREDMEMORY_H
+#include "aiaprpool.h"
+
class LLPluginSharedMemoryPlatformImpl;
/**
@@ -115,6 +117,7 @@ private:
bool close(void);
bool unlink(void);
+ AIAPRPool mPool;
std::string mName;
size_t mSize;
void *mMappedAddress;
diff --git a/linden/indra/llplugin/slplugin/slplugin.cpp b/linden/indra/llplugin/slplugin/slplugin.cpp
index 64c087b..cca8ead 100755
--- a/linden/indra/llplugin/slplugin/slplugin.cpp
+++ b/linden/indra/llplugin/slplugin/slplugin.cpp
@@ -183,8 +183,6 @@ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL
int main(int argc, char **argv)
#endif
{
- ll_init_apr();
-
// Set up llerror logging
{
LLError::initForApplication(".");
@@ -400,8 +398,6 @@ int main(int argc, char **argv)
delete plugin;
- ll_cleanup_apr();
-
return 0;
}
diff --git a/linden/indra/llvfs/llvfs.cpp b/linden/indra/llvfs/llvfs.cpp
index dea8c9c..654dfa1 100644
--- a/linden/indra/llvfs/llvfs.cpp
+++ b/linden/indra/llvfs/llvfs.cpp
@@ -237,7 +237,7 @@ const S32 LLVFSFileBlock::SERIAL_SIZE = 34;
LLVFS::LLVFS(const std::string& index_filename, const std::string& data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)
: mRemoveAfterCrash(remove_after_crash)
{
- mDataMutex = new LLMutex(0);
+ mDataMutex = new LLMutex;
S32 i;
for (i = 0; i < VFSLOCK_COUNT; i++)
diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp
index c4c4181..cc3836e 100644
--- a/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp
+++ b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp
@@ -58,7 +58,7 @@ extern "C" {
#include
#include // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
-#include "apr_pools.h"
+#include "aiaprpool.h"
#include "apr_dso.h"
}
@@ -74,7 +74,7 @@ extern "C" {
#undef LL_PA_SYM
static bool sSymsGrabbed = false;
-static apr_pool_t *sSymPADSOMemoryPool = NULL;
+static AIAPRPool sSymPADSOMemoryPool;
static apr_dso_handle_t *sSymPADSOHandleG = NULL;
bool grab_pa_syms(std::string pulse_dso_name)
@@ -93,11 +93,11 @@ bool grab_pa_syms(std::string pulse_dso_name)
#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0)
//attempt to load the shared library
- apr_pool_create(&sSymPADSOMemoryPool, NULL);
+ sSymPADSOMemoryPool.create();
if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle,
pulse_dso_name.c_str(),
- sSymPADSOMemoryPool) ))
+ sSymPADSOMemoryPool()) ))
{
INFOMSG("Found DSO: %s", pulse_dso_name.c_str());
@@ -140,11 +140,7 @@ void ungrab_pa_syms()
sSymPADSOHandleG = NULL;
}
- if ( sSymPADSOMemoryPool )
- {
- apr_pool_destroy(sSymPADSOMemoryPool);
- sSymPADSOMemoryPool = NULL;
- }
+ sSymPADSOMemoryPool.destroy();
// NULL-out all of the symbols we'd grabbed
#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0)
diff --git a/linden/indra/newview/llappviewer.cpp b/linden/indra/newview/llappviewer.cpp
index 8aea221..aaae4d2 100644
--- a/linden/indra/newview/llappviewer.cpp
+++ b/linden/indra/newview/llappviewer.cpp
@@ -920,7 +920,7 @@ bool LLAppViewer::mainLoop()
//-------------------------------------------
// Create IO Pump to use for HTTP Requests.
- gServicePump = new LLPumpIO(gAPRPoolp);
+ gServicePump = new LLPumpIO;
LLHTTPClient::setPump(*gServicePump);
LLCurl::setCAFile(gDirUtilp->getCAFile());
diff --git a/linden/indra/newview/llappviewerlinux.cpp b/linden/indra/newview/llappviewerlinux.cpp
index 307f925..88ddf7e 100644
--- a/linden/indra/newview/llappviewerlinux.cpp
+++ b/linden/indra/newview/llappviewerlinux.cpp
@@ -129,6 +129,7 @@ int main( int argc, char **argv )
}
delete viewer_app_ptr;
viewer_app_ptr = NULL;
+
return 0;
}
diff --git a/linden/indra/newview/llappviewermacosx.cpp b/linden/indra/newview/llappviewermacosx.cpp
index 91de066..d81b6e3 100644
--- a/linden/indra/newview/llappviewermacosx.cpp
+++ b/linden/indra/newview/llappviewermacosx.cpp
@@ -119,6 +119,7 @@ int main( int argc, char **argv )
}
delete viewer_app_ptr;
viewer_app_ptr = NULL;
+
return 0;
}
diff --git a/linden/indra/newview/lltexturecache.cpp b/linden/indra/newview/lltexturecache.cpp
index a1a9a39..2b032a5 100644
--- a/linden/indra/newview/lltexturecache.cpp
+++ b/linden/indra/newview/lltexturecache.cpp
@@ -736,9 +736,6 @@ void LLTextureCacheWorker::endWork(S32 param, bool aborted)
LLTextureCache::LLTextureCache(bool threaded)
: LLWorkerThread("TextureCache", threaded),
- mWorkersMutex(NULL),
- mHeaderMutex(NULL),
- mListMutex(NULL),
mHeaderAPRFile(NULL),
mReadOnly(FALSE),
mTexturesSizeTotal(0),
@@ -1541,7 +1538,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort)
}
}
- unlockWorkers();
+ unlockWorkers();
if (delete_worker) worker->scheduleDelete();
diff --git a/linden/indra/newview/lltexturecache.h b/linden/indra/newview/lltexturecache.h
index c859b9a..56b4c4f 100644
--- a/linden/indra/newview/lltexturecache.h
+++ b/linden/indra/newview/lltexturecache.h
@@ -139,9 +139,6 @@ protected:
std::string getTextureFileName(const LLUUID& id);
void addCompleted(Responder* responder, bool success);
-protected:
- //void setFileAPRPool(apr_pool_t* pool) { mFileAPRPool = pool ; }
-
private:
void setDirNames(ELLPath location);
void readHeaderCache();
diff --git a/linden/indra/newview/lltexturefetch.cpp b/linden/indra/newview/lltexturefetch.cpp
index f93a574..072af25 100644
--- a/linden/indra/newview/lltexturefetch.cpp
+++ b/linden/indra/newview/lltexturefetch.cpp
@@ -428,7 +428,6 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mRetryAttempt(0),
mActiveCount(0),
mGetStatus(0),
- mWorkMutex(NULL),
mFirstPacket(0),
mLastPacket(-1),
mTotalPackets(0),
@@ -1540,8 +1539,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mDebugPause(FALSE),
mPacketCount(0),
mBadPacketCount(0),
- mQueueMutex(getAPRPool()),
- mNetworkQueueMutex(getAPRPool()),
mTextureCache(cache),
mImageDecodeThread(imagedecodethread),
mTextureBandwidth(0),
diff --git a/linden/indra/newview/llviewerprecompiledheaders.h b/linden/indra/newview/llviewerprecompiledheaders.h
index 9bc6574..a0b99bf 100644
--- a/linden/indra/newview/llviewerprecompiledheaders.h
+++ b/linden/indra/newview/llviewerprecompiledheaders.h
@@ -165,7 +165,7 @@
#include "llinstantmessage.h"
#include "llinvite.h"
//#include "llloginflags.h"
-#include "llmail.h"
+//#include "llmail.h"
#include "llmessagethrottle.h"
#include "llnamevalue.h"
#include "llpacketack.h"
diff --git a/linden/indra/newview/llvoiceclient.cpp b/linden/indra/newview/llvoiceclient.cpp
index d67b9e3..7b1ed95 100644
--- a/linden/indra/newview/llvoiceclient.cpp
+++ b/linden/indra/newview/llvoiceclient.cpp
@@ -1795,7 +1795,7 @@ void LLVoiceClient::stateMachine()
if(!mSocket)
{
- mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ mSocket = LLSocket::create(LLSocket::STREAM_TCP);
}
mConnected = mSocket->blockingConnect(mDaemonHost);
diff --git a/linden/indra/newview/llwatchdog.cpp b/linden/indra/newview/llwatchdog.cpp
index 330bc8a..9af050c 100644
--- a/linden/indra/newview/llwatchdog.cpp
+++ b/linden/indra/newview/llwatchdog.cpp
@@ -184,8 +184,8 @@ void LLWatchdog::init(killer_event_callback func)
mKillerCallback = func;
if(!mSuspectsAccessMutex && !mTimer)
{
- mSuspectsAccessMutex = new LLMutex(NULL);
- mTimer = new LLWatchdogTimerThread();
+ mSuspectsAccessMutex = new LLMutex;
+ mTimer = new LLWatchdogTimerThread;
mTimer->setSleepTime(WATCHDOG_SLEEP_TIME_USEC / 1000);
mLastClockCount = LLTimer::getTotalTime();
diff --git a/linden/indra/test/lltemplatemessagebuilder_tut.cpp b/linden/indra/test/lltemplatemessagebuilder_tut.cpp
index 5b33d02..9fecc2f 100644
--- a/linden/indra/test/lltemplatemessagebuilder_tut.cpp
+++ b/linden/indra/test/lltemplatemessagebuilder_tut.cpp
@@ -35,7 +35,6 @@
#include "linden_common.h"
#include "lltut.h"
-#include "llapr.h"
#include "llmessagetemplate.h"
#include "llquaternion.h"
#include "lltemplatemessagebuilder.h"
@@ -59,7 +58,6 @@ namespace tut
static bool init = false;
if(! init)
{
- ll_init_apr();
const F32 circuit_heartbeat_interval=5;
const F32 circuit_timeout=100;
diff --git a/linden/indra/test/message_tut.cpp b/linden/indra/test/message_tut.cpp
index 3fede26..694db52 100644
--- a/linden/indra/test/message_tut.cpp
+++ b/linden/indra/test/message_tut.cpp
@@ -35,7 +35,6 @@
#include "linden_common.h"
#include "lltut.h"
-#include "llapr.h"
#include "llmessageconfig.h"
#include "llsdserialize.h"
#include "llversionserver.h"
@@ -68,7 +67,6 @@ namespace tut
static bool init = false;
if(!init)
{
- ll_init_apr();
//init_prehash_data();
init = true;
}
diff --git a/linden/indra/test/test.cpp b/linden/indra/test/test.cpp
index ba81c6e..b699f74 100644
--- a/linden/indra/test/test.cpp
+++ b/linden/indra/test/test.cpp
@@ -43,8 +43,8 @@
#include "linden_common.h"
#include "llerrorcontrol.h"
#include "lltut.h"
+#include "aiaprpool.h"
-#include "apr_pools.h"
#include "apr_getopt.h"
// the CTYPE_WORKAROUND is needed for linux dev stations that don't
@@ -248,17 +248,12 @@ int main(int argc, char **argv)
ctype_workaround();
#endif
- apr_initialize();
- apr_pool_t* pool = NULL;
- if(APR_SUCCESS != apr_pool_create(&pool, NULL))
- {
- std::cerr << "Unable to initialize pool" << std::endl;
- return 1;
- }
+ LLAPRPool pool;
+ pool.create();
apr_getopt_t* os = NULL;
- if(APR_SUCCESS != apr_getopt_init(&os, pool, argc, argv))
+ if(APR_SUCCESS != apr_getopt_init(&os, pool(), argc, argv))
{
- std::cerr << "Unable to pool" << std::endl;
+ std::cerr << "Unable to initialize the arguments for parsing by apr_getopt()." << std::endl;
return 1;
}
@@ -360,6 +355,5 @@ int main(int argc, char **argv)
s.close();
}
- apr_terminate();
return 0;
}
--
cgit v1.1
From 7bc679e616a4b8e0fafe2c1f6cac821208b649ff Mon Sep 17 00:00:00 2001
From: Aleric Inglewood
Date: Thu, 11 Nov 2010 00:06:08 +0100
Subject: IMP-701: An API to wrap objects for thread-safe access.
See http://redmine.imprudenceviewer.org/issues/701
---
linden/doc/contributions.txt | 1 +
linden/indra/llcommon/CMakeLists.txt | 1 +
linden/indra/llcommon/aiaprpool.h | 8 +-
linden/indra/llcommon/aithreadsafe.h | 463 +++++++++++++++++++++++++++++++++++
linden/indra/llcommon/llthread.h | 96 +++++++-
5 files changed, 565 insertions(+), 4 deletions(-)
create mode 100644 linden/indra/llcommon/aithreadsafe.h
diff --git a/linden/doc/contributions.txt b/linden/doc/contributions.txt
index c8dd773..0ca56be 100644
--- a/linden/doc/contributions.txt
+++ b/linden/doc/contributions.txt
@@ -92,6 +92,7 @@ Aleric Inglewood
IMP-670
IMP-688
IMP-692
+ IMP-701
IMP-712
Alissa Sabre
VWR-81
diff --git a/linden/indra/llcommon/CMakeLists.txt b/linden/indra/llcommon/CMakeLists.txt
index a05ee6f..ee80ec3 100644
--- a/linden/indra/llcommon/CMakeLists.txt
+++ b/linden/indra/llcommon/CMakeLists.txt
@@ -76,6 +76,7 @@ set(llcommon_HEADER_FILES
CMakeLists.txt
aiaprpool.h
+ aithreadsafe.h
bitpack.h
ctype_workaround.h
doublelinkedlist.h
diff --git a/linden/indra/llcommon/aiaprpool.h b/linden/indra/llcommon/aiaprpool.h
index f6d7ffa..72e9ddb 100644
--- a/linden/indra/llcommon/aiaprpool.h
+++ b/linden/indra/llcommon/aiaprpool.h
@@ -157,17 +157,19 @@ public:
/**
* @brief Root memory pool (allocates memory from the operating system).
*
- * This class should only be used by AIThreadLocalData (and LLMutexRootPool
- * when APR_HAS_THREADS isn't defined).
+ * This class should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase
+ * (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
*/
class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool
{
private:
friend class AIThreadLocalData;
+ friend class AIThreadSafeSimpleDCRootPool_pbase;
#if !APR_HAS_THREADS
friend class LLMutexRootPool;
#endif
- //! Construct a root memory pool. Should only be used by AIThreadLocalData.
+ //! Construct a root memory pool.
+ // Should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase.
AIAPRRootPool(void);
~AIAPRRootPool();
diff --git a/linden/indra/llcommon/aithreadsafe.h b/linden/indra/llcommon/aithreadsafe.h
new file mode 100644
index 0000000..e1b93ba
--- /dev/null
+++ b/linden/indra/llcommon/aithreadsafe.h
@@ -0,0 +1,463 @@
+/**
+ * @file aithreadsafe.h
+ * @brief Implementation of AIThreadSafe, AIReadAccessConst, AIReadAccess and AIWriteAccess.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 31/03/2010
+ * Initial version, written by Aleric Inglewood @ SL
+ */
+
+#ifndef AITHREADSAFE_H
+#define AITHREADSAFE_H
+
+#include
+
+#include "llthread.h"
+#include "llerror.h"
+
+template struct AIReadAccessConst;
+template struct AIReadAccess;
+template struct AIWriteAccess;
+template struct AIAccess;
+
+template
+class AIThreadSafeBits
+{
+private:
+ // AIThreadSafe is a wrapper around an instance of T.
+ // Because T might not have a default constructor, it is constructed
+ // 'in place', with placement new, in the memory reserved here.
+ //
+ // Make sure that the memory that T will be placed in is properly
+ // aligned by using an array of long's.
+ long mMemory[(sizeof(T) + sizeof(long) - 1) / sizeof(long)];
+
+public:
+ // The wrapped objects are constructed in-place with placement new *outside*
+ // of this object (by AITHREADSAFE macro(s) or derived classes).
+ // However, we are responsible for the destruction of the wrapped object.
+ ~AIThreadSafeBits() { ptr()->~T(); }
+
+ // Only for use by AITHREADSAFE, see below.
+ void* memory() const { return const_cast(&mMemory[0]); }
+
+protected:
+ // Accessors.
+ T const* ptr() const { return reinterpret_cast(mMemory); }
+ T* ptr() { return reinterpret_cast(mMemory); }
+};
+
+/**
+ * @brief A wrapper class for objects that need to be accessed by more than one thread, allowing concurrent readers.
+ *
+ * Use AITHREADSAFE to define instances of any type, and use AIReadAccessConst,
+ * AIReadAccess and AIWriteAccess to get access to the instance.
+ *
+ * For example,
+ *
+ *
+ * class Foo { public: Foo(int, int); };
+ *
+ * AITHREADSAFE(Foo, foo, (2, 3));
+ *
+ * AIReadAccess foo_r(foo);
+ * // Use foo_r-> for read access.
+ *
+ * AIWriteAccess foo_w(foo);
+ * // Use foo_w-> for write access.
+ *
+ *
+ * If foo
is constant, you have to use AIReadAccessConst
.
+ *
+ * It is possible to pass access objects to a function that
+ * downgrades the access, for example:
+ *
+ *
+ * void readfunc(AIReadAccess const& access);
+ *
+ * AIWriteAccess foo_w(foo);
+ * readfunc(foo_w); // readfunc will perform read access to foo_w.
+ *
+ *
+ * If AIReadAccess
is non-const, you can upgrade the access by creating
+ * an AIWriteAccess
object from it. For example:
+ *
+ *
+ * AIWriteAccess foo_w(foo_r);
+ *
+ *
+ * This API is Robust(tm). If you try anything that could result in problems,
+ * it simply won't compile. The only mistake you can still easily make is
+ * to obtain write access to an object when it is not needed, or to unlock
+ * an object in between accesses while the state of the object should be
+ * preserved. For example:
+ *
+ *
+ * // This resets foo to point to the first file and then returns that.
+ * std::string filename = AIWriteAccess(foo)->get_first_filename();
+ *
+ * // WRONG! The state between calling get_first_filename and get_next_filename should be preserved!
+ *
+ * AIWriteAccess foo_w(foo); // Wrong. The code below only needs read-access.
+ * while (!filename.empty())
+ * {
+ * something(filename);
+ * filename = foo_w->next_filename();
+ * }
+ *
+ *
+ * Correct would be
+ *
+ *
+ * AIReadAccess foo_r(foo);
+ * std::string filename = AIWriteAccess(foo_r)->get_first_filename();
+ * while (!filename.empty())
+ * {
+ * something(filename);
+ * filename = foo_r->next_filename();
+ * }
+ *
+ *
+ */
+template
+class AIThreadSafe : public AIThreadSafeBits
+{
+protected:
+ // Only these may access the object (through ptr()).
+ friend struct AIReadAccessConst;
+ friend struct AIReadAccess;
+ friend struct AIWriteAccess;
+
+ // Locking control.
+ AIRWLock mRWLock;
+
+ // For use by AIThreadSafeDC
+ AIThreadSafe(void) { }
+ AIThreadSafe(AIAPRPool& parent) : mRWLock(parent) { }
+
+public:
+ // Only for use by AITHREADSAFE, see below.
+ AIThreadSafe(T* object) { llassert(object == AIThreadSafeBits::ptr()); }
+};
+
+/**
+ * @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafe, using an arbitrary constructor.
+ *
+ * For example, instead of doing
+ *
+ *
+ * Foo foo(x, y);
+ * static Bar bar;
+ *
+ *
+ * One can instantiate a thread-safe instance with
+ *
+ *
+ * AITHREADSAFE(Foo, foo, (x, y));
+ * static AITHREADSAFE(Bar, bar, );
+ *
+ *
+ * Note: This macro does not allow to allocate such object on the heap.
+ * If that is needed, have a look at AIThreadSafeDC.
+ */
+#define AITHREADSAFE(type, var, paramlist) AIThreadSafe var(new (var.memory()) type paramlist)
+
+/**
+ * @brief A wrapper class for objects that need to be accessed by more than one thread.
+ *
+ * This class is the same as an AIThreadSafe wrapper, except that it can only
+ * be used for default constructed objects.
+ *
+ * For example, instead of
+ *
+ *
+ * Foo foo;
+ *
+ *
+ * One would use
+ *
+ *
+ * AIThreadSafeDC foo;
+ *
+ *
+ * The advantage over AITHREADSAFE is that this object can be allocated with
+ * new on the heap. For example:
+ *
+ *
+ * AIThreadSafeDC* ptr = new AIThreadSafeDC;
+ *
+ *
+ * which is not possible with AITHREADSAFE.
+ */
+template
+class AIThreadSafeDC : public AIThreadSafe
+{
+public:
+ // Construct a wrapper around a default constructed object.
+ AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; }
+};
+
+/**
+ * @brief Read lock object and provide read access.
+ */
+template
+struct AIReadAccessConst
+{
+ //! Internal enum for the lock-type of the AI*Access object.
+ enum state_type
+ {
+ readlocked, //!< A AIReadAccessConst or AIReadAccess.
+ read2writelocked, //!< A AIWriteAccess constructed from a AIReadAccess.
+ writelocked, //!< A AIWriteAccess constructed from a AIThreadSafe.
+ write2writelocked //!< A AIWriteAccess constructed from (the AIReadAccess base class of) a AIWriteAccess.
+ };
+
+ //! Construct a AIReadAccessConst from a constant AIThreadSafe.
+ AIReadAccessConst(AIThreadSafe const& wrapper)
+ : mWrapper(const_cast&>(wrapper)),
+ mState(readlocked)
+ {
+ mWrapper.mRWLock.rdlock();
+ }
+
+ //! Destruct the AI*Access object.
+ // These should never be dynamically allocated, so there is no need to make this virtual.
+ ~AIReadAccessConst()
+ {
+ if (mState == readlocked)
+ mWrapper.mRWLock.rdunlock();
+ else if (mState == writelocked)
+ mWrapper.mRWLock.wrunlock();
+ else if (mState == read2writelocked)
+ mWrapper.mRWLock.wr2rdlock();
+ }
+
+ //! Access the underlaying object for read access.
+ T const* operator->() const { return mWrapper.ptr(); }
+
+ //! Access the underlaying object for read access.
+ T const& operator*() const { return *mWrapper.ptr(); }
+
+protected:
+ //! Constructor used by AIReadAccess.
+ AIReadAccessConst(AIThreadSafe& wrapper, state_type state)
+ : mWrapper(wrapper), mState(state) { }
+
+ AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to.
+ state_type const mState; //!< The lock state that mWrapper is in.
+
+private:
+ // Disallow copy constructing directly.
+ AIReadAccessConst(AIReadAccessConst const&);
+};
+
+/**
+ * @brief Read lock object and provide read access, with possible promotion to write access.
+ */
+template
+struct AIReadAccess : public AIReadAccessConst
+{
+ typedef typename AIReadAccessConst::state_type state_type;
+ using AIReadAccessConst::readlocked;
+
+ //! Construct a AIReadAccess from a non-constant AIThreadSafe.
+ AIReadAccess(AIThreadSafe& wrapper) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(); }
+
+protected:
+ //! Constructor used by AIWriteAccess.
+ AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { }
+
+ friend class AIWriteAccess;
+};
+
+/**
+ * @brief Write lock object and provide read/write access.
+ */
+template
+struct AIWriteAccess : public AIReadAccess
+{
+ using AIReadAccessConst::readlocked;
+ using AIReadAccessConst::read2writelocked;
+ using AIReadAccessConst::writelocked;
+ using AIReadAccessConst::write2writelocked;
+
+ //! Construct a AIWriteAccess from a non-constant AIThreadSafe.
+ AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();}
+
+ //! Promote read access to write access.
+ explicit AIWriteAccess(AIReadAccess& access)
+ : AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked)
+ {
+ if (this->mState == read2writelocked)
+ {
+ this->mWrapper.mRWLock.rd2wrlock();
+ }
+ }
+
+ //! Access the underlaying object for (read and) write access.
+ T* operator->() const { return this->mWrapper.ptr(); }
+
+ //! Access the underlaying object for (read and) write access.
+ T& operator*() const { return *this->mWrapper.ptr(); }
+};
+
+/**
+ * @brief A wrapper class for objects that need to be accessed by more than one thread.
+ *
+ * Use AITHREADSAFESIMPLE to define instances of any type, and use AIAccess
+ * to get access to the instance.
+ *
+ * For example,
+ *
+ *
+ * class Foo { public: Foo(int, int); };
+ *
+ * AITHREADSAFESIMPLE(Foo, foo, (2, 3));
+ *
+ * AIAccess foo_w(foo);
+ * // Use foo_w-> for read and write access.
+ *
+ * See also AIThreadSafe
+ */
+template
+class AIThreadSafeSimple : public AIThreadSafeBits
+{
+protected:
+ // Only this one may access the object (through ptr()).
+ friend struct AIAccess;
+
+ // Locking control.
+ LLMutex mMutex;
+
+ // For use by AIThreadSafeSimpleDC
+ AIThreadSafeSimple(void) { }
+ AIThreadSafeSimple(AIAPRPool& parent) : mMutex(parent) { }
+
+public:
+ // Only for use by AITHREADSAFESIMPLE, see below.
+ AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits::ptr()); }
+};
+
+/**
+ * @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafeSimple, using an arbitrary constructor.
+ *
+ * For example, instead of doing
+ *
+ *
+ * Foo foo(x, y);
+ * static Bar bar;
+ *
+ *
+ * One can instantiate a thread-safe instance with
+ *
+ *
+ * AITHREADSAFESIMPLE(Foo, foo, (x, y));
+ * static AITHREADSAFESIMPLE(Bar, bar, );
+ *
+ *
+ * Note: This macro does not allow to allocate such object on the heap.
+ * If that is needed, have a look at AIThreadSafeSimpleDC.
+ */
+#define AITHREADSAFESIMPLE(type, var, paramlist) AIThreadSafeSimple var(new (var.memory()) type paramlist)
+
+/**
+ * @brief A wrapper class for objects that need to be accessed by more than one thread.
+ *
+ * This class is the same as an AIThreadSafeSimple wrapper, except that it can only
+ * be used for default constructed objects.
+ *
+ * For example, instead of
+ *
+ *
+ * Foo foo;
+ *
+ *
+ * One would use
+ *
+ *
+ * AIThreadSafeSimpleDC foo;
+ *
+ *
+ * The advantage over AITHREADSAFESIMPLE is that this object can be allocated with
+ * new on the heap. For example:
+ *
+ *
+ * AIThreadSafeSimpleDC* ptr = new AIThreadSafeSimpleDC;
+ *
+ *
+ * which is not possible with AITHREADSAFESIMPLE.
+ */
+template
+class AIThreadSafeSimpleDC : public AIThreadSafeSimple
+{
+public:
+ // Construct a wrapper around a default constructed object.
+ AIThreadSafeSimpleDC(void) { new (AIThreadSafeSimple::ptr()) T; }
+
+protected:
+ // For use by AIThreadSafeSimpleDCRootPool
+ AIThreadSafeSimpleDC(AIAPRPool& parent) : AIThreadSafeSimple(parent) { new (AIThreadSafeSimple::ptr()) T; }
+};
+
+// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
+// the root pool before constructing AIThreadSafeSimpleDC.
+class AIThreadSafeSimpleDCRootPool_pbase
+{
+protected:
+ AIAPRRootPool mRootPool;
+
+private:
+ template friend class AIThreadSafeSimpleDCRootPool;
+ AIThreadSafeSimpleDCRootPool_pbase(void) { }
+};
+
+/**
+ * @brief A wrapper class for objects that need to be accessed by more than one thread.
+ *
+ * The same as AIThreadSafeSimpleDC except that this class creates its own AIAPRRootPool
+ * for the internally used mutexes and condition, instead of using the current threads
+ * root pool. The advantage of this is that it can be used for objects that need to
+ * be accessed from the destructors of global objects (after main). The disadvantage
+ * is that it's less efficient to use your own root pool, therefore it's use should be
+ * restricted to those cases where it is absolutely necessary.
+ */
+template
+class AIThreadSafeSimpleDCRootPool : private AIThreadSafeSimpleDCRootPool_pbase, public AIThreadSafeSimpleDC
+{
+public:
+ // Construct a wrapper around a default constructed object, using memory allocated
+ // from the operating system for the internal APR objects (mutexes and conditional),
+ // as opposed to allocated from the current threads root pool.
+ AIThreadSafeSimpleDCRootPool(void) :
+ AIThreadSafeSimpleDCRootPool_pbase(),
+ AIThreadSafeSimpleDC(mRootPool) { }
+};
+
+/**
+ * @brief Write lock object and provide read/write access.
+ */
+template
+struct AIAccess
+{
+ //! Construct a AIAccess from a non-constant AIThreadSafeSimple.
+ AIAccess(AIThreadSafeSimple& wrapper) : mWrapper(wrapper) { this->mWrapper.mMutex.lock(); }
+
+ //! Access the underlaying object for (read and) write access.
+ T* operator->() const { return this->mWrapper.ptr(); }
+
+ //! Access the underlaying object for (read and) write access.
+ T& operator*() const { return *this->mWrapper.ptr(); }
+
+ ~AIAccess() { this->mWrapper.mMutex.unlock(); }
+
+protected:
+ AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to.
+
+private:
+ // Disallow copy constructing directly.
+ AIAccess(AIAccess const&);
+};
+
+#endif
diff --git a/linden/indra/llcommon/llthread.h b/linden/indra/llcommon/llthread.h
index 8708929..1e982cc 100644
--- a/linden/indra/llcommon/llthread.h
+++ b/linden/indra/llcommon/llthread.h
@@ -235,6 +235,101 @@ private:
LLMutexBase* mMutex;
};
+class AIRWLock
+{
+public:
+ AIRWLock(AIAPRPool& parent = LLThread::tldata().mRootPool) :
+ mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { }
+
+private:
+ LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access.
+ LLCondition mNoHoldersCondition; //!< Access control for mHoldersCount. Condition true when there are no more holders.
+ int mHoldersCount; //!< Number of readers or -1 if a writer locked this object.
+ // This is volatile because we read it outside the critical area of mWriterWaitingMutex, at [1].
+ // That means that other threads can change it while we are already in the (inlined) function rdlock.
+ // Without volatile, the following assembly would fail:
+ // register x = mWriterIsWaiting;
+ // /* some thread changes mWriterIsWaiting */
+ // if (x ...
+ // However, because the function is fuzzy to begin with (we don't mind that this race
+ // condition exists) it would work fine without volatile. So, basically it's just here
+ // out of principle ;). -- Aleric
+ bool volatile mWriterIsWaiting; //!< True when there is a writer waiting for write access.
+
+public:
+ void rdlock(bool high_priority = false)
+ {
+ // Give a writer a higher priority (kinda fuzzy).
+ if (mWriterIsWaiting && !high_priority) // [1] If there is a writer interested,
+ {
+ mWriterWaitingMutex.lock(); // [2] then give it precedence and wait here.
+ // If we get here then the writer got it's access; mHoldersCount == -1.
+ mWriterWaitingMutex.unlock();
+ }
+ mNoHoldersCondition.lock(); // [3] Get exclusive access to mHoldersCount.
+ while (mHoldersCount == -1) // [4]
+ {
+ mNoHoldersCondition.wait(); // [5] Wait till mHoldersCount is (or just was) 0.
+ }
+ ++mHoldersCount; // One more reader.
+ mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
+ }
+ void rdunlock(void)
+ {
+ mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
+ if (--mHoldersCount == 0) // Was this the last reader?
+ {
+ mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
+ }
+ mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
+ }
+ void wrlock(void)
+ {
+ mWriterWaitingMutex.lock(); // Block new readers, see [2],
+ mWriterIsWaiting = true; // from this moment on, see [1].
+ mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
+ while (mHoldersCount != 0) // Other readers or writers have this lock?
+ {
+ mNoHoldersCondition.wait(); // [6] Wait till mHoldersCount is (or just was) 0.
+ }
+ mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
+ mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
+ mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
+ mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
+ }
+ void wrunlock(void)
+ {
+ mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
+ mHoldersCount = 0; // We have no writer anymore.
+ mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
+ mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
+ }
+ void rd2wrlock(void)
+ {
+ mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount. Blocks new readers at [3].
+ if (--mHoldersCount > 0) // Any other reads left?
+ {
+ mWriterWaitingMutex.lock(); // Block new readers, see [2],
+ mWriterIsWaiting = true; // from this moment on, see [1].
+ while (mHoldersCount != 0) // Other readers (still) have this lock?
+ {
+ mNoHoldersCondition.wait(); // [7] Wait till mHoldersCount is (or just was) 0.
+ }
+ mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
+ mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
+ }
+ mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
+ mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
+ }
+ void wr2rdlock(void)
+ {
+ mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
+ mHoldersCount = 1; // Turn writer into a reader.
+ mNoHoldersCondition.signal(); // Tell waiting readers, see [5].
+ mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
+ }
+};
+
//============================================================================
void LLThread::lockData()
@@ -247,7 +342,6 @@ void LLThread::unlockData()
mRunCondition->unlock();
}
-
//============================================================================
// see llmemory.h for LLPointer<> definition
--
cgit v1.1