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.

351 lines
12 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include <stdafx.h>
  9. #include "tier0/t0constants.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. static const int k_cubMemBlockPrefixSize = sizeof(uint32);
  13. #define ALLOCSIZE_TO_LOOKUP( cubAlloc ) ( (cubAlloc - 1) >> 5 )
  14. #define LOOKUP_TO_ALLOCSIZE( iLookup ) ( (iLookup << 5) + 1 )
  15. //-----------------------------------------------------------------------------
  16. // Purpose: constructor, the sizes in pMemPoolConfig must be in ascending order
  17. //-----------------------------------------------------------------------------
  18. CThreadSafeMultiMemoryPool::CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pMemPoolConfig, int cnMemPoolConfig, int nGrowMode /*= GROW_FAST*/ )
  19. {
  20. m_cubReallocedTotal = 0;
  21. m_MapRawAllocation.SetLessFunc( DefLessFunc( void * ) );
  22. for ( int iMemPoolConfig = 0; iMemPoolConfig < cnMemPoolConfig; iMemPoolConfig++ )
  23. {
  24. MemPoolRecord_t memPoolRecord;
  25. // verify that the mem pool sizes are in ascending order
  26. Assert( iMemPoolConfig == 0 || ( iMemPoolConfig > 0 && pMemPoolConfig[ iMemPoolConfig - 1 ].m_cubBlockSize < pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize ) );
  27. AssertMsg( pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize % 32 == 0, "Mempools sizes must be multiples of 32" );
  28. // add an int to the block size so we can note the alloc size
  29. memPoolRecord.m_pMemPool = new CThreadSafeMemoryPool( pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize + k_cubMemBlockPrefixSize,
  30. pMemPoolConfig[ iMemPoolConfig ].m_cubDefaultPoolSize, nGrowMode );
  31. Assert( memPoolRecord.m_pMemPool );
  32. memPoolRecord.m_nBlockSize = pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize;
  33. m_VecMemPool.AddToTail( memPoolRecord );
  34. // update the largest blocksize
  35. m_nBlockSizeMax = MAX( m_nBlockSizeMax, memPoolRecord.m_nBlockSize );
  36. }
  37. // build the lookup table
  38. int nLookupMax = m_nBlockSizeMax >> 5;
  39. m_VecMemPoolLookup.AddMultipleToTail( nLookupMax );
  40. for ( int i = 0; i < nLookupMax; i++ )
  41. {
  42. uint32 cubAllocSize = LOOKUP_TO_ALLOCSIZE( i );
  43. for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  44. {
  45. if ( m_VecMemPool[iMemPool].m_nBlockSize >= cubAllocSize )
  46. {
  47. m_VecMemPoolLookup[i] = &m_VecMemPool[iMemPool];
  48. break;
  49. }
  50. }
  51. }
  52. #if defined(_DEBUG)
  53. // validate the lookup table
  54. for ( int i = 1; i < (int)m_nBlockSizeMax; i++ )
  55. {
  56. for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  57. {
  58. if ( (int)m_VecMemPool[iMemPool].m_nBlockSize >= i )
  59. {
  60. AssertMsg( m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP(i)] == &m_VecMemPool[iMemPool], "Invalid mempool block size, can't generate lookup table" );
  61. break;
  62. }
  63. }
  64. }
  65. #endif // _DEBUG
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose: destructor
  69. //-----------------------------------------------------------------------------
  70. CThreadSafeMultiMemoryPool::~CThreadSafeMultiMemoryPool()
  71. {
  72. AUTO_LOCK( m_mutexRawAllocations );
  73. for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool ++ )
  74. {
  75. delete m_VecMemPool[iMemPool].m_pMemPool;
  76. }
  77. FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
  78. {
  79. FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
  80. }
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose: Allocates a block of memory at of least nAllocSize bytes
  84. // Input : nAllocSize - number of bytes to alloc
  85. // Output : pointer to memory alloc'd, NULL on error
  86. //-----------------------------------------------------------------------------
  87. void *CThreadSafeMultiMemoryPool::Alloc( uint32 cubAllocSize )
  88. {
  89. if ( cubAllocSize == 0 )
  90. return NULL;
  91. if ( cubAllocSize <= m_nBlockSizeMax )
  92. {
  93. MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
  94. void *pvMem = pMemPoolRecord->m_pMemPool->Alloc( cubAllocSize + k_cubMemBlockPrefixSize );
  95. *(uint32 *)pvMem = cubAllocSize;
  96. return ( (char *)pvMem + k_cubMemBlockPrefixSize );
  97. }
  98. // can't fit in our mem pools, alloc it in our one off buffer
  99. RawAllocation_t rawAllocation;
  100. rawAllocation.m_nBlockSize = cubAllocSize;
  101. rawAllocation.m_pvMem = PvAlloc( cubAllocSize + k_cubMemBlockPrefixSize );
  102. if ( !rawAllocation.m_pvMem )
  103. {
  104. return NULL;
  105. }
  106. *(uint32 *)rawAllocation.m_pvMem = rawAllocation.m_nBlockSize;
  107. AUTO_LOCK( m_mutexRawAllocations );
  108. m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
  109. return ( (char *)rawAllocation.m_pvMem + k_cubMemBlockPrefixSize );
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Free a previously alloc'd block
  113. // Input : pMem - memory to free
  114. //-----------------------------------------------------------------------------
  115. void CThreadSafeMultiMemoryPool::Free( void *pvMem )
  116. {
  117. if ( !pvMem )
  118. return;
  119. uint32 cubAllocSize = *( (uint32 *)pvMem - 1 );
  120. if ( cubAllocSize <= m_nBlockSizeMax )
  121. {
  122. MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
  123. pMemPoolRecord->m_pMemPool->Free( (char *)pvMem - k_cubMemBlockPrefixSize, cubAllocSize + k_cubMemBlockPrefixSize );
  124. return;
  125. }
  126. AUTO_LOCK( m_mutexRawAllocations );
  127. // must have been alloc'd from the raw heap, find it in map
  128. void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
  129. int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
  130. if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
  131. {
  132. AssertMsg3( false, "CThreadSafeMultiMemoryPool::Free: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
  133. pvMem, pvAllocedMem, cubAllocSize );
  134. return;
  135. }
  136. FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
  137. m_MapRawAllocation.RemoveAt( iRawAllocation);
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose: Return the size alloc'd for this block
  141. // Input : pMem - memory to report
  142. // Output : size in bytes of this memory
  143. //-----------------------------------------------------------------------------
  144. int CThreadSafeMultiMemoryPool::CubAllocSize(void *pvMem)
  145. {
  146. if ( !pvMem )
  147. {
  148. return -1;
  149. }
  150. return *(((uint32 *)pvMem) -1);
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose: Frees all previously alloc'd memory
  154. //-----------------------------------------------------------------------------
  155. void CThreadSafeMultiMemoryPool::Clear()
  156. {
  157. AUTO_LOCK( m_mutexRawAllocations );
  158. for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  159. {
  160. m_VecMemPool[iMemPool].m_pMemPool->Clear();
  161. }
  162. FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
  163. {
  164. FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
  165. }
  166. m_MapRawAllocation.RemoveAll();
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose: print to the console info about our storage
  170. //-----------------------------------------------------------------------------
  171. void CThreadSafeMultiMemoryPool::PrintStats()
  172. {
  173. for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  174. {
  175. m_VecMemPool[iMemPool].m_pMemPool->PrintStats();
  176. }
  177. int cubRawBytesAllocd = 0;
  178. AUTO_LOCK( m_mutexRawAllocations );
  179. FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
  180. {
  181. cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
  182. }
  183. Msg( "Raw bytes alloc'd: %s\n", Q_pretifymem( cubRawBytesAllocd, 2, true ) );
  184. Msg( "Cumulative bytes re-alloced: %s\n", Q_pretifymem( m_cubReallocedTotal, 2, true ) );
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: return the total mem alloced by this pool in MB
  188. //-----------------------------------------------------------------------------
  189. int CThreadSafeMultiMemoryPool::CMBPoolSize() const
  190. {
  191. uint64 cubRawBytesAllocd = 0;
  192. for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  193. {
  194. cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubTotalSize() );
  195. }
  196. AUTO_LOCK( m_mutexRawAllocations );
  197. FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
  198. {
  199. cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
  200. }
  201. return ( cubRawBytesAllocd / k_nMegabyte );
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose: return the total mem alloced by this pool in MB
  205. //-----------------------------------------------------------------------------
  206. int CThreadSafeMultiMemoryPool::CMBPoolSizeInUse() const
  207. {
  208. uint64 cubRawBytesAllocd = 0;
  209. for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  210. {
  211. cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubSizeInUse() );
  212. }
  213. AUTO_LOCK( m_mutexRawAllocations );
  214. FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
  215. {
  216. cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
  217. }
  218. return ( cubRawBytesAllocd / k_nMegabyte );
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: return number of mempool blocks alloc'd
  222. //-----------------------------------------------------------------------------
  223. int CThreadSafeMultiMemoryPool::Count()
  224. {
  225. int cCount = 0;
  226. for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
  227. {
  228. cCount += m_VecMemPool[iMemPool].m_pMemPool->Count();
  229. }
  230. return cCount;
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: reallocate an existing block of memory to a new size (and copy the data
  234. // Input: pvMem - a pointer to the existing memory
  235. // cubAlloc - number of bytes to alloc
  236. // Output: returns a pointer to the memory allocated (NULL on error)
  237. //-----------------------------------------------------------------------------
  238. void *CThreadSafeMultiMemoryPool::ReAlloc( void *pvMem, uint32 cubAlloc )
  239. {
  240. uint32 cubOldAlloc = CubAllocSize(pvMem);
  241. if ( pvMem && cubAlloc <= cubOldAlloc )
  242. return pvMem;
  243. if ( cubOldAlloc > m_nBlockSizeMax )
  244. {
  245. AUTO_LOCK( m_mutexRawAllocations );
  246. // okay, must have been alloc'd from the raw heap, search for it
  247. void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
  248. int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
  249. if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
  250. {
  251. AssertMsg3( false, "CThreadSafeMultiMemoryPool::ReAlloc: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
  252. pvMem, pvAllocedMem, cubOldAlloc );
  253. return NULL;
  254. }
  255. // realloc the memory
  256. void *pvNewMem = PvRealloc( pvAllocedMem, cubAlloc + k_cubMemBlockPrefixSize );
  257. if ( !pvNewMem )
  258. {
  259. m_MapRawAllocation.RemoveAt( iRawAllocation );
  260. return NULL;
  261. }
  262. // update our tracking
  263. *(uint32 *)pvNewMem = cubAlloc;
  264. if ( pvAllocedMem == pvNewMem )
  265. {
  266. // if pointer is the same, use the same map entry with the same key (the pointer given to caller)
  267. m_MapRawAllocation[iRawAllocation].m_pvMem = pvNewMem;
  268. m_MapRawAllocation[iRawAllocation].m_nBlockSize = cubAlloc;
  269. }
  270. else
  271. {
  272. // if pointer changed, need to remove the old entry and re-insert with new key
  273. m_MapRawAllocation.RemoveAt( iRawAllocation );
  274. RawAllocation_t rawAllocation;
  275. rawAllocation.m_pvMem = pvNewMem;
  276. rawAllocation.m_nBlockSize = cubAlloc;
  277. m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
  278. }
  279. return ( (char *)pvNewMem + k_cubMemBlockPrefixSize );
  280. }
  281. else
  282. {
  283. // see if we can stay in the same block
  284. MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubOldAlloc )];
  285. if ( cubAlloc <= pMemPoolRecord->m_nBlockSize )
  286. {
  287. // re-assign the size
  288. *((uint32 *)pvMem - 1) = cubAlloc;
  289. return pvMem;
  290. }
  291. void *pvNewMem = Alloc( cubAlloc );
  292. if ( !pvNewMem )
  293. {
  294. return NULL;
  295. }
  296. m_cubReallocedTotal += cubOldAlloc;
  297. Q_memcpy( pvNewMem, pvMem, cubOldAlloc );
  298. Free( pvMem ); // now free the old memory buffer we had
  299. return pvNewMem;
  300. }
  301. }