|
|
//=========== (C) Copyright 2000 Valve, L.L.C. 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; }
|