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.
1385 lines
37 KiB
1385 lines
37 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "datacache/idatacache.h" |
|
|
|
#ifdef _LINUX |
|
#include <malloc.h> |
|
#endif |
|
|
|
#include "tier0/vprof.h" |
|
#include "basetypes.h" |
|
#include "convar.h" |
|
#include "interface.h" |
|
#include "datamanager.h" |
|
#include "utlrbtree.h" |
|
#include "utlhash.h" |
|
#include "utlmap.h" |
|
#include "generichash.h" |
|
#include "filesystem.h" |
|
#include "datacache.h" |
|
#include "utlvector.h" |
|
#include "fmtstr.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
//----------------------------------------------------------------------------- |
|
CDataCache g_DataCache; |
|
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CDataCache, IDataCache, DATACACHE_INTERFACE_VERSION, g_DataCache ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Data Cache class implemenations |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Console commands |
|
//----------------------------------------------------------------------------- |
|
ConVar developer( "developer", "0", FCVAR_INTERNAL_USE ); |
|
static ConVar mem_force_flush( "mem_force_flush", "0", FCVAR_CHEAT, "Force cache flush of unlocked resources on every alloc" ); |
|
static int g_iDontForceFlush; |
|
|
|
//----------------------------------------------------------------------------- |
|
// DataCacheItem_t |
|
//----------------------------------------------------------------------------- |
|
|
|
DEFINE_FIXEDSIZE_ALLOCATOR_MT( DataCacheItem_t, 4096/sizeof(DataCacheItem_t), CUtlMemoryPool::GROW_SLOW ); |
|
|
|
void DataCacheItem_t::DestroyResource() |
|
{ |
|
if ( pSection ) |
|
{ |
|
pSection->DiscardItemData( this, DC_AGE_DISCARD ); |
|
} |
|
delete this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CDataCacheSection |
|
//----------------------------------------------------------------------------- |
|
|
|
CDataCacheSection::CDataCacheSection( CDataCache *pSharedCache, IDataCacheClient *pClient, const char *pszName ) |
|
: m_pClient( pClient ), |
|
m_LRU( pSharedCache->m_LRU ), |
|
m_mutex( pSharedCache->m_mutex ), |
|
m_pSharedCache( pSharedCache ), |
|
m_nFrameUnlockCounter( 0 ), |
|
m_options( 0 ) |
|
{ |
|
memset( &m_status, 0, sizeof(m_status) ); |
|
AssertMsg1( strlen(pszName) <= DC_MAX_CLIENT_NAME, "Cache client name too long \"%s\"", pszName ); |
|
Q_strncpy( szName, pszName, sizeof(szName) ); |
|
|
|
for ( int i = 0; i < DC_MAX_THREADS_FRAMELOCKED; i++ ) |
|
{ |
|
FrameLock_t *pFrameLock = new FrameLock_t; |
|
pFrameLock->m_iThread = i; |
|
m_FreeFrameLocks.Push( pFrameLock ); |
|
} |
|
} |
|
|
|
CDataCacheSection::~CDataCacheSection() |
|
{ |
|
FrameLock_t *pFrameLock; |
|
while ( ( pFrameLock = m_FreeFrameLocks.Pop() ) != NULL ) |
|
{ |
|
delete pFrameLock; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Controls cache size. |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::SetLimits( const DataCacheLimits_t &limits ) |
|
{ |
|
m_limits = limits; |
|
AssertMsg( m_limits.nMinBytes == 0 && m_limits.nMinItems == 0, "Cache minimums not yet implemented" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
const DataCacheLimits_t &CDataCacheSection::GetLimits() |
|
{ |
|
return m_limits; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Controls cache options. |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::SetOptions( unsigned options ) |
|
{ |
|
m_options = options; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the current state of the section |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits ) |
|
{ |
|
if ( pStatus ) |
|
{ |
|
*pStatus = m_status; |
|
} |
|
|
|
if ( pLimits ) |
|
{ |
|
*pLimits = m_limits; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::EnsureCapacity( unsigned nBytes, unsigned nItems ) |
|
{ |
|
VPROF( "CDataCacheSection::EnsureCapacity" ); |
|
|
|
if ( m_limits.nMaxItems != (unsigned)-1 || m_limits.nMaxBytes != (unsigned)-1 ) |
|
{ |
|
unsigned nNewSectionBytes = GetNumBytes() + nBytes; |
|
|
|
if ( nNewSectionBytes > m_limits.nMaxBytes ) |
|
{ |
|
Purge( nNewSectionBytes - m_limits.nMaxBytes ); |
|
} |
|
|
|
if ( GetNumItems() >= m_limits.nMaxItems ) |
|
{ |
|
PurgeItems( ( GetNumItems() - m_limits.nMaxItems ) + 1 ); |
|
} |
|
} |
|
|
|
m_pSharedCache->EnsureCapacity( nBytes ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::Add( DataCacheClientID_t clientId, const void *pItemData, unsigned size, DataCacheHandle_t *pHandle ) |
|
{ |
|
return AddEx( clientId, pItemData, size, DCAF_DEFAULT, pHandle ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::AddEx( DataCacheClientID_t clientId, const void *pItemData, unsigned size, unsigned flags, DataCacheHandle_t *pHandle ) |
|
{ |
|
VPROF( "CDataCacheSection::Add" ); |
|
|
|
if ( mem_force_flush.GetBool() ) |
|
{ |
|
m_pSharedCache->Flush(); |
|
} |
|
|
|
if ( ( m_options & DC_VALIDATE ) && Find( clientId ) ) |
|
{ |
|
Error( "Duplicate add to data cache\n" ); |
|
return false; |
|
} |
|
|
|
EnsureCapacity( size ); |
|
|
|
DataCacheItemData_t itemData = |
|
{ |
|
pItemData, |
|
size, |
|
clientId, |
|
this |
|
}; |
|
|
|
memhandle_t hMem = m_LRU.CreateResource( itemData, true ); |
|
|
|
Assert( hMem != (memhandle_t)0 && hMem != (memhandle_t)DC_INVALID_HANDLE ); |
|
|
|
AccessItem( hMem )->hLRU = hMem; |
|
|
|
if ( pHandle ) |
|
{ |
|
*pHandle = (DataCacheHandle_t)hMem; |
|
} |
|
|
|
NoteAdd( size ); |
|
|
|
OnAdd( clientId, (DataCacheHandle_t)hMem ); |
|
|
|
g_iDontForceFlush++; |
|
|
|
if ( flags & DCAF_LOCK ) |
|
{ |
|
Lock( (DataCacheHandle_t)hMem ); |
|
} |
|
// Add implies a frame lock. A no-op if not in frame lock |
|
FrameLock( (DataCacheHandle_t)hMem ); |
|
|
|
g_iDontForceFlush--; |
|
|
|
m_LRU.UnlockResource( hMem ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Finds an item in the cache, returns NULL if item is not in cache. |
|
//----------------------------------------------------------------------------- |
|
DataCacheHandle_t CDataCacheSection::Find( DataCacheClientID_t clientId ) |
|
{ |
|
VPROF( "CDataCacheSection::Find" ); |
|
|
|
m_status.nFindRequests++; |
|
|
|
DataCacheHandle_t hResult = DoFind( clientId ); |
|
|
|
if ( hResult != DC_INVALID_HANDLE ) |
|
{ |
|
m_status.nFindHits++; |
|
} |
|
|
|
return hResult; |
|
} |
|
|
|
//--------------------------------------------------------- |
|
DataCacheHandle_t CDataCacheSection::DoFind( DataCacheClientID_t clientId ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
memhandle_t hCurrent; |
|
|
|
hCurrent = GetFirstUnlockedItem(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
if ( AccessItem( hCurrent )->clientId == clientId ) |
|
{ |
|
m_status.nFindHits++; |
|
return (DataCacheHandle_t)hCurrent; |
|
} |
|
hCurrent = GetNextItem( hCurrent ); |
|
} |
|
|
|
hCurrent = GetFirstLockedItem(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
if ( AccessItem( hCurrent )->clientId == clientId ) |
|
{ |
|
m_status.nFindHits++; |
|
return (DataCacheHandle_t)hCurrent; |
|
} |
|
hCurrent = GetNextItem( hCurrent ); |
|
} |
|
|
|
return DC_INVALID_HANDLE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get an item out of the cache and remove it. No callbacks are executed. |
|
//----------------------------------------------------------------------------- |
|
DataCacheRemoveResult_t CDataCacheSection::Remove( DataCacheHandle_t handle, const void **ppItemData, unsigned *pItemSize, bool bNotify ) |
|
{ |
|
VPROF( "CDataCacheSection::Remove" ); |
|
|
|
if ( handle != DC_INVALID_HANDLE ) |
|
{ |
|
memhandle_t lruHandle = (memhandle_t)handle; |
|
if ( m_LRU.LockCount( lruHandle ) > 0 ) |
|
{ |
|
return DC_LOCKED; |
|
} |
|
|
|
AUTO_LOCK( m_mutex ); |
|
|
|
DataCacheItem_t *pItem = AccessItem( lruHandle ); |
|
if ( pItem ) |
|
{ |
|
if ( ppItemData ) |
|
{ |
|
*ppItemData = pItem->pItemData; |
|
} |
|
|
|
if ( pItemSize ) |
|
{ |
|
*pItemSize = pItem->size; |
|
} |
|
|
|
DiscardItem( lruHandle, ( bNotify ) ? DC_REMOVED : DC_NONE ); |
|
|
|
return DC_OK; |
|
} |
|
} |
|
|
|
return DC_NOT_FOUND; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::IsPresent( DataCacheHandle_t handle ) |
|
{ |
|
return ( m_LRU.GetResource_NoLockNoLRUTouch( (memhandle_t)handle ) != NULL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Lock an item in the cache, returns NULL if item is not in the cache. |
|
//----------------------------------------------------------------------------- |
|
void *CDataCacheSection::Lock( DataCacheHandle_t handle ) |
|
{ |
|
VPROF( "CDataCacheSection::Lock" ); |
|
|
|
if ( mem_force_flush.GetBool() && !g_iDontForceFlush) |
|
Flush(); |
|
|
|
if ( handle != DC_INVALID_HANDLE ) |
|
{ |
|
DataCacheItem_t *pItem = m_LRU.LockResource( (memhandle_t)handle ); |
|
if ( pItem ) |
|
{ |
|
if ( m_LRU.LockCount( (memhandle_t)handle ) == 1 ) |
|
{ |
|
NoteLock( pItem->size ); |
|
} |
|
return const_cast<void *>(pItem->pItemData); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Unlock a previous lock. |
|
//----------------------------------------------------------------------------- |
|
int CDataCacheSection::Unlock( DataCacheHandle_t handle ) |
|
{ |
|
VPROF( "CDataCacheSection::Unlock" ); |
|
|
|
int iNewLockCount = 0; |
|
if ( handle != DC_INVALID_HANDLE ) |
|
{ |
|
AssertMsg( AccessItem( (memhandle_t)handle ) != NULL, "Attempted to unlock nonexistent cache entry" ); |
|
unsigned nBytesUnlocked = 0; |
|
m_mutex.Lock(); |
|
iNewLockCount = m_LRU.UnlockResource( (memhandle_t)handle ); |
|
if ( iNewLockCount == 0 ) |
|
{ |
|
nBytesUnlocked = AccessItem( (memhandle_t)handle )->size; |
|
} |
|
m_mutex.Unlock(); |
|
if ( nBytesUnlocked ) |
|
{ |
|
NoteUnlock( nBytesUnlocked ); |
|
EnsureCapacity( 0 ); |
|
} |
|
} |
|
return iNewLockCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Lock the mutex |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::LockMutex() |
|
{ |
|
g_iDontForceFlush++; |
|
m_mutex.Lock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Unlock the mutex |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::UnlockMutex() |
|
{ |
|
g_iDontForceFlush--; |
|
m_mutex.Unlock(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get without locking |
|
//----------------------------------------------------------------------------- |
|
void *CDataCacheSection::Get( DataCacheHandle_t handle, bool bFrameLock ) |
|
{ |
|
VPROF( "CDataCacheSection::Get" ); |
|
|
|
if ( mem_force_flush.GetBool() && !g_iDontForceFlush) |
|
Flush(); |
|
|
|
if ( handle != DC_INVALID_HANDLE ) |
|
{ |
|
if ( bFrameLock && IsFrameLocking() ) |
|
return FrameLock( handle ); |
|
|
|
AUTO_LOCK( m_mutex ); |
|
DataCacheItem_t *pItem = m_LRU.GetResource_NoLock( (memhandle_t)handle ); |
|
if ( pItem ) |
|
{ |
|
return const_cast<void *>( pItem->pItemData ); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get without locking |
|
//----------------------------------------------------------------------------- |
|
void *CDataCacheSection::GetNoTouch( DataCacheHandle_t handle, bool bFrameLock ) |
|
{ |
|
VPROF( "CDataCacheSection::GetNoTouch" ); |
|
|
|
if ( handle != DC_INVALID_HANDLE ) |
|
{ |
|
if ( bFrameLock && IsFrameLocking() ) |
|
return FrameLock( handle ); |
|
|
|
AUTO_LOCK( m_mutex ); |
|
DataCacheItem_t *pItem = m_LRU.GetResource_NoLockNoLRUTouch( (memhandle_t)handle ); |
|
if ( pItem ) |
|
{ |
|
return const_cast<void *>( pItem->pItemData ); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: "Frame locking" (not game frame). A crude way to manage locks over relatively |
|
// short periods. Does not affect normal locks/unlocks |
|
//----------------------------------------------------------------------------- |
|
int CDataCacheSection::BeginFrameLocking() |
|
{ |
|
FrameLock_t *pFrameLock = m_ThreadFrameLock.Get(); |
|
if ( pFrameLock ) |
|
{ |
|
pFrameLock->m_iLock++; |
|
} |
|
else |
|
{ |
|
while ( ( pFrameLock = m_FreeFrameLocks.Pop() ) == NULL ) |
|
{ |
|
ThreadPause(); |
|
ThreadSleep( 1 ); |
|
} |
|
pFrameLock->m_iLock = 1; |
|
pFrameLock->m_pFirst = NULL; |
|
m_ThreadFrameLock.Set( pFrameLock ); |
|
} |
|
return pFrameLock->m_iLock; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::IsFrameLocking() |
|
{ |
|
FrameLock_t *pFrameLock = m_ThreadFrameLock.Get(); |
|
return ( pFrameLock != NULL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void *CDataCacheSection::FrameLock( DataCacheHandle_t handle ) |
|
{ |
|
VPROF( "CDataCacheSection::FrameLock" ); |
|
|
|
if ( mem_force_flush.GetBool() && !g_iDontForceFlush) |
|
Flush(); |
|
|
|
void *pResult = NULL; |
|
FrameLock_t *pFrameLock = m_ThreadFrameLock.Get(); |
|
if ( pFrameLock ) |
|
{ |
|
DataCacheItem_t *pItem = m_LRU.LockResource( (memhandle_t)handle ); |
|
|
|
if ( pItem ) |
|
{ |
|
int iThread = pFrameLock->m_iThread; |
|
if ( pItem->pNextFrameLocked[iThread] == DC_NO_NEXT_LOCKED ) |
|
{ |
|
pItem->pNextFrameLocked[iThread] = pFrameLock->m_pFirst; |
|
pFrameLock->m_pFirst = pItem; |
|
Lock( handle ); |
|
} |
|
|
|
pResult = const_cast<void *>(pItem->pItemData); |
|
m_LRU.UnlockResource( (memhandle_t)handle ); |
|
} |
|
} |
|
|
|
return pResult; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
int CDataCacheSection::EndFrameLocking() |
|
{ |
|
FrameLock_t *pFrameLock = m_ThreadFrameLock.Get(); |
|
Assert( pFrameLock->m_iLock > 0 ); |
|
|
|
if ( pFrameLock->m_iLock == 1 ) |
|
{ |
|
VPROF( "CDataCacheSection::EndFrameLocking" ); |
|
|
|
DataCacheItem_t *pItem = pFrameLock->m_pFirst; |
|
DataCacheItem_t *pNext; |
|
int iThread = pFrameLock->m_iThread; |
|
while ( pItem ) |
|
{ |
|
pNext = pItem->pNextFrameLocked[iThread]; |
|
pItem->pNextFrameLocked[iThread] = DC_NO_NEXT_LOCKED; |
|
Unlock( pItem->hLRU ); |
|
pItem = pNext; |
|
} |
|
|
|
m_FreeFrameLocks.Push( pFrameLock ); |
|
m_ThreadFrameLock.Set( NULL ); |
|
return 0; |
|
} |
|
else |
|
{ |
|
pFrameLock->m_iLock--; |
|
} |
|
return pFrameLock->m_iLock; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
int *CDataCacheSection::GetFrameUnlockCounterPtr() |
|
{ |
|
return &m_nFrameUnlockCounter; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Lock management, not for the feint of heart |
|
//----------------------------------------------------------------------------- |
|
int CDataCacheSection::GetLockCount( DataCacheHandle_t handle ) |
|
{ |
|
return m_LRU.LockCount( (memhandle_t)handle ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
int CDataCacheSection::BreakLock( DataCacheHandle_t handle ) |
|
{ |
|
return m_LRU.BreakLock( (memhandle_t)handle ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Explicitly mark an item as "recently used" |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::Touch( DataCacheHandle_t handle ) |
|
{ |
|
m_LRU.TouchResource( (memhandle_t)handle ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Explicitly mark an item as "least recently used". |
|
//----------------------------------------------------------------------------- |
|
bool CDataCacheSection::Age( DataCacheHandle_t handle ) |
|
{ |
|
m_LRU.MarkAsStale( (memhandle_t)handle ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified |
|
//----------------------------------------------------------------------------- |
|
unsigned CDataCacheSection::Flush( bool bUnlockedOnly, bool bNotify ) |
|
{ |
|
VPROF( "CDataCacheSection::Flush" ); |
|
|
|
AUTO_LOCK( m_mutex ); |
|
|
|
DataCacheNotificationType_t notificationType = ( bNotify )? DC_FLUSH_DISCARD : DC_NONE; |
|
|
|
memhandle_t hCurrent; |
|
memhandle_t hNext; |
|
|
|
unsigned nBytesFlushed = 0; |
|
unsigned nBytesCurrent = 0; |
|
|
|
hCurrent = GetFirstUnlockedItem(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
hNext = GetNextItem( hCurrent ); |
|
nBytesCurrent = AccessItem( hCurrent )->size; |
|
|
|
if ( DiscardItem( hCurrent, notificationType ) ) |
|
{ |
|
nBytesFlushed += nBytesCurrent; |
|
} |
|
hCurrent = hNext; |
|
} |
|
|
|
if ( !bUnlockedOnly ) |
|
{ |
|
hCurrent = GetFirstLockedItem(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
hNext = GetNextItem( hCurrent ); |
|
nBytesCurrent = AccessItem( hCurrent )->size; |
|
|
|
if ( DiscardItem( hCurrent, notificationType ) ) |
|
{ |
|
nBytesFlushed += nBytesCurrent; |
|
} |
|
hCurrent = hNext; |
|
} |
|
} |
|
|
|
return nBytesFlushed; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed |
|
//----------------------------------------------------------------------------- |
|
unsigned CDataCacheSection::Purge( unsigned nBytes ) |
|
{ |
|
VPROF( "CDataCacheSection::Purge" ); |
|
|
|
AUTO_LOCK( m_mutex ); |
|
|
|
unsigned nBytesPurged = 0; |
|
unsigned nBytesCurrent = 0; |
|
|
|
memhandle_t hCurrent = GetFirstUnlockedItem(); |
|
memhandle_t hNext; |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE && nBytes > 0 ) |
|
{ |
|
hNext = GetNextItem( hCurrent ); |
|
nBytesCurrent = AccessItem( hCurrent )->size; |
|
|
|
if ( DiscardItem( hCurrent, DC_FLUSH_DISCARD ) ) |
|
{ |
|
nBytesPurged += nBytesCurrent; |
|
nBytes -= min( nBytesCurrent, nBytes ); |
|
} |
|
hCurrent = hNext; |
|
} |
|
|
|
return nBytesPurged; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Dump the oldest items to free the specified number of items. Returns number actually freed |
|
//----------------------------------------------------------------------------- |
|
unsigned CDataCacheSection::PurgeItems( unsigned nItems ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
|
|
unsigned nPurged = 0; |
|
|
|
memhandle_t hCurrent = GetFirstUnlockedItem(); |
|
memhandle_t hNext; |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE && nItems ) |
|
{ |
|
hNext = GetNextItem( hCurrent ); |
|
|
|
if ( DiscardItem( hCurrent, DC_FLUSH_DISCARD ) ) |
|
{ |
|
nItems--; |
|
nPurged++; |
|
} |
|
hCurrent = hNext; |
|
} |
|
|
|
return nPurged; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Output the state of the section |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::OutputReport( DataCacheReportType_t reportType ) |
|
{ |
|
m_pSharedCache->OutputReport( reportType, GetName() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Updates the size of a specific item |
|
// Input : handle - |
|
// newSize - |
|
//----------------------------------------------------------------------------- |
|
void CDataCacheSection::UpdateSize( DataCacheHandle_t handle, unsigned int nNewSize ) |
|
{ |
|
DataCacheItem_t *pItem = m_LRU.LockResource( (memhandle_t)handle ); |
|
if ( !pItem ) |
|
{ |
|
// If it's gone from memory, size is already irrelevant |
|
return; |
|
} |
|
|
|
unsigned oldSize = pItem->size; |
|
|
|
if ( oldSize != nNewSize ) |
|
{ |
|
// Update the size |
|
pItem->size = nNewSize; |
|
|
|
int bytesAdded = nNewSize - oldSize; |
|
// If change would grow cache size, then purge items until we have room |
|
if ( bytesAdded > 0 ) |
|
{ |
|
m_pSharedCache->EnsureCapacity( bytesAdded ); |
|
} |
|
|
|
m_LRU.NotifySizeChanged( (memhandle_t)handle, oldSize, nNewSize ); |
|
NoteSizeChanged( oldSize, nNewSize ); |
|
} |
|
|
|
m_LRU.UnlockResource( (memhandle_t)handle ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
memhandle_t CDataCacheSection::GetFirstUnlockedItem() |
|
{ |
|
memhandle_t hCurrent; |
|
|
|
hCurrent = m_LRU.GetFirstUnlocked(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
if ( AccessItem( hCurrent )->pSection == this ) |
|
{ |
|
return hCurrent; |
|
} |
|
hCurrent = m_LRU.GetNext( hCurrent ); |
|
} |
|
return INVALID_MEMHANDLE; |
|
} |
|
|
|
|
|
memhandle_t CDataCacheSection::GetFirstLockedItem() |
|
{ |
|
memhandle_t hCurrent; |
|
|
|
hCurrent = m_LRU.GetFirstLocked(); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
if ( AccessItem( hCurrent )->pSection == this ) |
|
{ |
|
return hCurrent; |
|
} |
|
hCurrent = m_LRU.GetNext( hCurrent ); |
|
} |
|
return INVALID_MEMHANDLE; |
|
} |
|
|
|
|
|
memhandle_t CDataCacheSection::GetNextItem( memhandle_t hCurrent ) |
|
{ |
|
hCurrent = m_LRU.GetNext( hCurrent ); |
|
|
|
while ( hCurrent != INVALID_MEMHANDLE ) |
|
{ |
|
if ( AccessItem( hCurrent )->pSection == this ) |
|
{ |
|
return hCurrent; |
|
} |
|
hCurrent = m_LRU.GetNext( hCurrent ); |
|
} |
|
return INVALID_MEMHANDLE; |
|
} |
|
|
|
bool CDataCacheSection::DiscardItem( memhandle_t hItem, DataCacheNotificationType_t type ) |
|
{ |
|
DataCacheItem_t *pItem = AccessItem( hItem ); |
|
if ( DiscardItemData( pItem, type ) ) |
|
{ |
|
if ( m_LRU.LockCount( hItem ) ) |
|
{ |
|
m_LRU.BreakLock( hItem ); |
|
NoteUnlock( pItem->size ); |
|
} |
|
|
|
FrameLock_t *pFrameLock = m_ThreadFrameLock.Get(); |
|
if ( pFrameLock ) |
|
{ |
|
int iThread = pFrameLock->m_iThread; |
|
if ( pItem->pNextFrameLocked[iThread] != DC_NO_NEXT_LOCKED ) |
|
{ |
|
if ( pFrameLock->m_pFirst == pItem ) |
|
{ |
|
pFrameLock->m_pFirst = pItem->pNextFrameLocked[iThread]; |
|
} |
|
else |
|
{ |
|
DataCacheItem_t *pCurrent = pFrameLock->m_pFirst; |
|
while ( pCurrent ) |
|
{ |
|
if ( pCurrent->pNextFrameLocked[iThread] == pItem ) |
|
{ |
|
pCurrent->pNextFrameLocked[iThread] = pItem->pNextFrameLocked[iThread]; |
|
break; |
|
} |
|
pCurrent = pCurrent->pNextFrameLocked[iThread]; |
|
} |
|
} |
|
pItem->pNextFrameLocked[iThread] = DC_NO_NEXT_LOCKED; |
|
} |
|
|
|
} |
|
|
|
#ifdef _DEBUG |
|
for ( int i = 0; i < DC_MAX_THREADS_FRAMELOCKED; i++ ) |
|
{ |
|
if ( pItem->pNextFrameLocked[i] != DC_NO_NEXT_LOCKED ) |
|
{ |
|
DebuggerBreak(); // higher level code needs to handle better |
|
} |
|
} |
|
#endif |
|
|
|
pItem->pSection = NULL; // inhibit callbacks from lower level resource system |
|
m_LRU.DestroyResource( hItem ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool CDataCacheSection::DiscardItemData( DataCacheItem_t *pItem, DataCacheNotificationType_t type ) |
|
{ |
|
if ( pItem ) |
|
{ |
|
if ( type != DC_NONE ) |
|
{ |
|
Assert( type == DC_AGE_DISCARD || type == DC_FLUSH_DISCARD || DC_REMOVED ); |
|
|
|
if ( type == DC_AGE_DISCARD && m_pSharedCache->IsInFlush() ) |
|
type = DC_FLUSH_DISCARD; |
|
|
|
DataCacheNotification_t notification = |
|
{ |
|
type, |
|
GetName(), |
|
pItem->clientId, |
|
pItem->pItemData, |
|
pItem->size |
|
}; |
|
|
|
bool bResult = m_pClient->HandleCacheNotification( notification ); |
|
AssertMsg( bResult, "Refusal of cache drop not yet implemented!" ); |
|
|
|
if ( bResult ) |
|
{ |
|
NoteRemove( pItem->size ); |
|
} |
|
|
|
return bResult; |
|
} |
|
|
|
OnRemove( pItem->clientId ); |
|
|
|
pItem->pSection = NULL; |
|
pItem->pItemData = NULL, |
|
pItem->clientId = 0; |
|
|
|
NoteRemove( pItem->size ); |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CDataCacheSectionFastFind |
|
//----------------------------------------------------------------------------- |
|
DataCacheHandle_t CDataCacheSectionFastFind::DoFind( DataCacheClientID_t clientId ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
UtlHashFastHandle_t hHash = m_Handles.Find( Hash4( &clientId ) ); |
|
if( hHash != m_Handles.InvalidHandle() ) |
|
return m_Handles[hHash]; |
|
return DC_INVALID_HANDLE; |
|
} |
|
|
|
|
|
void CDataCacheSectionFastFind::OnAdd( DataCacheClientID_t clientId, DataCacheHandle_t hCacheItem ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
Assert( m_Handles.Find( Hash4( &clientId ) ) == m_Handles.InvalidHandle()); |
|
m_Handles.FastInsert( Hash4( &clientId ), hCacheItem ); |
|
} |
|
|
|
|
|
void CDataCacheSectionFastFind::OnRemove( DataCacheClientID_t clientId ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
UtlHashFastHandle_t hHash = m_Handles.Find( Hash4( &clientId ) ); |
|
Assert( hHash != m_Handles.InvalidHandle()); |
|
if( hHash != m_Handles.InvalidHandle() ) |
|
return m_Handles.Remove( hHash ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CDataCache |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// Convar callback to change data cache |
|
//----------------------------------------------------------------------------- |
|
void DataCacheSize_f( IConVar *pConVar, const char *pOldString, float flOldValue ) |
|
{ |
|
ConVarRef var( pConVar ); |
|
int nOldValue = (int)flOldValue; |
|
if ( var.GetInt() != nOldValue ) |
|
{ |
|
g_DataCache.SetSize( var.GetInt() * 1024 * 1024 ); |
|
} |
|
} |
|
ConVar datacachesize( "datacachesize", "64", FCVAR_INTERNAL_USE, "Size in MB.", true, 32, true, 512, DataCacheSize_f ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Connect, disconnect |
|
//----------------------------------------------------------------------------- |
|
bool CDataCache::Connect( CreateInterfaceFn factory ) |
|
{ |
|
if ( !BaseClass::Connect( factory ) ) |
|
return false; |
|
|
|
g_DataCache.SetSize( datacachesize.GetInt() * 1024 * 1024 ); |
|
g_pDataCache = this; |
|
|
|
return true; |
|
} |
|
|
|
void CDataCache::Disconnect() |
|
{ |
|
g_pDataCache = NULL; |
|
BaseClass::Disconnect(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Init, Shutdown |
|
//----------------------------------------------------------------------------- |
|
InitReturnVal_t CDataCache::Init( void ) |
|
{ |
|
return BaseClass::Init(); |
|
} |
|
|
|
void CDataCache::Shutdown( void ) |
|
{ |
|
Flush( false, false ); |
|
BaseClass::Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Query interface |
|
//----------------------------------------------------------------------------- |
|
void *CDataCache::QueryInterface( const char *pInterfaceName ) |
|
{ |
|
// Loading the datacache DLL mounts *all* interfaces |
|
// This includes the backward-compatible interfaces + IStudioDataCache |
|
CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary |
|
return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing. |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
CDataCache::CDataCache() |
|
: m_mutex( m_LRU.AccessMutex() ) |
|
{ |
|
memset( &m_status, 0, sizeof(m_status) ); |
|
m_bInFlush = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Controls cache size. |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::SetSize( int nMaxBytes ) |
|
{ |
|
m_LRU.SetTargetSize( nMaxBytes ); |
|
m_LRU.FlushToTargetSize(); |
|
|
|
nMaxBytes /= 1024 * 1024; |
|
|
|
if ( datacachesize.GetInt() != nMaxBytes ) |
|
{ |
|
datacachesize.SetValue( nMaxBytes ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Controls cache options. |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::SetOptions( unsigned options ) |
|
{ |
|
for ( int i = 0; m_Sections.Count(); i++ ) |
|
{ |
|
m_Sections[i]->SetOptions( options ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Controls cache section size. |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::SetSectionLimits( const char *pszSectionName, const DataCacheLimits_t &limits ) |
|
{ |
|
IDataCacheSection *pSection = FindSection( pszSectionName ); |
|
|
|
if ( !pSection ) |
|
{ |
|
DevMsg( "Cannot find requested cache section \"%s\"", pszSectionName ); |
|
return; |
|
} |
|
|
|
pSection->SetLimits( limits ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the current state of the cache |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits ) |
|
{ |
|
if ( pStatus ) |
|
{ |
|
*pStatus = m_status; |
|
} |
|
|
|
if ( pLimits ) |
|
{ |
|
Construct( pLimits ); |
|
pLimits->nMaxBytes = m_LRU.TargetSize(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Add a section to the cache |
|
//----------------------------------------------------------------------------- |
|
IDataCacheSection *CDataCache::AddSection( IDataCacheClient *pClient, const char *pszSectionName, const DataCacheLimits_t &limits, bool bSupportFastFind ) |
|
{ |
|
CDataCacheSection *pSection; |
|
|
|
pSection = (CDataCacheSection *)FindSection( pszSectionName ); |
|
if ( pSection ) |
|
{ |
|
AssertMsg1( pSection->GetClient() == pClient, "Duplicate cache section name \"%s\"", pszSectionName ); |
|
return pSection; |
|
} |
|
|
|
if ( !bSupportFastFind ) |
|
pSection = new CDataCacheSection( this, pClient, pszSectionName ); |
|
else |
|
pSection = new CDataCacheSectionFastFind( this, pClient, pszSectionName ); |
|
|
|
pSection->SetLimits( limits ); |
|
|
|
m_Sections.AddToTail( pSection ); |
|
return pSection; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remove a section from the cache |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::RemoveSection( const char *pszClientName, bool bCallFlush ) |
|
{ |
|
int iSection = FindSectionIndex( pszClientName ); |
|
|
|
if ( iSection != m_Sections.InvalidIndex() ) |
|
{ |
|
if ( bCallFlush ) |
|
{ |
|
m_Sections[iSection]->Flush( false ); |
|
} |
|
delete m_Sections[iSection]; |
|
m_Sections.FastRemove( iSection ); |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Find a section of the cache |
|
//----------------------------------------------------------------------------- |
|
IDataCacheSection *CDataCache::FindSection( const char *pszClientName ) |
|
{ |
|
int iSection = FindSectionIndex( pszClientName ); |
|
|
|
if ( iSection != m_Sections.InvalidIndex() ) |
|
{ |
|
return m_Sections[iSection]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::EnsureCapacity( unsigned nBytes ) |
|
{ |
|
VPROF( "CDataCache::EnsureCapacity" ); |
|
|
|
m_LRU.EnsureCapacity( nBytes ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed |
|
//----------------------------------------------------------------------------- |
|
unsigned CDataCache::Purge( unsigned nBytes ) |
|
{ |
|
VPROF( "CDataCache::Purge" ); |
|
|
|
return m_LRU.Purge( nBytes ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified |
|
//----------------------------------------------------------------------------- |
|
unsigned CDataCache::Flush( bool bUnlockedOnly, bool bNotify ) |
|
{ |
|
VPROF( "CDataCache::Flush" ); |
|
|
|
unsigned result; |
|
|
|
if ( m_bInFlush ) |
|
{ |
|
return 0; |
|
} |
|
|
|
m_bInFlush = true; |
|
|
|
if ( bUnlockedOnly ) |
|
{ |
|
result = m_LRU.FlushAllUnlocked(); |
|
} |
|
else |
|
{ |
|
result = m_LRU.FlushAll(); |
|
} |
|
|
|
m_bInFlush = false; |
|
|
|
return result; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Output the state of the cache |
|
//----------------------------------------------------------------------------- |
|
void CDataCache::OutputReport( DataCacheReportType_t reportType, const char *pszSection ) |
|
{ |
|
int i; |
|
|
|
AUTO_LOCK( m_mutex ); |
|
int bytesUsed = m_LRU.UsedSize(); |
|
int bytesTotal = m_LRU.TargetSize(); |
|
|
|
float percent = 100.0f * (float)bytesUsed / (float)bytesTotal; |
|
|
|
CUtlVector<memhandle_t> lruList, lockedlist; |
|
|
|
m_LRU.GetLockHandleList( lockedlist ); |
|
m_LRU.GetLRUHandleList( lruList ); |
|
|
|
CDataCacheSection *pSection = NULL; |
|
if ( pszSection ) |
|
{ |
|
pSection = (CDataCacheSection *)FindSection( pszSection ); |
|
if ( !pSection ) |
|
{ |
|
Msg( "Unknown cache section %s\n", pszSection ); |
|
return; |
|
} |
|
} |
|
|
|
if ( reportType == DC_DETAIL_REPORT ) |
|
{ |
|
CUtlRBTree< memhandle_t, int > sortedbysize( 0, 0, SortMemhandlesBySizeLessFunc ); |
|
for ( i = 0; i < lockedlist.Count(); ++i ) |
|
{ |
|
if ( !pSection || AccessItem( lockedlist[ i ] )->pSection == pSection ) |
|
sortedbysize.Insert( lockedlist[ i ] ); |
|
} |
|
|
|
for ( i = 0; i < lruList.Count(); ++i ) |
|
{ |
|
if ( !pSection || AccessItem( lruList[ i ] )->pSection == pSection ) |
|
sortedbysize.Insert( lruList[ i ] ); |
|
} |
|
|
|
for ( i = sortedbysize.FirstInorder(); i != sortedbysize.InvalidIndex(); i = sortedbysize.NextInorder( i ) ) |
|
{ |
|
OutputItemReport( sortedbysize[ i ] ); |
|
} |
|
OutputReport( DC_SUMMARY_REPORT, pszSection ); |
|
} |
|
else if ( reportType == DC_DETAIL_REPORT_LRU ) |
|
{ |
|
for ( i = 0; i < lockedlist.Count(); ++i ) |
|
{ |
|
if ( !pSection || AccessItem( lockedlist[ i ] )->pSection == pSection ) |
|
OutputItemReport( lockedlist[ i ] ); |
|
} |
|
|
|
for ( i = 0; i < lruList.Count(); ++i ) |
|
{ |
|
if ( !pSection || AccessItem( lruList[ i ] )->pSection == pSection ) |
|
OutputItemReport( lruList[ i ] ); |
|
} |
|
OutputReport( DC_SUMMARY_REPORT, pszSection ); |
|
} |
|
else if ( reportType == DC_SUMMARY_REPORT ) |
|
{ |
|
if ( !pszSection ) |
|
{ |
|
// summary for all of the sections |
|
for ( int i = 0; i < m_Sections.Count(); ++i ) |
|
{ |
|
if ( m_Sections[i]->GetName() ) |
|
{ |
|
OutputReport( DC_SUMMARY_REPORT, m_Sections[i]->GetName() ); |
|
} |
|
} |
|
Msg( "Summary: %i resources total %s, %.2f %% of capacity\n", lockedlist.Count() + lruList.Count(), Q_pretifymem( bytesUsed, 2, true ), percent ); |
|
} |
|
else |
|
{ |
|
// summary for the specified section |
|
DataCacheItem_t *pItem; |
|
int sectionBytes = 0; |
|
int sectionCount = 0; |
|
for ( i = 0; i < lockedlist.Count(); ++i ) |
|
{ |
|
if ( AccessItem( lockedlist[ i ] )->pSection == pSection ) |
|
{ |
|
pItem = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lockedlist[i] ); |
|
sectionBytes += pItem->size; |
|
sectionCount++; |
|
} |
|
} |
|
for ( i = 0; i < lruList.Count(); ++i ) |
|
{ |
|
if ( AccessItem( lruList[ i ] )->pSection == pSection ) |
|
{ |
|
pItem = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lruList[i] ); |
|
sectionBytes += pItem->size; |
|
sectionCount++; |
|
} |
|
} |
|
int sectionSize = 1; |
|
float sectionPercent; |
|
if ( pSection->GetLimits().nMaxBytes == (unsigned int)-1 ) |
|
{ |
|
// section unrestricted, base on total size |
|
sectionSize = bytesTotal; |
|
} |
|
else if ( pSection->GetLimits().nMaxBytes ) |
|
{ |
|
sectionSize = pSection->GetLimits().nMaxBytes; |
|
} |
|
sectionPercent = 100.0f * (float)sectionBytes/(float)sectionSize; |
|
Msg( "Section [%s]: %i resources total %s, %.2f %% of limit (%s)\n", pszSection, sectionCount, Q_pretifymem( sectionBytes, 2, true ), sectionPercent, Q_pretifymem( sectionSize, 2, true ) ); |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------- |
|
|
|
void CDataCache::OutputItemReport( memhandle_t hItem ) |
|
{ |
|
AUTO_LOCK( m_mutex ); |
|
DataCacheItem_t *pItem = m_LRU.GetResource_NoLockNoLRUTouch( hItem ); |
|
if ( !pItem ) |
|
return; |
|
|
|
CDataCacheSection *pSection = pItem->pSection; |
|
|
|
char name[DC_MAX_ITEM_NAME+1]; |
|
|
|
name[0] = 0; |
|
pSection->GetClient()->GetItemName( pItem->clientId, pItem->pItemData, name, DC_MAX_ITEM_NAME ); |
|
|
|
Msg( "\t%16.16s : %12s : 0x%08x, 0x%p, 0x%p : %s : %s\n", |
|
Q_pretifymem( pItem->size, 2, true ), |
|
pSection->GetName(), |
|
pItem->clientId, pItem->pItemData, hItem, |
|
( name[0] ) ? name : "unknown", |
|
( m_LRU.LockCount( hItem ) ) ? CFmtStr( "Locked %d", m_LRU.LockCount( hItem ) ).operator const char*() : "" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
int CDataCache::FindSectionIndex( const char *pszSection ) |
|
{ |
|
for ( int i = 0; i < m_Sections.Count(); i++ ) |
|
{ |
|
if ( stricmp( m_Sections[i]->GetName(), pszSection ) == 0 ) |
|
return i; |
|
} |
|
return m_Sections.InvalidIndex(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sorting utility used by the data cache report |
|
//----------------------------------------------------------------------------- |
|
bool CDataCache::SortMemhandlesBySizeLessFunc( const memhandle_t& lhs, const memhandle_t& rhs ) |
|
{ |
|
DataCacheItem_t *pItem1 = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lhs ); |
|
DataCacheItem_t *pItem2 = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( rhs ); |
|
|
|
Assert( pItem1 ); |
|
Assert( pItem2 ); |
|
|
|
return pItem1->size < pItem2->size; |
|
} |
|
|
|
|
|
|