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.

312 lines
7.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mempool.h"
  7. #include <stdio.h>
  8. #include <malloc.h>
  9. #include <memory.h>
  10. #include "tier0/dbg.h"
  11. #include <ctype.h>
  12. #include "tier1/strtools.h"
  13. // Should be last include
  14. #include "tier0/memdbgon.h"
  15. MemoryPoolReportFunc_t CUtlMemoryPool::g_ReportFunc = 0;
  16. //-----------------------------------------------------------------------------
  17. // Error reporting... (debug only)
  18. //-----------------------------------------------------------------------------
  19. void CUtlMemoryPool::SetErrorReportFunc( MemoryPoolReportFunc_t func )
  20. {
  21. g_ReportFunc = func;
  22. }
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Constructor
  25. //-----------------------------------------------------------------------------
  26. CUtlMemoryPool::CUtlMemoryPool( int blockSize, int numElements, int growMode, const char *pszAllocOwner, int nAlignment )
  27. {
  28. #ifdef _X360
  29. if( numElements > 0 && growMode != UTLMEMORYPOOL_GROW_NONE )
  30. {
  31. numElements = 1;
  32. }
  33. #endif
  34. m_nAlignment = ( nAlignment != 0 ) ? nAlignment : 1;
  35. Assert( IsPowerOfTwo( m_nAlignment ) );
  36. m_BlockSize = blockSize < sizeof(void*) ? sizeof(void*) : blockSize;
  37. m_BlockSize = AlignValue( m_BlockSize, m_nAlignment );
  38. m_BlocksPerBlob = numElements;
  39. m_PeakAlloc = 0;
  40. m_GrowMode = growMode;
  41. if ( !pszAllocOwner )
  42. {
  43. pszAllocOwner = __FILE__;
  44. }
  45. m_pszAllocOwner = pszAllocOwner;
  46. Init();
  47. AddNewBlob();
  48. }
  49. //-----------------------------------------------------------------------------
  50. // Purpose: Frees the memory contained in the mempool, and invalidates it for
  51. // any further use.
  52. // Input : *memPool - the mempool to shutdown
  53. //-----------------------------------------------------------------------------
  54. CUtlMemoryPool::~CUtlMemoryPool()
  55. {
  56. if (m_BlocksAllocated > 0)
  57. {
  58. ReportLeaks();
  59. }
  60. Clear();
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Resets the pool
  64. //-----------------------------------------------------------------------------
  65. void CUtlMemoryPool::Init()
  66. {
  67. m_NumBlobs = 0;
  68. m_BlocksAllocated = 0;
  69. m_pHeadOfFreeList = 0;
  70. m_BlobHead.m_pNext = m_BlobHead.m_pPrev = &m_BlobHead;
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Frees everything
  74. //-----------------------------------------------------------------------------
  75. void CUtlMemoryPool::Clear()
  76. {
  77. // Free everything..
  78. CBlob *pNext;
  79. for( CBlob *pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pNext )
  80. {
  81. pNext = pCur->m_pNext;
  82. free( pCur );
  83. }
  84. Init();
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Reports memory leaks
  88. //-----------------------------------------------------------------------------
  89. void CUtlMemoryPool::ReportLeaks()
  90. {
  91. if (!g_ReportFunc)
  92. return;
  93. g_ReportFunc("Memory leak: mempool blocks left in memory: %d\n", m_BlocksAllocated);
  94. #ifdef _DEBUG
  95. // walk and destroy the free list so it doesn't intefere in the scan
  96. while (m_pHeadOfFreeList != NULL)
  97. {
  98. void *next = *((void**)m_pHeadOfFreeList);
  99. memset(m_pHeadOfFreeList, 0, m_BlockSize);
  100. m_pHeadOfFreeList = next;
  101. }
  102. g_ReportFunc("Dumping memory: \'");
  103. for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext )
  104. {
  105. // scan the memory block and dump the leaks
  106. char *scanPoint = (char *)pCur->m_Data;
  107. char *scanEnd = pCur->m_Data + pCur->m_NumBytes;
  108. bool needSpace = false;
  109. while (scanPoint < scanEnd)
  110. {
  111. // search for and dump any strings
  112. if ((unsigned)(*scanPoint + 1) <= 256 && isprint(*scanPoint))
  113. {
  114. g_ReportFunc("%c", *scanPoint);
  115. needSpace = true;
  116. }
  117. else if (needSpace)
  118. {
  119. needSpace = false;
  120. g_ReportFunc(" ");
  121. }
  122. scanPoint++;
  123. }
  124. }
  125. g_ReportFunc("\'\n");
  126. #endif // _DEBUG
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CUtlMemoryPool::AddNewBlob()
  132. {
  133. MEM_ALLOC_CREDIT_(m_pszAllocOwner);
  134. int sizeMultiplier;
  135. if( m_GrowMode == UTLMEMORYPOOL_GROW_SLOW )
  136. {
  137. sizeMultiplier = 1;
  138. }
  139. else
  140. {
  141. if ( m_GrowMode == UTLMEMORYPOOL_GROW_NONE )
  142. {
  143. // Can only have one allocation when we're in this mode
  144. if( m_NumBlobs != 0 )
  145. {
  146. Assert( !"CUtlMemoryPool::AddNewBlob: mode == UTLMEMORYPOOL_GROW_NONE" );
  147. return;
  148. }
  149. }
  150. // GROW_FAST and GROW_NONE use this.
  151. sizeMultiplier = m_NumBlobs + 1;
  152. }
  153. // maybe use something other than malloc?
  154. int nElements = m_BlocksPerBlob * sizeMultiplier;
  155. int blobSize = m_BlockSize * nElements;
  156. CBlob *pBlob = (CBlob*)malloc( sizeof(CBlob) - 1 + blobSize + ( m_nAlignment - 1 ) );
  157. Assert( pBlob );
  158. // Link it in at the end of the blob list.
  159. pBlob->m_NumBytes = blobSize;
  160. pBlob->m_pNext = &m_BlobHead;
  161. pBlob->m_pPrev = pBlob->m_pNext->m_pPrev;
  162. pBlob->m_pNext->m_pPrev = pBlob->m_pPrev->m_pNext = pBlob;
  163. // setup the free list
  164. m_pHeadOfFreeList = AlignValue( pBlob->m_Data, m_nAlignment );
  165. Assert (m_pHeadOfFreeList);
  166. void **newBlob = (void**)m_pHeadOfFreeList;
  167. for (int j = 0; j < nElements-1; j++)
  168. {
  169. newBlob[0] = (char*)newBlob + m_BlockSize;
  170. newBlob = (void**)newBlob[0];
  171. }
  172. // null terminate list
  173. newBlob[0] = NULL;
  174. m_NumBlobs++;
  175. }
  176. void* CUtlMemoryPool::Alloc()
  177. {
  178. return Alloc( m_BlockSize );
  179. }
  180. void* CUtlMemoryPool::AllocZero()
  181. {
  182. return AllocZero( m_BlockSize );
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Allocs a single block of memory from the pool.
  186. // Input : amount -
  187. //-----------------------------------------------------------------------------
  188. void *CUtlMemoryPool::Alloc( size_t amount )
  189. {
  190. void *returnBlock;
  191. if ( amount > (unsigned int)m_BlockSize )
  192. return NULL;
  193. if( !m_pHeadOfFreeList )
  194. {
  195. // returning NULL is fine in UTLMEMORYPOOL_GROW_NONE
  196. if( m_GrowMode == UTLMEMORYPOOL_GROW_NONE )
  197. {
  198. //Assert( !"CUtlMemoryPool::Alloc: tried to make new blob with UTLMEMORYPOOL_GROW_NONE" );
  199. return NULL;
  200. }
  201. // overflow
  202. AddNewBlob();
  203. // still failure, error out
  204. if( !m_pHeadOfFreeList )
  205. {
  206. Assert( !"CUtlMemoryPool::Alloc: ran out of memory" );
  207. return NULL;
  208. }
  209. }
  210. m_BlocksAllocated++;
  211. m_PeakAlloc = max(m_PeakAlloc, m_BlocksAllocated);
  212. returnBlock = m_pHeadOfFreeList;
  213. // move the pointer the next block
  214. m_pHeadOfFreeList = *((void**)m_pHeadOfFreeList);
  215. return returnBlock;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose: Allocs a single block of memory from the pool, zeroes the memory before returning
  219. // Input : amount -
  220. //-----------------------------------------------------------------------------
  221. void *CUtlMemoryPool::AllocZero( size_t amount )
  222. {
  223. void *mem = Alloc( amount );
  224. if ( mem )
  225. {
  226. V_memset( mem, 0x00, amount );
  227. }
  228. return mem;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Frees a block of memory
  232. // Input : *memBlock - the memory to free
  233. //-----------------------------------------------------------------------------
  234. void CUtlMemoryPool::Free( void *memBlock )
  235. {
  236. if ( !memBlock )
  237. return; // trying to delete NULL pointer, ignore
  238. #ifdef _DEBUG
  239. // check to see if the memory is from the allocated range
  240. bool bOK = false;
  241. for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext )
  242. {
  243. if (memBlock >= pCur->m_Data && (char*)memBlock < (pCur->m_Data + pCur->m_NumBytes))
  244. {
  245. bOK = true;
  246. }
  247. }
  248. Assert (bOK);
  249. #endif // _DEBUG
  250. #ifdef _DEBUG
  251. // invalidate the memory
  252. memset( memBlock, 0xDD, m_BlockSize );
  253. #endif
  254. m_BlocksAllocated--;
  255. // make the block point to the first item in the list
  256. *((void**)memBlock) = m_pHeadOfFreeList;
  257. // the list head is now the new block
  258. m_pHeadOfFreeList = memBlock;
  259. }