Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

Allocator.h

Go to the documentation of this file.
00001 // Copyright NVIDIA Corporation 2002-2004
00002 // TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
00003 // *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS
00004 // OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
00005 // AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS
00006 // BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
00007 // WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
00008 // BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS)
00009 // ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS
00010 // BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 
00011 
00012 #pragma once
00013 
00015 #include "nvsgcommon.h"
00016 
00017 //#define ALLOCATION_COUNTER
00018 
00019 // placement operator new does not have a matching delete operator!
00020 // 
00021 // objects of type T that are constructed at explicit specified memory 
00022 // locations by using the placement operator new() (i.g. new((void*)p) T())
00023 // should be destructed by explicitely calling their destructor (p->~T())
00024 #pragma warning(disable:4291) 
00025 
00026 #include <algorithm>
00027 #include <vector>
00028 #if defined( ALLOCATION_COUNTER )
00029 #include <map>
00030 #endif
00031 #include "nvutil/Singleton.h"
00032 #include "nvutil/Trace.h"
00033 
00034 namespace nvutil
00035 {
00037   class Chunk
00038   {
00039     public:
00041       Chunk(size_t blockSize); 
00042       
00044       bool operator==(const Chunk& rhs) const;
00045       bool operator!=(const Chunk& rhs) const;
00046       
00048       void * alloc();
00049       void dealloc(void * p);
00051       void freeMemory(); 
00052       
00054       unsigned char blocksAvailable() const;
00056       bool isUnused() const; 
00058       bool isInsideBounds(void * p) const;
00059 
00060     private:
00062       enum { numBlocks = 255 };
00063 
00065       void init();
00066       
00068       void * lowerBound() const;
00069       void * upperBound() const;
00070 
00071       unsigned char * m_rawMem; // raw memory
00072 
00074       size_t m_blockSize;
00075       size_t m_chunkSize;
00076 
00077       unsigned char m_firstAvailableBlock;  // addresses first memory block available
00078       unsigned char m_blocksAvailable;      // total amount of blocks available 
00079   };
00080 
00082   class FixedAllocator
00083   {
00084     public:
00086       FixedAllocator(); 
00087 
00089       ~FixedAllocator();
00090 
00092       void * alloc();
00093 
00095       void dealloc(void * p);
00096 
00098       void init ( size_t blockSize );
00099 
00100     private:
00101       size_t m_blockSize;   // fixed block size
00102 
00103       typedef std::vector< Chunk > Chunks;
00104       Chunks m_chunks; // array of chunks
00105       
00106       Chunk  * m_lastAlloc;   // last chunk taken for allocation
00107       Chunk  * m_lastDealloc; // last chunk taken for deallocation
00108   };
00109 
00110 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
00112   /** This class is used as a \c Singleton by \c IAllocator, which is the base of all \c RCObject classes.
00113     * It manages the efficient allocation of small objects by using an array of \c FixedAllocator objects, one for
00114     * each small size up to \c maxBlockSize. Allocations larger than that size are redirected to the standard
00115     * allocation \c ::new.  */
00116   class Allocator
00117   {
00118     public:
00120 
00121       Allocator();
00122 
00124 
00125       ~Allocator();
00126 
00128 
00129       void * alloc( size_t size   
00130                   );
00131 
00132 #ifdef _DEBUG
00133 
00134 
00136       void * alloc( size_t size         
00137                   , const char * src    
00138                   , unsigned int ln     
00139                   );
00140 #endif
00141 
00143 
00145       void dealloc( void * p      
00146                   , size_t size   
00147                   );
00148 
00149     private:
00151       void * __alloc(size_t size);
00152 
00153     private:
00154       // forward to default new/delete operators if block size
00155       // exceeds the threshold given by maxBlockSize
00156       enum { maxBlockSize = 256 };
00157 
00158       // provide one allocator for each single size <= maxBlockSize
00159       FixedAllocator m_allocTbl[maxBlockSize];
00160 
00161       // leak detection in debug mode
00162 #ifdef _DEBUG
00163       struct DbgAllocInfo
00164       {
00165         void  * p;            //  memory location
00166         std::string  source;  //  source file of allocation
00167         unsigned int ln;      //  line of allocation
00168 
00169         // with this we can simply use a vector for our alloc infos
00170         bool operator==(void * _p) const { return p==_p; }
00171         bool operator!=(void * _p) const { return p!=_p; }
00172       };
00173 
00174       std::vector<DbgAllocInfo> m_dbgAllocInfos;   //  vector of allocation infos
00175 
00176       //  helper functor to put out a memory leak warning
00177       struct LeakWarning
00178       {
00179         void operator()(const DbgAllocInfo& allocInfo) const
00180         {
00181           std::string str;
00182           char cPtr[sizeof(void*)*2+1];          
00183           sprintf(cPtr, "%p", allocInfo.p); // cast to a hex-string for pointers
00184           char cLine[20];  // enough for 64-bit integers even
00185           sprintf(cLine, "%d", allocInfo.ln); 
00186 
00187           str = "****Memory Leak Detected*** Source: ";
00188           str += allocInfo.source;
00189           str += ", Line: ";
00190           str += cLine;
00191           str += ", Ptr: 0x";
00192           str += cPtr;
00193           str += "\n";
00194           traceDebugOutput()(str.c_str());
00195         }
00196       };
00197 #endif
00198 #if defined( ALLOCATION_COUNTER )
00199       std::map<size_t,size_t> m_allocSizeMap;
00200 #endif
00201   };
00202 
00203   inline void * Allocator::alloc(size_t size)
00204   {
00205     __ASSERT(!"WARNING: Bypassing leak detection in debug mode!\n");
00206     return __alloc(size);
00207   }
00208 
00209 #ifdef _DEBUG
00210   inline void * Allocator::alloc(size_t size, const char* src, unsigned int ln)
00211   {
00212     DbgAllocInfo allocInfo;
00213     // collect allocation info for memory leak detection
00214     allocInfo.p = __alloc(size);
00215     allocInfo.source = src;
00216     allocInfo.ln = ln;
00217     m_dbgAllocInfos.push_back(allocInfo);
00218     return allocInfo.p;
00219   }
00220 #endif
00221 
00222   inline void Allocator::dealloc(void *p, size_t size)
00223   {
00224 #ifdef _DEBUG
00225     m_dbgAllocInfos.erase(std::remove(m_dbgAllocInfos.begin(), m_dbgAllocInfos.end(), p), m_dbgAllocInfos.end());
00226 #endif
00227     if ( size <= maxBlockSize )
00228     {
00229       m_allocTbl[size-1].dealloc(p); 
00230     } 
00231     else
00232     {
00233       ::operator delete(p);
00234     }
00235   }
00236 
00237   inline void * Allocator::__alloc(size_t size)
00238   {
00239 #if defined( ALLOCATION_COUNTER )
00240     m_allocSizeMap[size]++;
00241 #endif
00242     // use suitable allocation for given size
00243     return (size<=maxBlockSize) ? m_allocTbl[size-1].alloc() : ::operator new(size);
00244   }
00245 //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
00246 
00248 
00254   class IAllocator
00255   {
00256     public:
00258       typedef Singleton<Allocator> theAllocator;
00259       
00261 
00263       IAllocator() {}
00265       virtual ~IAllocator() {}
00266 
00268 
00270       NVSG_API void * operator new( size_t size // Size in bytes of demanded memory block.
00271                                   );
00272 #ifdef _DEBUG
00273 
00274 
00277       NVSG_API void * operator new( size_t size       // Size in bytes of demanded memory block.
00278                                   , const char * src  // Source file where allocation takes place.
00279                                   , unsigned int ln   // Line number within the source file where allocation takes place.
00280                                   );
00281 #endif
00282 
00283 
00289       NVSG_API void * operator new( size_t size // Size in bytes of demanded memory block.
00290                                   , void * p    // Start address of a valid memory block where the object will be created.
00291                                   );
00293 
00294       NVSG_API void operator delete( void * p    // Start address of memory block to be freed.
00295                                   , size_t size // Size of memory block to be freed.
00296                                   ); 
00297   };
00298 
00299 
00300   // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00301   // implementation following
00302   // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
00303 
00309   inline void Chunk::init()
00310   {
00311     m_rawMem = ::new unsigned char[m_chunkSize];
00312     
00313     m_firstAvailableBlock = 0;
00314     m_blocksAvailable = numBlocks;
00315 
00316     unsigned char * p = m_rawMem;
00317     for ( unsigned char i=0; i!=numBlocks; p+=m_blockSize ) 
00318     {
00319       // code indices of next available blocks in first byte of each block 
00320       *p = ++i;
00321     }
00322   }
00323 
00329   inline void Chunk::freeMemory()
00330   {
00331     // should not attempt to free memory of a chunk in use
00332     __ASSERT(isUnused());
00333     ::delete[] m_rawMem;
00334   }
00335 
00341   inline void * Chunk::alloc()
00342   {
00343     if ( !m_blocksAvailable ) 
00344     {
00345       // this needs to be handled in superior layers
00346       return NULL;
00347     }
00348 
00349     unsigned char * p = &m_rawMem[m_firstAvailableBlock*m_blockSize];
00350     // index of next block available is coded in first byte
00351     m_firstAvailableBlock = *p; 
00352     // one block less available in this chunk
00353     --m_blocksAvailable; 
00354 
00355     return (void *)p;
00356   }
00357 
00363   inline void Chunk::dealloc(void * p)
00364   {
00365     unsigned char * ptr = (unsigned char*)p;
00366     
00367     // range check
00368     __ASSERT(ptr>=m_rawMem);
00369     __ASSERT(ptr<&m_rawMem[m_chunkSize]);
00370     // alignment check
00371     __ASSERT(!((ptr-m_rawMem)%m_blockSize));
00372     
00373     *ptr = m_firstAvailableBlock;
00374     m_firstAvailableBlock = (unsigned char)((ptr - m_rawMem) / m_blockSize);
00375 
00376     // truncation check
00377     __ASSERT(m_firstAvailableBlock==((ptr-m_rawMem)/m_blockSize));
00378     ++m_blocksAvailable;
00379   }
00380 
00386   inline bool Chunk::operator==(const Chunk& rhs) const
00387   {
00388     // consider chunks to be equal if they point to same raw memory location
00389 #ifdef _DEBUG
00390     if ( m_rawMem==rhs.m_rawMem )
00391     {
00392       __ASSERT(  m_blockSize == rhs.m_blockSize 
00393             && m_firstAvailableBlock==rhs.m_firstAvailableBlock
00394             && m_blocksAvailable==rhs.m_blocksAvailable );
00395     }
00396 #endif
00397 
00398     return (  m_rawMem==rhs.m_rawMem );
00399   }
00400 
00406   inline bool Chunk::operator!=(const Chunk& rhs) const
00407   {
00408     return !operator==(rhs);
00409   }
00410 
00416   inline unsigned char Chunk::blocksAvailable() const 
00417   { 
00418     return m_blocksAvailable; 
00419   }
00420 
00426   inline bool Chunk::isInsideBounds(void * p) const
00427   {
00428     return p>=lowerBound() && p<upperBound();
00429   }
00430 
00436   inline bool Chunk::isUnused() const 
00437   { 
00438     return m_blocksAvailable==numBlocks; 
00439   }
00440 
00446   inline void * Chunk::lowerBound() const 
00447   { 
00448     return (void*)m_rawMem; 
00449   }
00450 
00456   inline void * Chunk::upperBound() const 
00457   { 
00458     return (void*)&m_rawMem[m_chunkSize]; 
00459   }
00460 
00466   inline void FixedAllocator::init(size_t blockSize)
00467   { 
00468     m_blockSize = blockSize; 
00469   }
00470 
00476   inline void * IAllocator::operator new(size_t size) 
00477   {
00478     return theAllocator::instance()->alloc(size);
00479   }
00480 
00481 #ifdef _DEBUG
00482   // provide memory leak detection in debug mode
00483   inline void * IAllocator::operator new(size_t size, const char * src, unsigned int ln)
00484   {
00485     return theAllocator::instance()->alloc(size, src, ln);
00486   }
00487 #endif
00488 
00494   inline void * IAllocator::operator new(size_t size, void * p)
00495   {
00496     // as this is used when constructing an object at a explicit
00497     // specified memory location, that location shoud be a valid 
00498     // one - assert this!
00499     __ASSERT(p);
00500     return p;
00501   }
00502 
00508   inline void IAllocator::operator delete(void * p, size_t size)
00509   {
00510     theAllocator::instance()->dealloc(p, size);
00511   }
00512 
00513 } // namespace nvutil
00514 

Generated on Tue Mar 1 13:19:16 2005 for NVSGSDK by NVIDIA