Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

559 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //-----------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //===========================================================================//
  13. #ifndef MEMPOOL_H
  14. #define MEMPOOL_H
  15. #ifdef _WIN32
  16. #pragma once
  17. #endif
  18. #include "tier0/memalloc.h"
  19. #include "tier0/tslist.h"
  20. #include "tier0/platform.h"
  21. #include "tier1/utlvector.h"
  22. #include "tier1/utlrbtree.h"
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Optimized pool memory allocator
  25. //-----------------------------------------------------------------------------
  26. typedef void (*MemoryPoolReportFunc_t)( PRINTF_FORMAT_STRING char const* pMsg, ... );
  27. // Ways a memory pool can grow when it needs to make a new blob:
  28. enum MemoryPoolGrowType_t
  29. {
  30. UTLMEMORYPOOL_GROW_NONE=0, // Don't allow new blobs.
  31. UTLMEMORYPOOL_GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates
  32. // get larger and larger each time it allocates one).
  33. UTLMEMORYPOOL_GROW_SLOW=2 // New blob size is numElements.
  34. };
  35. class CUtlMemoryPool
  36. {
  37. public:
  38. // !KLUDGE! For legacy code support, import the global enum into this scope
  39. enum MemoryPoolGrowType_t
  40. {
  41. GROW_NONE=UTLMEMORYPOOL_GROW_NONE,
  42. GROW_FAST=UTLMEMORYPOOL_GROW_FAST,
  43. GROW_SLOW=UTLMEMORYPOOL_GROW_SLOW
  44. };
  45. CUtlMemoryPool( int blockSize, int numElements, int growMode = UTLMEMORYPOOL_GROW_FAST, const char *pszAllocOwner = NULL, int nAlignment = 0 );
  46. ~CUtlMemoryPool();
  47. void* Alloc(); // Allocate the element size you specified in the constructor.
  48. void* Alloc( size_t amount );
  49. void* AllocZero(); // Allocate the element size you specified in the constructor, zero the memory before construction
  50. void* AllocZero( size_t amount );
  51. void Free(void *pMem);
  52. // Frees everything
  53. void Clear();
  54. // Error reporting...
  55. static void SetErrorReportFunc( MemoryPoolReportFunc_t func );
  56. // returns number of allocated blocks
  57. int Count() { return m_BlocksAllocated; }
  58. int PeakCount() { return m_PeakAlloc; }
  59. protected:
  60. class CBlob
  61. {
  62. public:
  63. CBlob *m_pPrev, *m_pNext;
  64. int m_NumBytes; // Number of bytes in this blob.
  65. char m_Data[1];
  66. char m_Padding[3]; // to int align the struct
  67. };
  68. // Resets the pool
  69. void Init();
  70. void AddNewBlob();
  71. void ReportLeaks();
  72. int m_BlockSize;
  73. int m_BlocksPerBlob;
  74. int m_GrowMode; // GROW_ enum.
  75. // Put m_BlocksAllocated in front of m_pHeadOfFreeList for better
  76. // packing on 64-bit where pointers are 8-byte aligned.
  77. int m_BlocksAllocated;
  78. // FIXME: Change m_ppMemBlob into a growable array?
  79. void *m_pHeadOfFreeList;
  80. int m_PeakAlloc;
  81. unsigned short m_nAlignment;
  82. unsigned short m_NumBlobs;
  83. const char * m_pszAllocOwner;
  84. // CBlob could be not a multiple of 4 bytes so stuff it at the end here to keep us otherwise aligned
  85. CBlob m_BlobHead;
  86. static MemoryPoolReportFunc_t g_ReportFunc;
  87. };
  88. //-----------------------------------------------------------------------------
  89. //
  90. //-----------------------------------------------------------------------------
  91. class CMemoryPoolMT : public CUtlMemoryPool
  92. {
  93. public:
  94. CMemoryPoolMT(int blockSize, int numElements, int growMode = UTLMEMORYPOOL_GROW_FAST, const char *pszAllocOwner = NULL) : CUtlMemoryPool( blockSize, numElements, growMode, pszAllocOwner) {}
  95. void* Alloc() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc(); }
  96. void* Alloc( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc( amount ); }
  97. void* AllocZero() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero(); }
  98. void* AllocZero( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero( amount ); }
  99. void Free(void *pMem) { AUTO_LOCK( m_mutex ); CUtlMemoryPool::Free( pMem ); }
  100. // Frees everything
  101. void Clear() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Clear(); }
  102. private:
  103. CThreadFastMutex m_mutex; // @TODO: Rework to use tslist (toml 7/6/2007)
  104. };
  105. //-----------------------------------------------------------------------------
  106. // Wrapper macro to make an allocator that returns particular typed allocations
  107. // and construction and destruction of objects.
  108. //-----------------------------------------------------------------------------
  109. template< class T >
  110. class CClassMemoryPool : public CUtlMemoryPool
  111. {
  112. public:
  113. CClassMemoryPool(int numElements, int growMode = GROW_FAST, int nAlignment = 0 ) :
  114. CUtlMemoryPool( sizeof(T), numElements, growMode, MEM_ALLOC_CLASSNAME(T), nAlignment ) {
  115. #ifdef PLATFORM_64BITS
  116. COMPILE_TIME_ASSERT( sizeof(CUtlMemoryPool) == 64 );
  117. #else
  118. COMPILE_TIME_ASSERT( sizeof(CUtlMemoryPool) == 48 );
  119. #endif
  120. }
  121. T* Alloc();
  122. T* AllocZero();
  123. void Free( T *pMem );
  124. void Clear();
  125. };
  126. //-----------------------------------------------------------------------------
  127. // Specialized pool for aligned data management (e.g., Xbox cubemaps)
  128. //-----------------------------------------------------------------------------
  129. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD = 4 >
  130. class CAlignedMemPool
  131. {
  132. enum
  133. {
  134. BLOCK_SIZE = ALIGN_VALUE( ITEM_SIZE, ALIGNMENT ) > 8 ? ALIGN_VALUE( ITEM_SIZE, ALIGNMENT ) : 8
  135. };
  136. public:
  137. CAlignedMemPool();
  138. void *Alloc();
  139. void Free( void *p );
  140. static int __cdecl CompareChunk( void * const *ppLeft, void * const *ppRight );
  141. void Compact();
  142. int NumTotal() { return m_Chunks.Count() * ( CHUNK_SIZE / BLOCK_SIZE ); }
  143. int NumAllocated() { return NumTotal() - m_nFree; }
  144. int NumFree() { return m_nFree; }
  145. int BytesTotal() { return NumTotal() * BLOCK_SIZE; }
  146. int BytesAllocated() { return NumAllocated() * BLOCK_SIZE; }
  147. int BytesFree() { return NumFree() * BLOCK_SIZE; }
  148. int ItemSize() { return ITEM_SIZE; }
  149. int BlockSize() { return BLOCK_SIZE; }
  150. int ChunkSize() { return CHUNK_SIZE; }
  151. private:
  152. struct FreeBlock_t
  153. {
  154. FreeBlock_t *pNext;
  155. byte reserved[ BLOCK_SIZE - sizeof( FreeBlock_t *) ];
  156. };
  157. CUtlVector<void *> m_Chunks; // Chunks are tracked outside blocks (unlike CUtlMemoryPool) to simplify alignment issues
  158. FreeBlock_t * m_pFirstFree;
  159. int m_nFree;
  160. CAllocator m_Allocator;
  161. float m_TimeLastCompact;
  162. };
  163. //-----------------------------------------------------------------------------
  164. // Pool variant using standard allocation
  165. //-----------------------------------------------------------------------------
  166. template <typename T, int nInitialCount = 0, bool bDefCreateNewIfEmpty = true >
  167. class CObjectPool
  168. {
  169. public:
  170. CObjectPool()
  171. {
  172. int i = nInitialCount;
  173. while ( i-- > 0 )
  174. {
  175. m_AvailableObjects.PushItem( new T );
  176. }
  177. }
  178. ~CObjectPool()
  179. {
  180. Purge();
  181. }
  182. int NumAvailable()
  183. {
  184. return m_AvailableObjects.Count();
  185. }
  186. void Purge()
  187. {
  188. T *p;
  189. while ( m_AvailableObjects.PopItem( &p ) )
  190. {
  191. delete p;
  192. }
  193. }
  194. T *GetObject( bool bCreateNewIfEmpty = bDefCreateNewIfEmpty )
  195. {
  196. T *p;
  197. if ( !m_AvailableObjects.PopItem( &p ) )
  198. {
  199. p = ( bCreateNewIfEmpty ) ? new T : NULL;
  200. }
  201. return p;
  202. }
  203. void PutObject( T *p )
  204. {
  205. m_AvailableObjects.PushItem( p );
  206. }
  207. private:
  208. CTSList<T *> m_AvailableObjects;
  209. };
  210. //-----------------------------------------------------------------------------
  211. template< class T >
  212. inline T* CClassMemoryPool<T>::Alloc()
  213. {
  214. T *pRet;
  215. {
  216. MEM_ALLOC_CREDIT_(MEM_ALLOC_CLASSNAME(T));
  217. pRet = (T*)CUtlMemoryPool::Alloc();
  218. }
  219. if ( pRet )
  220. {
  221. Construct( pRet );
  222. }
  223. return pRet;
  224. }
  225. template< class T >
  226. inline T* CClassMemoryPool<T>::AllocZero()
  227. {
  228. T *pRet;
  229. {
  230. MEM_ALLOC_CREDIT_(MEM_ALLOC_CLASSNAME(T));
  231. pRet = (T*)CUtlMemoryPool::AllocZero();
  232. }
  233. if ( pRet )
  234. {
  235. Construct( pRet );
  236. }
  237. return pRet;
  238. }
  239. template< class T >
  240. inline void CClassMemoryPool<T>::Free(T *pMem)
  241. {
  242. if ( pMem )
  243. {
  244. Destruct( pMem );
  245. }
  246. CUtlMemoryPool::Free( pMem );
  247. }
  248. template< class T >
  249. inline void CClassMemoryPool<T>::Clear()
  250. {
  251. CUtlRBTree<void *> freeBlocks;
  252. SetDefLessFunc( freeBlocks );
  253. void *pCurFree = m_pHeadOfFreeList;
  254. while ( pCurFree != NULL )
  255. {
  256. freeBlocks.Insert( pCurFree );
  257. pCurFree = *((void**)pCurFree);
  258. }
  259. for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext )
  260. {
  261. T *p = (T *)pCur->m_Data;
  262. T *pLimit = (T *)(pCur->m_Data + pCur->m_NumBytes);
  263. while ( p < pLimit )
  264. {
  265. if ( freeBlocks.Find( p ) == freeBlocks.InvalidIndex() )
  266. {
  267. Destruct( p );
  268. }
  269. p++;
  270. }
  271. }
  272. CUtlMemoryPool::Clear();
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Macros that make it simple to make a class use a fixed-size allocator
  276. // Put DECLARE_FIXEDSIZE_ALLOCATOR in the private section of a class,
  277. // Put DEFINE_FIXEDSIZE_ALLOCATOR in the CPP file
  278. //-----------------------------------------------------------------------------
  279. #define DECLARE_FIXEDSIZE_ALLOCATOR( _class ) \
  280. public: \
  281. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  282. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  283. inline void operator delete( void* p ) { s_Allocator.Free(p); } \
  284. inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
  285. private: \
  286. static CUtlMemoryPool s_Allocator
  287. #define DEFINE_FIXEDSIZE_ALLOCATOR( _class, _initsize, _grow ) \
  288. CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
  289. #define DEFINE_FIXEDSIZE_ALLOCATOR_ALIGNED( _class, _initsize, _grow, _alignment ) \
  290. CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool", _alignment )
  291. #define DECLARE_FIXEDSIZE_ALLOCATOR_MT( _class ) \
  292. public: \
  293. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  294. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  295. inline void operator delete( void* p ) { s_Allocator.Free(p); } \
  296. inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
  297. private: \
  298. static CMemoryPoolMT s_Allocator
  299. #define DEFINE_FIXEDSIZE_ALLOCATOR_MT( _class, _initsize, _grow ) \
  300. CMemoryPoolMT _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
  301. //-----------------------------------------------------------------------------
  302. // Macros that make it simple to make a class use a fixed-size allocator
  303. // This version allows us to use a memory pool which is externally defined...
  304. // Put DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the private section of a class,
  305. // Put DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the CPP file
  306. //-----------------------------------------------------------------------------
  307. #define DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class ) \
  308. public: \
  309. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
  310. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
  311. inline void operator delete( void* p ) { s_pAllocator->Free(p); } \
  312. private: \
  313. static CUtlMemoryPool* s_pAllocator
  314. #define DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class, _allocator ) \
  315. CUtlMemoryPool* _class::s_pAllocator = _allocator
  316. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD >
  317. inline CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, COMPACT_THRESHOLD>::CAlignedMemPool()
  318. : m_pFirstFree( 0 ),
  319. m_nFree( 0 ),
  320. m_TimeLastCompact( 0 )
  321. {
  322. COMPILE_TIME_ASSERT( sizeof( FreeBlock_t ) >= BLOCK_SIZE );
  323. COMPILE_TIME_ASSERT( ALIGN_VALUE( sizeof( FreeBlock_t ), ALIGNMENT ) == sizeof( FreeBlock_t ) );
  324. }
  325. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD >
  326. inline void *CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, COMPACT_THRESHOLD>::Alloc()
  327. {
  328. if ( !m_pFirstFree )
  329. {
  330. FreeBlock_t *pNew = (FreeBlock_t *)m_Allocator.Alloc( CHUNK_SIZE );
  331. Assert( (unsigned)pNew % ALIGNMENT == 0 );
  332. m_Chunks.AddToTail( pNew );
  333. m_nFree = CHUNK_SIZE / BLOCK_SIZE;
  334. m_pFirstFree = pNew;
  335. for ( int i = 0; i < m_nFree - 1; i++ )
  336. {
  337. pNew->pNext = pNew + 1;
  338. pNew++;
  339. }
  340. pNew->pNext = NULL;
  341. }
  342. void *p = m_pFirstFree;
  343. m_pFirstFree = m_pFirstFree->pNext;
  344. m_nFree--;
  345. return p;
  346. }
  347. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD >
  348. inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, COMPACT_THRESHOLD>::Free( void *p )
  349. {
  350. // Insertion sort to encourage allocation clusters in chunks
  351. FreeBlock_t *pFree = ((FreeBlock_t *)p);
  352. FreeBlock_t *pCur = m_pFirstFree;
  353. FreeBlock_t *pPrev = NULL;
  354. while ( pCur && pFree > pCur )
  355. {
  356. pPrev = pCur;
  357. pCur = pCur->pNext;
  358. }
  359. pFree->pNext = pCur;
  360. if ( pPrev )
  361. {
  362. pPrev->pNext = pFree;
  363. }
  364. else
  365. {
  366. m_pFirstFree = pFree;
  367. }
  368. m_nFree++;
  369. if ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD )
  370. {
  371. float time = Plat_FloatTime();
  372. float compactTime = ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD * 4 ) ? 15.0 : 30.0;
  373. if ( m_TimeLastCompact > time || m_TimeLastCompact + compactTime < Plat_FloatTime() )
  374. {
  375. Compact();
  376. m_TimeLastCompact = time;
  377. }
  378. }
  379. }
  380. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD >
  381. inline int __cdecl CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, COMPACT_THRESHOLD>::CompareChunk( void * const *ppLeft, void * const *ppRight )
  382. {
  383. return ((unsigned)*ppLeft) - ((unsigned)*ppRight);
  384. }
  385. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, int COMPACT_THRESHOLD >
  386. inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, COMPACT_THRESHOLD>::Compact()
  387. {
  388. FreeBlock_t *pCur = m_pFirstFree;
  389. FreeBlock_t *pPrev = NULL;
  390. m_Chunks.Sort( CompareChunk );
  391. #ifdef VALIDATE_ALIGNED_MEM_POOL
  392. {
  393. FreeBlock_t *p = m_pFirstFree;
  394. while ( p )
  395. {
  396. if ( p->pNext && p > p->pNext )
  397. {
  398. __asm { int 3 }
  399. }
  400. p = p->pNext;
  401. }
  402. for ( int i = 0; i < m_Chunks.Count(); i++ )
  403. {
  404. if ( i + 1 < m_Chunks.Count() )
  405. {
  406. if ( m_Chunks[i] > m_Chunks[i + 1] )
  407. {
  408. __asm { int 3 }
  409. }
  410. }
  411. }
  412. }
  413. #endif
  414. int i;
  415. for ( i = 0; i < m_Chunks.Count(); i++ )
  416. {
  417. int nBlocksPerChunk = CHUNK_SIZE / BLOCK_SIZE;
  418. FreeBlock_t *pChunkLimit = ((FreeBlock_t *)m_Chunks[i]) + nBlocksPerChunk;
  419. int nFromChunk = 0;
  420. if ( pCur == m_Chunks[i] )
  421. {
  422. FreeBlock_t *pFirst = pCur;
  423. while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit )
  424. {
  425. pCur = pCur->pNext;
  426. nFromChunk++;
  427. }
  428. pCur = pFirst;
  429. }
  430. while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit )
  431. {
  432. if ( nFromChunk != nBlocksPerChunk )
  433. {
  434. if ( pPrev )
  435. {
  436. pPrev->pNext = pCur;
  437. }
  438. else
  439. {
  440. m_pFirstFree = pCur;
  441. }
  442. pPrev = pCur;
  443. }
  444. else if ( pPrev )
  445. {
  446. pPrev->pNext = NULL;
  447. }
  448. else
  449. {
  450. m_pFirstFree = NULL;
  451. }
  452. pCur = pCur->pNext;
  453. }
  454. if ( nFromChunk == nBlocksPerChunk )
  455. {
  456. m_Allocator.Free( m_Chunks[i] );
  457. m_nFree -= nBlocksPerChunk;
  458. m_Chunks[i] = 0;
  459. }
  460. }
  461. for ( i = m_Chunks.Count() - 1; i >= 0 ; i-- )
  462. {
  463. if ( !m_Chunks[i] )
  464. {
  465. m_Chunks.FastRemove( i );
  466. }
  467. }
  468. }
  469. #endif // MEMPOOL_H