2022-06-14 18:09:10 +08:00
|
|
|
|
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
|
2020-04-23 00:56:21 +08:00
|
|
|
|
//
|
|
|
|
|
// Purpose:
|
|
|
|
|
//
|
|
|
|
|
// $NoKeywords: $
|
|
|
|
|
//
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#include "basetypes.h"
|
|
|
|
|
#include "datamanager.h"
|
|
|
|
|
|
2022-06-14 18:09:10 +08:00
|
|
|
|
// NOTE: This has to be the last file included!
|
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
|
|
|
2020-04-23 00:56:21 +08:00
|
|
|
|
DECLARE_POINTER_HANDLE( memhandle_t );
|
|
|
|
|
|
2022-06-14 18:09:10 +08:00
|
|
|
|
#define AUTO_LOCK_DM() AUTO_LOCK_( CDataManagerBase, *this )
|
|
|
|
|
|
2020-04-23 00:56:21 +08:00
|
|
|
|
CDataManagerBase::CDataManagerBase( unsigned int maxSize )
|
|
|
|
|
{
|
|
|
|
|
m_targetMemorySize = maxSize;
|
|
|
|
|
m_memUsed = 0;
|
|
|
|
|
m_lruList = m_memoryLists.CreateList();
|
|
|
|
|
m_lockList = m_memoryLists.CreateList();
|
|
|
|
|
m_freeList = m_memoryLists.CreateList();
|
|
|
|
|
m_listsAreFreed = 0;
|
2022-06-14 18:09:10 +08:00
|
|
|
|
m_freeOnDestruct = 1;
|
2020-04-23 00:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CDataManagerBase::~CDataManagerBase()
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
Assert( !m_freeOnDestruct || m_listsAreFreed );
|
2020-04-23 00:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::NotifySizeChanged( memhandle_t handle, unsigned int oldSize, unsigned int newSize )
|
|
|
|
|
{
|
|
|
|
|
Lock();
|
|
|
|
|
m_memUsed += (int)newSize - (int)oldSize;
|
|
|
|
|
Unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::SetTargetSize( unsigned int targetSize )
|
|
|
|
|
{
|
|
|
|
|
m_targetMemorySize = targetSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::FlushAllUnlocked()
|
|
|
|
|
{
|
|
|
|
|
Lock();
|
|
|
|
|
|
|
|
|
|
int nFlush = m_memoryLists.Count( m_lruList );
|
2022-06-14 18:09:10 +08:00
|
|
|
|
void **pScratch = (void **)stackalloc( nFlush * sizeof(void *) );
|
2020-04-23 00:56:21 +08:00
|
|
|
|
CUtlVector<void *> destroyList( pScratch, nFlush );
|
|
|
|
|
|
|
|
|
|
unsigned nBytesInitial = MemUsed_Inline();
|
|
|
|
|
|
|
|
|
|
int node = m_memoryLists.Head(m_lruList);
|
|
|
|
|
while ( node != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
int next = m_memoryLists.Next(node);
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, node );
|
|
|
|
|
destroyList.AddToTail( GetForFreeByIndex( node ) );
|
|
|
|
|
node = next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Unlock();
|
|
|
|
|
|
|
|
|
|
for ( int i = 0; i < nFlush; i++ )
|
|
|
|
|
{
|
|
|
|
|
DestroyResourceStorage( destroyList[i] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ( nBytesInitial - MemUsed_Inline() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::FlushToTargetSize()
|
|
|
|
|
{
|
|
|
|
|
return EnsureCapacity(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Frees everything! The LRU AND the LOCKED items. This is only used to forcibly free the resources,
|
|
|
|
|
// not to make space.
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::FlushAll()
|
|
|
|
|
{
|
|
|
|
|
Lock();
|
|
|
|
|
|
|
|
|
|
int nFlush = m_memoryLists.Count( m_lruList ) + m_memoryLists.Count( m_lockList );
|
2022-06-14 18:09:10 +08:00
|
|
|
|
void **pScratch = (void **) stackalloc( nFlush * sizeof(void *) );
|
2020-04-23 00:56:21 +08:00
|
|
|
|
CUtlVector<void *> destroyList( pScratch, nFlush );
|
|
|
|
|
|
|
|
|
|
unsigned result = MemUsed_Inline();
|
|
|
|
|
int node;
|
|
|
|
|
int nextNode;
|
|
|
|
|
|
|
|
|
|
node = m_memoryLists.Head(m_lruList);
|
|
|
|
|
while ( node != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
nextNode = m_memoryLists.Next(node);
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, node );
|
|
|
|
|
destroyList.AddToTail( GetForFreeByIndex( node ) );
|
|
|
|
|
node = nextNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node = m_memoryLists.Head(m_lockList);
|
|
|
|
|
while ( node != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
nextNode = m_memoryLists.Next(node);
|
|
|
|
|
m_memoryLists.Unlink( m_lockList, node );
|
|
|
|
|
m_memoryLists[node].lockCount = 0;
|
|
|
|
|
destroyList.AddToTail( GetForFreeByIndex( node ) );
|
|
|
|
|
node = nextNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_listsAreFreed = false;
|
|
|
|
|
Unlock();
|
|
|
|
|
|
|
|
|
|
for ( int i = 0; i < nFlush; i++ )
|
|
|
|
|
{
|
|
|
|
|
DestroyResourceStorage( destroyList[i] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::Purge( unsigned int nBytesToPurge )
|
|
|
|
|
{
|
|
|
|
|
unsigned int nTargetSize = MemUsed_Inline() - nBytesToPurge;
|
2022-06-14 18:09:10 +08:00
|
|
|
|
if ( nBytesToPurge > MemUsed_Inline() )
|
2020-04-23 00:56:21 +08:00
|
|
|
|
nTargetSize = 0;
|
|
|
|
|
unsigned int nImpliedCapacity = MemTotal_Inline() - nTargetSize;
|
|
|
|
|
return EnsureCapacity( nImpliedCapacity );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::DestroyResource( memhandle_t handle )
|
|
|
|
|
{
|
|
|
|
|
Lock();
|
|
|
|
|
unsigned short index = FromHandle( handle );
|
|
|
|
|
if ( !m_memoryLists.IsValidIndex(index) )
|
|
|
|
|
{
|
|
|
|
|
Unlock();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert( m_memoryLists[index].lockCount == 0 );
|
|
|
|
|
if ( m_memoryLists[index].lockCount )
|
|
|
|
|
BreakLock( handle );
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, index );
|
|
|
|
|
void *p = GetForFreeByIndex( index );
|
|
|
|
|
Unlock();
|
|
|
|
|
|
|
|
|
|
DestroyResourceStorage( p );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *CDataManagerBase::LockResource( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lockList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
Assert(m_memoryLists[memoryIndex].lockCount != (unsigned short)-1);
|
|
|
|
|
m_memoryLists[memoryIndex].lockCount++;
|
|
|
|
|
return m_memoryLists[memoryIndex].pStore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-14 18:09:10 +08:00
|
|
|
|
void *CDataManagerBase::LockResourceReturnCount( int *pCount, memhandle_t handle )
|
|
|
|
|
{
|
|
|
|
|
AUTO_LOCK_DM();
|
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lockList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
Assert(m_memoryLists[memoryIndex].lockCount != (unsigned short)-1);
|
|
|
|
|
*pCount = ++m_memoryLists[memoryIndex].lockCount;
|
|
|
|
|
return m_memoryLists[memoryIndex].pStore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pCount = 0;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 00:56:21 +08:00
|
|
|
|
int CDataManagerBase::UnlockResource( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
Assert( m_memoryLists[memoryIndex].lockCount > 0 );
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount > 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists[memoryIndex].lockCount--;
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_lockList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lruList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return m_memoryLists[memoryIndex].lockCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *CDataManagerBase::GetResource_NoLockNoLRUTouch( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
return m_memoryLists[memoryIndex].pStore;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *CDataManagerBase::GetResource_NoLock( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
TouchByIndex( memoryIndex );
|
|
|
|
|
return m_memoryLists[memoryIndex].pStore;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::TouchResource( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
TouchByIndex( FromHandle(handle) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::MarkAsStale( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToHead( m_lruList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CDataManagerBase::BreakLock( memhandle_t handle )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
unsigned short memoryIndex = FromHandle(handle);
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() && m_memoryLists[memoryIndex].lockCount )
|
|
|
|
|
{
|
|
|
|
|
int nBroken = m_memoryLists[memoryIndex].lockCount;
|
|
|
|
|
m_memoryLists[memoryIndex].lockCount = 0;
|
|
|
|
|
m_memoryLists.Unlink( m_lockList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lruList, memoryIndex );
|
|
|
|
|
|
|
|
|
|
return nBroken;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CDataManagerBase::BreakAllLocks()
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
int nBroken = 0;
|
|
|
|
|
int node;
|
|
|
|
|
int nextNode;
|
|
|
|
|
|
|
|
|
|
node = m_memoryLists.Head(m_lockList);
|
|
|
|
|
while ( node != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
nBroken++;
|
|
|
|
|
nextNode = m_memoryLists.Next(node);
|
|
|
|
|
m_memoryLists[node].lockCount = 0;
|
|
|
|
|
m_memoryLists.Unlink( m_lockList, node );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lruList, node );
|
|
|
|
|
node = nextNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nBroken;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned short CDataManagerBase::CreateHandle( bool bCreateLocked )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
int memoryIndex = m_memoryLists.Head(m_freeList);
|
|
|
|
|
unsigned short list = ( bCreateLocked ) ? m_lockList : m_lruList;
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_freeList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( list, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memoryIndex = m_memoryLists.AddToTail( list );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( bCreateLocked )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists[memoryIndex].lockCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return memoryIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memhandle_t CDataManagerBase::StoreResourceInHandle( unsigned short memoryIndex, void *pStore, unsigned int realSize )
|
|
|
|
|
{
|
2022-06-14 18:09:10 +08:00
|
|
|
|
AUTO_LOCK_DM();
|
2020-04-23 00:56:21 +08:00
|
|
|
|
resource_lru_element_t &mem = m_memoryLists[memoryIndex];
|
|
|
|
|
mem.pStore = pStore;
|
|
|
|
|
m_memUsed += realSize;
|
|
|
|
|
return ToHandle(memoryIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDataManagerBase::TouchByIndex( unsigned short memoryIndex )
|
|
|
|
|
{
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
if ( m_memoryLists[memoryIndex].lockCount == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, memoryIndex );
|
|
|
|
|
m_memoryLists.LinkToTail( m_lruList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memhandle_t CDataManagerBase::ToHandle( unsigned short index )
|
|
|
|
|
{
|
|
|
|
|
unsigned int hiword = m_memoryLists.Element(index).serial;
|
|
|
|
|
hiword <<= 16;
|
|
|
|
|
index++;
|
2022-06-14 18:09:10 +08:00
|
|
|
|
return reinterpret_cast< memhandle_t >( (uintp)( hiword|index ) );
|
2020-04-23 00:56:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::TargetSize()
|
|
|
|
|
{
|
|
|
|
|
return MemTotal_Inline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::AvailableSize()
|
|
|
|
|
{
|
|
|
|
|
return MemAvailable_Inline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int CDataManagerBase::UsedSize()
|
|
|
|
|
{
|
|
|
|
|
return MemUsed_Inline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// free resources until there is enough space to hold "size"
|
|
|
|
|
unsigned int CDataManagerBase::EnsureCapacity( unsigned int size )
|
|
|
|
|
{
|
|
|
|
|
unsigned nBytesInitial = MemUsed_Inline();
|
|
|
|
|
while ( MemUsed_Inline() > MemTotal_Inline() || MemAvailable_Inline() < size )
|
|
|
|
|
{
|
|
|
|
|
Lock();
|
|
|
|
|
int lruIndex = m_memoryLists.Head( m_lruList );
|
|
|
|
|
if ( lruIndex == m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
Unlock();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
m_memoryLists.Unlink( m_lruList, lruIndex );
|
|
|
|
|
void *p = GetForFreeByIndex( lruIndex );
|
|
|
|
|
Unlock();
|
|
|
|
|
DestroyResourceStorage( p );
|
|
|
|
|
}
|
|
|
|
|
return ( nBytesInitial - MemUsed_Inline() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// free this resource and move the handle to the free list
|
|
|
|
|
void *CDataManagerBase::GetForFreeByIndex( unsigned short memoryIndex )
|
|
|
|
|
{
|
|
|
|
|
void *p = NULL;
|
|
|
|
|
if ( memoryIndex != m_memoryLists.InvalidIndex() )
|
|
|
|
|
{
|
|
|
|
|
Assert( m_memoryLists[memoryIndex].lockCount == 0 );
|
|
|
|
|
|
|
|
|
|
resource_lru_element_t &mem = m_memoryLists[memoryIndex];
|
|
|
|
|
unsigned size = GetRealSize( mem.pStore );
|
|
|
|
|
if ( size > m_memUsed )
|
|
|
|
|
{
|
|
|
|
|
ExecuteOnce( Warning( "Data manager 'used' memory incorrect\n" ) );
|
|
|
|
|
size = m_memUsed;
|
|
|
|
|
}
|
|
|
|
|
m_memUsed -= size;
|
|
|
|
|
p = mem.pStore;
|
|
|
|
|
mem.pStore = NULL;
|
|
|
|
|
mem.serial++;
|
|
|
|
|
m_memoryLists.LinkToTail( m_freeList, memoryIndex );
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get a list of everything in the LRU
|
|
|
|
|
void CDataManagerBase::GetLRUHandleList( CUtlVector< memhandle_t >& list )
|
|
|
|
|
{
|
|
|
|
|
for ( int node = m_memoryLists.Tail(m_lruList);
|
|
|
|
|
node != m_memoryLists.InvalidIndex();
|
|
|
|
|
node = m_memoryLists.Previous(node) )
|
|
|
|
|
{
|
|
|
|
|
list.AddToTail( ToHandle( node ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get a list of everything locked
|
|
|
|
|
void CDataManagerBase::GetLockHandleList( CUtlVector< memhandle_t >& list )
|
|
|
|
|
{
|
|
|
|
|
for ( int node = m_memoryLists.Head(m_lockList);
|
|
|
|
|
node != m_memoryLists.InvalidIndex();
|
|
|
|
|
node = m_memoryLists.Next(node) )
|
|
|
|
|
{
|
|
|
|
|
list.AddToTail( ToHandle( node ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|