pion-net  4.0.9
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)
00176 
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