# common/include/pion/PionPoolAllocator.hpp

    00001 // -----------------------------------------------------------------------
    00002 // pion-common: a collection of common libraries used by the Pion Platform
    00003 // -----------------------------------------------------------------------
    00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
    00005 //
    00006 // Distributed under the Boost Software License, Version 1.0.
    00007 // See http://www.boost.org/LICENSE_1_0.txt
    00008 //
    00009
    00010 #ifndef __PION_PIONPOOLALLOCATOR_HEADER__
    00011 #define __PION_PIONPOOLALLOCATOR_HEADER__
    00012
    00013 #include <cstdlib>
    00014 #include <boost/array.hpp>
    00015 #include <boost/scoped_ptr.hpp>
    00016 #include <boost/static_assert.hpp>
    00017 #include <boost/noncopyable.hpp>
    00018 #include <boost/thread/mutex.hpp>
    00019 #include <boost/pool/pool.hpp>
    00020 #include <pion/PionConfig.hpp>
    00021 #include <pion/PionException.hpp>
    00022
    00023 #if defined(PION_HAVE_MALLOC_TRIM)
    00024     #include <malloc.h>
    00025 #endif
    00026
    00028 #if defined(PION_HAVE_LOCKFREE)
    00029 #ifdef _MSC_VER
    00030     #pragma warning(push)
    00031     #pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)
    00032 #endif
    00033     #include <boost/lockfree/detail/tagged_ptr.hpp>
    00034 #ifdef _MSC_VER
    00035     #pragma warning(pop)
    00036 #endif
    00037     #include <boost/lockfree/atomic_int.hpp>
    00038 #endif
    00039
    00040
    00041 namespace pion {    // begin namespace pion
    00042
    00043
    00051 template <std::size_t MinSize = 16, std::size_t MaxSize = 256>
    00052 class PionPoolAllocator
    00053     : private boost::noncopyable
    00054 {
    00055 public:
    00056
    00058     virtual ~PionPoolAllocator()
    00059     {}
    00060
    00062     PionPoolAllocator(void)
    00063     {
    00064         for (std::size_t n = 0; n < NumberOfAllocs; ++n) {
    00065             m_pools[n].reset(new FixedSizeAlloc((n+1) * MinSize));
    00066         }
    00067     }
    00068
    00076     inline void *malloc(std::size_t n)
    00077     {
    00078         // check for size greater than MaxSize
    00079         if (n > MaxSize)
    00080             return ::malloc(n);
    00081         FixedSizeAlloc *pool_ptr = getPool(n);
    00082
    00083 #if defined(PION_HAVE_LOCKFREE)
    00084         while (true) {
    00085             // get copy of free list pointer
    00086             FreeListPtr old_free_ptr(pool_ptr->m_free_ptr);
    00087             if (! old_free_ptr)
    00088                 break;  // use pool alloc if free list is empty
    00089
    00090             // use CAS operation to swap the free list pointer
    00091             if (pool_ptr->m_free_ptr.cas(old_free_ptr, old_free_ptr->next.get_ptr()))
    00092                 return reinterpret_cast<void*>(old_free_ptr.get_ptr());
    00093         }
    00094 #endif
    00095
    00096         boost::unique_lock<boost::mutex> pool_lock(pool_ptr->m_mutex);
    00097         return pool_ptr->m_pool.malloc();
    00098     }
    00099
    00106     inline void free(void *ptr, std::size_t n)
    00107     {
    00108         // check for size greater than MaxSize
    00109         if (n > MaxSize) {
    00110 			::free(ptr);
    00111             return;
    00112         }
    00113         FixedSizeAlloc *pool_ptr = getPool(n);
    00114 #if defined(PION_HAVE_LOCKFREE)
    00115         while (true) {
    00116             // get copy of free list pointer
    00117             FreeListPtr old_free_ptr(pool_ptr->m_free_ptr);
    00118
    00119             // cast memory being released to a free list node
    00120             // and point its next pointer to the current free list
    00121             FreeListNode *node_ptr = reinterpret_cast<FreeListNode*>(ptr);
    00122             node_ptr->next.set_ptr(old_free_ptr.get_ptr());
    00123
    00124             // use CAS operation to swap the free list pointer
    00125             if (pool_ptr->m_free_ptr.cas(old_free_ptr, node_ptr))
    00126                 break;
    00127         }
    00128 #else
    00129         boost::unique_lock<boost::mutex> pool_lock(pool_ptr->m_mutex);
    00130         return pool_ptr->m_pool.free(ptr);
    00131 #endif
    00132     }
    00133
    00141     inline bool release_memory(size_t pad = 10240000UL)
    00142     {
    00143         bool result = false;
    00144 /*
    00145         for (std::size_t n = 0; n < NumberOfAllocs; ++n) {
    00146             FixedSizeAlloc *pool_ptr = m_pools[n].get();
    00147             // need to lock before releasing free list because of calls
    00148             // to pool::free()
    00149             boost::unique_lock<boost::mutex> pool_lock(pool_ptr->m_mutex);
    00150 #if defined(PION_HAVE_LOCKFREE)
    00151             while (true) {
    00152                 // get copy of free list pointer
    00153                 FreeListPtr old_free_ptr(pool_ptr->m_free_ptr);
    00154                 if (! old_free_ptr)
    00155                     break;  // all done: free list is empty
    00156
    00157                 // use CAS operation to swap the free list pointer
    00158                 if (pool_ptr->m_free_ptr.cas(old_free_ptr, old_free_ptr->next.get_ptr()))
    00159                     pool_ptr->m_pool.free(old_free_ptr.get_ptr());  // release memory from pool
    00160             }
    00161 #endif
    00162             if (pool_ptr->m_pool.release_memory())
    00163                 result = true;
    00164         }
    00165 #if defined(PION_HAVE_MALLOC_TRIM)
    00166         ::malloc_trim(pad);
    00167 #endif
    00168 */
    00169         return result;
    00170     }
    00171
    00172
    00173 protected:
    00174
    00175 #if defined(PION_HAVE_LOCKFREE)
    00177     struct FreeListNode {
    00178         boost::lockfree::tagged_ptr<struct FreeListNode>    next;
    00179     };
    00180
    00182     typedef boost::lockfree::tagged_ptr<struct FreeListNode>    FreeListPtr;
    00183 #else
    00184     typedef void *  FreeListPtr;
    00185 #endif
    00186
    00191     BOOST_STATIC_ASSERT(MaxSize >= MinSize);
    00192     BOOST_STATIC_ASSERT(MaxSize % MinSize == 0);
    00193 #if defined(PION_HAVE_LOCKFREE)
    00194     BOOST_STATIC_ASSERT(MinSize >= sizeof(FreeListNode));
    00195 #endif
    00196
    00198     enum { NumberOfAllocs = ((MaxSize-1) / MinSize) + 1 };
    00199
    00204     struct FixedSizeAlloc
    00205     {
    00211         FixedSizeAlloc(std::size_t size)
    00212             : m_size(size), m_pool(size), m_free_ptr(NULL)
    00213         {}
    00214
    00216         boost::mutex        m_mutex;
    00217
    00219         std::size_t         m_size;
    00220
    00222         boost::pool<>       m_pool;
    00223
    00225         FreeListPtr         m_free_ptr;
    00226     };
    00227
    00228
    00236     inline FixedSizeAlloc* getPool(const std::size_t n)
    00237     {
    00238         PION_ASSERT(n > 0);
    00239         PION_ASSERT(n <= MaxSize);
    00240         return m_pools[ (n-1) / MinSize ].get();
    00241     }
    00242
    00243
    00244 private:
    00245
    00247     boost::array<boost::scoped_ptr<FixedSizeAlloc>, NumberOfAllocs> m_pools;
    00248 };
    00249
    00250
    00251 }   // end namespace pion
    00252
    00253 #endif