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.

234 lines
7.2 KiB

  1. //=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
  2. //
  3. // The copyright to the contents herein is the property of Valve, L.L.C.
  4. // The contents may be used and/or copied only with the written permission of
  5. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  6. // the agreement/contract under which the contents have been supplied.
  7. //
  8. // Purpose:
  9. //=============================================================================
  10. //#include "pch_vstdlib.h"
  11. #include "stdafx.h"
  12. #include "tier0/tslist.h"
  13. #include "tier0/t0constants.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. static const uint k_cubBytesAllocatedToConsiderFreeingMemory = 5 * k_nMegabyte;
  17. static const int k_cBlocksAllocatedToConsiderFreeingMemory = 10;
  18. typedef TSLNodeBase_t FreeListItem_t;
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Constructor
  21. //-----------------------------------------------------------------------------
  22. CThreadSafeMemoryPool::CThreadSafeMemoryPool( int blockSize, int numElements, int growMode )
  23. {
  24. m_ptslistFreeBlocks = new CTSListBase;
  25. // round up to the nearest 8-byte boundary
  26. if ( blockSize % TSLIST_NODE_ALIGNMENT != 0 )
  27. {
  28. blockSize += TSLIST_NODE_ALIGNMENT - (blockSize % TSLIST_NODE_ALIGNMENT);
  29. }
  30. Assert( blockSize % TSLIST_NODE_ALIGNMENT == 0 );
  31. Assert( blockSize > sizeof(FreeListItem_t) );
  32. m_nGrowMode = growMode;
  33. m_cubBlockSize = blockSize;
  34. m_nGrowSize = numElements;
  35. m_cubAllocated = 0;
  36. }
  37. //-----------------------------------------------------------------------------
  38. // Purpose: Frees the memory contained in the mempool
  39. //-----------------------------------------------------------------------------
  40. CThreadSafeMemoryPool::~CThreadSafeMemoryPool()
  41. {
  42. AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
  43. FOR_EACH_VEC( m_vecBlockSets, i )
  44. {
  45. _aligned_free( m_vecBlockSets[i].m_pubBlockSet );
  46. }
  47. delete m_ptslistFreeBlocks;
  48. }
  49. //-----------------------------------------------------------------------------
  50. // Purpose: Frees everything
  51. //-----------------------------------------------------------------------------
  52. void CThreadSafeMemoryPool::Clear()
  53. {
  54. AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
  55. ClearNoLock();
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose: Frees everything
  59. //-----------------------------------------------------------------------------
  60. void CThreadSafeMemoryPool::ClearNoLock()
  61. {
  62. FOR_EACH_VEC( m_vecBlockSets, i )
  63. {
  64. _aligned_free( m_vecBlockSets[i].m_pubBlockSet );
  65. }
  66. m_ptslistFreeBlocks->Detach();
  67. m_cubAllocated = 0;
  68. m_cBlocksInUse = 0;
  69. m_vecBlockSets.RemoveAll();
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Allocates a single block of memory from the pool.
  73. //-----------------------------------------------------------------------------
  74. void *CThreadSafeMemoryPool::Alloc()
  75. {
  76. return Alloc( m_cubBlockSize );
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose: Allocates a single block of memory from the pool.
  80. //-----------------------------------------------------------------------------
  81. void *CThreadSafeMemoryPool::Alloc( unsigned int amount )
  82. {
  83. // loop attempting to get memory
  84. // there appears to be a case where memory corruption can get this into an infinite loop
  85. // normally 1 or 2 attempts are necessary to get a block, so if we hit 1000 we know something is wrong
  86. int cAttempts = 1000;
  87. while ( --cAttempts )
  88. {
  89. // pull first from the free list
  90. m_threadRWLock.LockForRead();
  91. FreeListItem_t *pFreeListItem = m_ptslistFreeBlocks->Pop();
  92. if ( pFreeListItem )
  93. {
  94. m_threadRWLock.UnlockRead();
  95. m_cBlocksInUse++;
  96. return (void *)pFreeListItem;
  97. }
  98. m_threadRWLock.UnlockRead();
  99. // no free items, add a new block
  100. // switch from a read lock to a write lock
  101. AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
  102. // another thread may have allocated memory; try the free list again if so
  103. if ( m_ptslistFreeBlocks->Count() > 0 )
  104. continue;
  105. size_t cubBlob = m_nGrowSize * m_cubBlockSize;
  106. if ( m_nGrowMode == GROW_FAST )
  107. {
  108. cubBlob *= (m_vecBlockSets.Count() + 1);
  109. }
  110. // don't grow if we're told not to
  111. if ( m_nGrowMode == GROW_NONE && m_vecBlockSets.Count() == 1 )
  112. return NULL;
  113. // allocate, but we can fail
  114. byte *pBlobBase = (byte *)MemAlloc_AllocAligned( cubBlob, TSLIST_NODE_ALIGNMENT /*, (m_nGrowMode == GROW_TIL_YOU_CANT)*/ );
  115. if ( !pBlobBase )
  116. return NULL;
  117. byte *pBlobEnd = pBlobBase + cubBlob;
  118. // add all the items to the pool
  119. for ( byte *pBlob = pBlobBase; pBlob < pBlobEnd; pBlob += m_cubBlockSize )
  120. {
  121. m_ptslistFreeBlocks->Push( (FreeListItem_t *)pBlob );
  122. }
  123. m_cubAllocated += cubBlob;
  124. BlockSet_t blockSet = { pBlobBase, cubBlob };
  125. m_vecBlockSets.AddToTail( blockSet );
  126. }
  127. return NULL;
  128. }
  129. //-----------------------------------------------------------------------------
  130. // Purpose: Frees a block of memory
  131. //-----------------------------------------------------------------------------
  132. void CThreadSafeMemoryPool::Free( void *pMem )
  133. {
  134. Free( pMem, m_cubBlockSize );
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Frees a block of memory
  138. //-----------------------------------------------------------------------------
  139. void CThreadSafeMemoryPool::Free( void *pMem, int cubAlloc )
  140. {
  141. m_threadRWLock.LockForRead();
  142. // push the item back onto the free list
  143. m_ptslistFreeBlocks->Push( (FreeListItem_t *)pMem );
  144. m_cBlocksInUse--;
  145. m_threadRWLock.UnlockRead();
  146. // if we're completely free, and have too much memory allocated, free some
  147. if ( m_cBlocksInUse == 0
  148. && m_cubAllocated >= k_cubBytesAllocatedToConsiderFreeingMemory
  149. && m_vecBlockSets.Count() >= k_cBlocksAllocatedToConsiderFreeingMemory )
  150. {
  151. AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
  152. // check again nothing is in use
  153. if ( m_cBlocksInUse == 0 )
  154. {
  155. // free all the blocks
  156. ClearNoLock();
  157. }
  158. }
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: display
  162. //-----------------------------------------------------------------------------
  163. void CThreadSafeMemoryPool::PrintStats()
  164. {
  165. AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
  166. int cBlocksInUse = m_cBlocksInUse;
  167. Msg( "Block size: %-11s Alloc'd: %8d Num blobs: %5d (%s)\n", Q_pretifymem( m_cubBlockSize, 2, true ),
  168. cBlocksInUse, m_vecBlockSets.Count(), Q_pretifymem( m_cubAllocated, 2, true ) );
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: data accessor
  172. //-----------------------------------------------------------------------------
  173. size_t CThreadSafeMemoryPool::CubTotalSize()
  174. {
  175. return m_cubAllocated;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: data accessor
  179. //-----------------------------------------------------------------------------
  180. size_t CThreadSafeMemoryPool::CubSizeInUse()
  181. {
  182. return m_cBlocksInUse * m_cubBlockSize;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: data accessor
  186. //-----------------------------------------------------------------------------
  187. int CThreadSafeMemoryPool::Count()
  188. {
  189. return m_cBlocksInUse;
  190. }