/** * @file llaprpool.h * @brief Implementation of LLAPRPool. * * 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 LL_LLAPRPOOL_H #define LL_LLAPRPOOL_H #ifdef LL_WINDOWS #pragma warning(push) #pragma warning(disable:4996) #include #include // Needed before including apr_portable.h #pragma warning(pop) #endif #include "apr_portable.h" #include "apr_pools.h" #include "llerror.h" #include "aithreadid.h" extern void ll_init_apr(); /** * @brief A wrapper around the APR memory pool API. * * Usage of this class should be restricted to passing it to libapr-1 function calls that need it. * */ class LL_COMMON_API LLAPRPool { protected: apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized. LLAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero. AIThreadID mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero. public: //! Construct an uninitialized (destructed) pool. LLAPRPool(void) : mPool(NULL), mOwner(AIThreadID::none) { } //! Construct a subpool from an existing pool. // This is not a copy-constructor, this class doesn't have one! LLAPRPool(LLAPRPool& parent) : mPool(NULL), mOwner(AIThreadID::none) { create(parent); } //! Destruct the memory pool (free all of it's subpools and allocated memory). ~LLAPRPool() { destroy(); } protected: // Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool. LLAPRPool(int) : mPool(NULL), mParent(NULL) { apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL); llassert_always(apr_pool_create_status == APR_SUCCESS); llassert(mPool); apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null); } public: //! Create a subpool from parent. May only be called for an uninitialized/destroyed pool. // The default parameter causes the root pool of the current thread to be used. void create(LLAPRPool& parent = *static_cast(NULL)); //! Destroy the (sub)pool, if any. void destroy(void); // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool. typedef LLAPRPool* const LLAPRPool::* const bool_type; /// Return true if the pool is initialized. operator bool_type() const { return mPool ? &LLAPRPool::mParent : 0; } // Painful, but we have to either provide access to this, or wrap // every APR function call that needs 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(mOwner.equals_current_thread()); return mPool; } // Free all memory without destructing the pool. void clear(void) { llassert(mPool); llassert(mOwner.equals_current_thread()); 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(mOwner.equals_current_thread()); return apr_palloc(mPool, size); } void* pcalloc(size_t size) { llassert(mPool); llassert(mOwner.equals_current_thread()); 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 LLAPRInitialization { public: LLAPRInitialization(void); }; /** * @brief Root memory pool (allocates memory from the operating system). * * This class should only be used by LLThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase * (and LLMutexRootPool when APR_HAS_THREADS isn't defined). */ class LL_COMMON_API LLAPRRootPool : public LLAPRInitialization, public LLAPRPool { private: friend class LLThreadLocalData; friend class AIThreadSafeSimpleDCRootPool_pbase; #if !APR_HAS_THREADS friend class LLMutexRootPool; #endif //! Construct a root memory pool. // Should only be used by LLThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase. LLAPRRootPool(void); ~LLAPRRootPool(); private: // Keep track of how many root pools exist and when the last one is destructed. static bool sCountInitialized; static apr_uint32_t volatile sCount; public: // Return a global root pool that is independent of LLThreadLocalData. // Normally you should not use this. Only use for early initialization // (before main) and deinitialization (after main). static LLAPRRootPool& get(void); #if APR_POOL_DEBUG void grab_ownership(void) { // You need a patched libapr to use this. // See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI apr_pool_owner_set(mPool); } #endif private: // Used for constructing the Special Global Root Pool (returned by LLAPRRootPool::get). // It is the same as the default constructor but omits to increment sCount. As a result, // we must be sure that at least one other LLAPRRootPool is created before termination // of the application (which is the case: we create one LLAPRRootPool per thread). LLAPRRootPool(int) : LLAPRInitialization(), LLAPRPool(0) { } }; //! Volatile memory pool // // 'Volatile' APR memory pool which normally only clears memory, // and does not destroy the pool (the same pool is reused) for // greater efficiency. However, as a safe guard the apr pool // is destructed every FULL_VOLATILE_APR_POOL uses to allow // the system memory to be allocated more efficiently and not // get scattered through RAM. // class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool { public: LLVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { } void clearVolatileAPRPool(void); bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; } bool isUnused() const { return mNumActiveRef == 0; } private: friend class LLScopedVolatileAPRPool; friend class LLAPRFile; apr_pool_t* getVolatileAPRPool(void) // The use of apr_pool_t is OK here. { if (!mPool) create(); ++mNumActiveRef; ++mNumTotalRef; return LLAPRPool::operator()(); } private: S32 mNumActiveRef; // Number of active uses of the pool. S32 mNumTotalRef; // Number of total uses of the pool since last creation. // Maximum number of references to LLVolatileAPRPool until the pool is recreated. static S32 const FULL_VOLATILE_APR_POOL = 1024; }; #endif // LLAPRPool_H