diff options
Diffstat (limited to 'gcsdk/steamextra/tier1/tsmempool.cpp')
| -rw-r--r-- | gcsdk/steamextra/tier1/tsmempool.cpp | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/gcsdk/steamextra/tier1/tsmempool.cpp b/gcsdk/steamextra/tier1/tsmempool.cpp new file mode 100644 index 0000000..71b0f5b --- /dev/null +++ b/gcsdk/steamextra/tier1/tsmempool.cpp @@ -0,0 +1,255 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +//============================================================================= + + +//#include "pch_vstdlib.h" + +#include "stdafx.h" +#include "tier0/tslist.h" +#include "tier0/t0constants.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static const uint k_cubBytesAllocatedToConsiderFreeingMemory = 5 * k_nMegabyte; +static const int k_cBlocksAllocatedToConsiderFreeingMemory = 10; + +typedef TSLNodeBase_t FreeListItem_t; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CThreadSafeMemoryPool::CThreadSafeMemoryPool( int blockSize, int numElements, int growMode ) +{ + m_ptslistFreeBlocks = new CTSListBase; + + // round up to the nearest 8-byte boundary + if ( blockSize % TSLIST_NODE_ALIGNMENT != 0 ) + { + blockSize += TSLIST_NODE_ALIGNMENT - (blockSize % TSLIST_NODE_ALIGNMENT); + } + Assert( blockSize % TSLIST_NODE_ALIGNMENT == 0 ); + Assert( blockSize > sizeof(FreeListItem_t) ); + m_nGrowMode = growMode; + m_cubBlockSize = blockSize; + m_nGrowSize = numElements; + m_cubAllocated = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees the memory contained in the mempool +//----------------------------------------------------------------------------- +CThreadSafeMemoryPool::~CThreadSafeMemoryPool() +{ + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + FOR_EACH_VEC( m_vecBlockSets, i ) + { + _aligned_free( m_vecBlockSets[i].m_pubBlockSet ); + } + + delete m_ptslistFreeBlocks; +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees everything +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::Clear() +{ + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + ClearNoLock(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees everything +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::ClearNoLock() +{ + FOR_EACH_VEC( m_vecBlockSets, i ) + { + _aligned_free( m_vecBlockSets[i].m_pubBlockSet ); + } + m_ptslistFreeBlocks->Detach(); + m_cubAllocated = 0; + m_cBlocksInUse = 0; + m_vecBlockSets.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocates a single block of memory from the pool. +//----------------------------------------------------------------------------- +void *CThreadSafeMemoryPool::Alloc() +{ + return Alloc( m_cubBlockSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocates a single block of memory from the pool. +//----------------------------------------------------------------------------- +void *CThreadSafeMemoryPool::Alloc( unsigned int amount ) +{ + // loop attempting to get memory + // there appears to be a case where memory corruption can get this into an infinite loop + // normally 1 or 2 attempts are necessary to get a block, so if we hit 1000 we know something is wrong + int cAttempts = 1000; + while ( --cAttempts ) + { + // pull first from the free list + m_threadRWLock.LockForRead(); + FreeListItem_t *pFreeListItem = m_ptslistFreeBlocks->Pop(); + if ( pFreeListItem ) + { + m_threadRWLock.UnlockRead(); + m_cBlocksInUse++; + return (void *)pFreeListItem; + } + m_threadRWLock.UnlockRead(); + + // no free items, add a new block + + // switch from a read lock to a write lock + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + + // another thread may have allocated memory; try the free list again if so + if ( m_ptslistFreeBlocks->Count() > 0 ) + continue; + + size_t cubBlob = m_nGrowSize * m_cubBlockSize; + if ( m_nGrowMode == GROW_FAST ) + { + cubBlob *= (m_vecBlockSets.Count() + 1); + } + + // don't grow if we're told not to + if ( m_nGrowMode == GROW_NONE && m_vecBlockSets.Count() == 1 ) + return NULL; + + // allocate, but we can fail + byte *pBlobBase = (byte *)MemAlloc_AllocAligned( cubBlob, TSLIST_NODE_ALIGNMENT /*, (m_nGrowMode == GROW_TIL_YOU_CANT)*/ ); + if ( !pBlobBase ) + return NULL; + + byte *pBlobEnd = pBlobBase + cubBlob; + // add all the items to the pool + for ( byte *pBlob = pBlobBase; pBlob < pBlobEnd; pBlob += m_cubBlockSize ) + { + m_ptslistFreeBlocks->Push( (FreeListItem_t *)pBlob ); + } + + m_cubAllocated += cubBlob; + BlockSet_t blockSet = { pBlobBase, cubBlob }; + m_vecBlockSets.AddToTail( blockSet ); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees a block of memory +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::Free( void *pMem ) +{ + Free( pMem, m_cubBlockSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees a block of memory +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::Free( void *pMem, int cubAlloc ) +{ + m_threadRWLock.LockForRead(); + + // push the item back onto the free list + m_ptslistFreeBlocks->Push( (FreeListItem_t *)pMem ); + m_cBlocksInUse--; + + m_threadRWLock.UnlockRead(); + + // if we're completely free, and have too much memory allocated, free some + if ( m_cBlocksInUse == 0 + && m_cubAllocated >= k_cubBytesAllocatedToConsiderFreeingMemory + && m_vecBlockSets.Count() >= k_cBlocksAllocatedToConsiderFreeingMemory ) + { + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + + // check again nothing is in use + if ( m_cBlocksInUse == 0 ) + { + // free all the blocks + ClearNoLock(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: display +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::PrintStats() +{ + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + int cBlocksInUse = m_cBlocksInUse; + Msg( "Block size: %-11s Alloc'd: %8d Num blobs: %5d (%s)\n", Q_pretifymem( m_cubBlockSize, 2, true ), + cBlocksInUse, m_vecBlockSets.Count(), Q_pretifymem( m_cubAllocated, 2, true ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +size_t CThreadSafeMemoryPool::CubTotalSize() +{ + return m_cubAllocated; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +size_t CThreadSafeMemoryPool::CubSizeInUse() +{ + return m_cBlocksInUse * m_cubBlockSize; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int CThreadSafeMemoryPool::Count() +{ + return m_cBlocksInUse; +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: Run a global validation pass on all of our data structures and memory +// allocations. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +void CThreadSafeMemoryPool::Validate( CValidator &validator, const char *pchName ) +{ + AUTO_LOCK_SPIN_WRITE( m_threadRWLock ); + VALIDATE_SCOPE(); + FOR_EACH_VEC( m_vecBlockSets, i ) + { + validator.ClaimMemory( MemAlloc_Unalign( m_vecBlockSets[i].m_pubBlockSet ) ); + } + ValidateObj( m_vecBlockSets ); + validator.ClaimMemory( MemAlloc_Unalign( m_ptslistFreeBlocks ) ); +} +#endif // DBGFLAG_VALIDATE |