Counter Strike : Global Offensive Source Code
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.

347 lines
8.6 KiB

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