TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/host/memory/MemoryPool.cpp
Go to the documentation of this file.
00001 #include "MemoryPool.hpp"
00002 
00003 #include <tuttle/common/utils/global.hpp>
00004 #include <tuttle/common/system/memoryInfo.hpp>
00005 #include <tuttle/host/Core.hpp>
00006 
00007 #include <boost/throw_exception.hpp>
00008 
00009 #include <algorithm>
00010 
00011 namespace tuttle {
00012 namespace host {
00013 namespace memory {
00014 
00015 IPool::~IPool() {}
00016 
00017 class PoolData : public IPoolData
00018 {
00019 private:
00020         PoolData(); ///< No default Ctor
00021         PoolData( const PoolData& ); ///< No copy Ctor
00022         friend class MemoryPool;
00023 
00024 public:
00025         PoolData( IPool& pool, const std::size_t size )
00026                 : _pool( pool )
00027                 , _id( _count++ )
00028                 , _reservedSize( size )
00029                 , _size( size )
00030                 , _pData( new char[size] )
00031                 , _refCount( 0 )
00032         {}
00033 
00034         ~PoolData()
00035         {
00036                 delete [] _pData;
00037         }
00038 
00039 public:
00040         bool operator==( const PoolData& other ) const
00041         {
00042                 return _id == other._id;
00043         }
00044 
00045         void addRef();
00046         void release();
00047 
00048         char*             data()               { return _pData; }
00049         const char*       data() const         { return _pData; }
00050         const std::size_t size() const         { return _size; }
00051         const std::size_t reservedSize() const { return _reservedSize; }
00052 
00053         void setSize( const std::size_t newSize )
00054         {
00055                 assert( newSize <= _reservedSize );
00056                 _size = newSize;
00057         }
00058 
00059 private:
00060         static std::size_t _count; ///< unique id generator
00061         IPool& _pool; ///< ref to the owner pool
00062         const std::size_t _id; ///< unique id to identify one memory data
00063         const std::size_t _reservedSize; ///< memory allocated
00064         std::size_t _size; ///< memory requested
00065         char* const _pData; ///< own the data
00066         int _refCount; ///< counter on clients currently using this data
00067 };
00068 
00069 void intrusive_ptr_add_ref( IPoolData* pData )
00070 {
00071         pData->addRef();
00072 }
00073 
00074 void intrusive_ptr_release( IPoolData* pData )
00075 {
00076         pData->release();
00077 }
00078 
00079 std::size_t PoolData::_count = 0;
00080 
00081 void PoolData::addRef()
00082 {
00083         if( ++_refCount == 1 )
00084                 _pool.referenced( this );
00085 }
00086 
00087 void PoolData::release()
00088 {
00089         if( --_refCount == 0 )
00090                 _pool.released( this );
00091 }
00092 
00093 MemoryPool::MemoryPool( const std::size_t maxSize )
00094         : _memoryAuthorized( maxSize )
00095 {}
00096 
00097 MemoryPool::~MemoryPool()
00098 {
00099         if( !_dataUsed.empty() )
00100         {
00101                 TUTTLE_LOG_DEBUG( "[Memory Pool] Error inside memory pool. Some data always mark used at the destruction (nb elements:" << _dataUsed.size() << ")" );
00102         }
00103 }
00104 
00105 void MemoryPool::referenced( PoolData* pData )
00106 {
00107         boost::mutex::scoped_lock locker( _mutex );
00108         DataList::iterator it = _dataUnused.find( pData );
00109 
00110         if( it != _dataUnused.end() )
00111         {
00112                 _dataUnused.erase( it );
00113         }
00114         else // a really new data
00115         {
00116                 _allDatas.push_back( pData );
00117                 _dataMap[pData->data()] = pData;
00118         }
00119         _dataUsed.insert( pData );
00120 }
00121 
00122 void MemoryPool::released( PoolData* pData )
00123 {
00124         boost::mutex::scoped_lock locker( _mutex );
00125         _dataUsed.erase( pData );
00126         _dataUnused.insert( pData );
00127 }
00128 
00129 namespace  {
00130 
00131 /// Functor to get the smallest element in pool
00132 struct DataFitSize : public std::unary_function<PoolData*, void>
00133 {
00134         DataFitSize( std::size_t size )
00135                 : _sizeNeeded( size )
00136                 , _bestMatchDiff( ULONG_MAX )
00137                 , _pBestMatch( NULL )
00138         {}
00139 
00140         void operator()( PoolData* pData )
00141         {
00142                 const std::size_t dataSize = pData->reservedSize();
00143                 
00144                 // Check minimum amount of memory
00145                 if( _sizeNeeded > dataSize )
00146                         return;
00147                 // Do not reuse too big buffers
00148                 if( dataSize > _maxBufferRatio * _sizeNeeded )
00149                         return;
00150                 const std::size_t diff = dataSize - _sizeNeeded;
00151                 if( diff >= _bestMatchDiff )
00152                         return;
00153                 _bestMatchDiff = diff;
00154                 _pBestMatch    = pData;
00155         }
00156 
00157         PoolData* bestMatch()
00158         {
00159                 return _pBestMatch;
00160         }
00161 
00162         private:
00163                 static const double _maxBufferRatio;  //< max ratio between used and unused part of the buffer
00164                 const std::size_t _sizeNeeded;
00165                 std::size_t _bestMatchDiff;
00166                 PoolData* _pBestMatch;
00167 };
00168 
00169 const double DataFitSize::_maxBufferRatio = 2.0;
00170 
00171 }
00172 
00173 IPoolDataPtr MemoryPool::allocate( const std::size_t size )
00174 {
00175         // Try to reuse a buffer available in the MemoryPool
00176         IPoolData* pData = getOneAvailableData( size );
00177         if( pData != NULL )
00178         {
00179                 TUTTLE_LOG_TRACE("[Memory Pool] Reuse a buffer available in the MemoryPool");
00180                 pData->setSize( size );
00181                 return pData;
00182         }
00183 
00184         // Try to remove unused element in MemoryCache, and reuse the buffer available in the MemoryPool
00185         memory::IMemoryCache& memoryCache = core().getMemoryCache();
00186         CACHE_ELEMENT unusedCacheElement = memoryCache.getUnusedWithSize( size );
00187         if( unusedCacheElement.get() != NULL )
00188         {
00189                 TUTTLE_LOG_TRACE("[Memory Pool] Pop element in the MemoryCache from " << unusedCacheElement->getFullName() << " of size " << size);
00190                 memoryCache.remove( unusedCacheElement );
00191 
00192                 pData = getOneAvailableData( size );
00193                 if( pData != NULL )
00194                 {
00195                         TUTTLE_LOG_TRACE("[Memory Pool] Reuse a buffer available in the MemoryPool");
00196                         pData->setSize( size );
00197                         return pData;
00198                 }
00199         }
00200 
00201         // Try to allocate a new buffer in MemoryPool
00202         std::size_t availableSize = getAvailableMemorySize();
00203         if( size > availableSize )
00204         {
00205                 // Try to release elements from the MemoryCache (make them available to the MemoryPool)
00206                 TUTTLE_LOG_TRACE("[Memory Pool] Release elements from the MemoryCache");
00207                 memoryCache.clearUnused();
00208 
00209                 availableSize = getAvailableMemorySize();
00210                 if( size > availableSize )
00211                 {
00212                         // Release elements from the MemoryPool (make them available to the OS)
00213                         TUTTLE_LOG_TRACE("[Memory Pool] Release elements from the MemoryPool");
00214                         clear();
00215                 }
00216 
00217                 availableSize = getAvailableMemorySize();
00218                 if( size > availableSize )
00219                 {
00220                         std::stringstream s;
00221                         s << "[Memory Pool] can't allocate size:" << size << " because memory available is equal to " << availableSize << " bytes";
00222                         BOOST_THROW_EXCEPTION( std::length_error( s.str() ) );
00223                 }
00224         }
00225 
00226         // Allocate a new buffer in MemoryPool
00227         TUTTLE_TLOG( TUTTLE_TRACE, "[Memory Pool] allocate " << size << " bytes" );
00228         return new PoolData( *this, size );
00229 }
00230 
00231 std::size_t MemoryPool::updateMemoryAuthorizedWithRAM()
00232 {
00233         _memoryAuthorized = /*getUsedMemorySize() +*/ getMemoryInfo()._totalRam;
00234         TUTTLE_LOG_DEBUG( TUTTLE_TRACE, "[Memory Pool] update memory authorized with RAM: " << _memoryAuthorized );
00235         return _memoryAuthorized;
00236 }
00237 
00238 namespace  {
00239 
00240 std::size_t accumulateReservedSize( const std::size_t& sum, const IPoolData* pData )
00241 {
00242         return sum + pData->reservedSize();
00243 }
00244 
00245 std::size_t accumulateWastedSize( const std::size_t& sum, const IPoolData* pData )
00246 {
00247         return sum + ( pData->reservedSize() - pData->size() );
00248 }
00249 
00250 }
00251 
00252 std::size_t MemoryPool::getUsedMemorySize() const
00253 {
00254         boost::mutex::scoped_lock locker( _mutex );
00255         return std::accumulate( _dataUsed.begin(), _dataUsed.end(), 0, &accumulateReservedSize );
00256 }
00257 
00258 std::size_t MemoryPool::getAllocatedAndUnusedMemorySize() const
00259 {
00260         boost::mutex::scoped_lock locker( _mutex );
00261         return std::accumulate( _dataUnused.begin(), _dataUnused.end(), 0, &accumulateReservedSize );
00262 }
00263 
00264 std::size_t MemoryPool::getAllocatedMemorySize() const
00265 {
00266         return getUsedMemorySize() + getAllocatedAndUnusedMemorySize();
00267 }
00268 
00269 std::size_t MemoryPool::getMaxMemorySize() const
00270 {
00271         return _memoryAuthorized;
00272 }
00273 
00274 std::size_t MemoryPool::getAvailableMemorySize() const
00275 {
00276         return getMaxMemorySize() - getUsedMemorySize();
00277 }
00278 
00279 std::size_t MemoryPool::getWastedMemorySize() const
00280 {
00281         boost::mutex::scoped_lock locker( _mutex );
00282         return std::accumulate( _dataUsed.begin(), _dataUsed.end(), 0, std::ptr_fun( &accumulateWastedSize ) );
00283 }
00284 
00285 std::size_t MemoryPool::getDataUsedSize() const
00286 {
00287         return _dataUsed.size();
00288 }
00289 
00290 std::size_t MemoryPool::getDataUnusedSize() const
00291 {
00292         return _dataUnused.size();
00293 }
00294 
00295 PoolData* MemoryPool::getOneAvailableData( const size_t size )
00296 {
00297         boost::mutex::scoped_lock locker( _mutex );
00298         return std::for_each( _dataUnused.begin(), _dataUnused.end(), DataFitSize( size ) ).bestMatch();
00299 }
00300 
00301 void MemoryPool::clear( std::size_t size )
00302 {
00303         /// @todo tuttle
00304 }
00305 
00306 void MemoryPool::clear()
00307 {
00308         boost::mutex::scoped_lock locker( _mutex );
00309         _dataUnused.clear();
00310 }
00311 
00312 void MemoryPool::clearOne()
00313 {
00314         boost::mutex::scoped_lock locker( _mutex );
00315         _dataUnused.erase( _dataUnused.begin() );
00316 }
00317 
00318 std::ostream& operator<<( std::ostream& os, const MemoryPool& memoryPool )
00319 {
00320         os << "[Memory Pool] Unused data:           " << memoryPool.getDataUnusedSize() << " bytes\n";
00321         os << "[Memory Pool] All datas:             " << memoryPool.getDataUsedSize() << " bytes\n";
00322         os << "[Memory Pool] Total RAM:             " << getMemoryInfo()._totalRam << " bytes\n";
00323         os << "\n";
00324         os << "[Memory Pool] Used memory:           " << memoryPool.getUsedMemorySize() << " bytes\n";
00325         os << "[Memory Pool] Allocated memory:      " << memoryPool.getAllocatedMemorySize() << " bytes\n";
00326         os << "[Memory Pool] Max memory:            " << memoryPool.getMaxMemorySize() << " bytes\n";
00327         os << "[Memory Pool] Available memory size: " << memoryPool.getAvailableMemorySize() << " bytes\n";
00328         os << "[Memory Pool] Wasted memory:         " << memoryPool.getWastedMemorySize() << " bytes\n";
00329         return os;
00330 }
00331 
00332 }
00333 }
00334 }