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