572 lines
12 KiB
C++
572 lines
12 KiB
C++
// code by Testors 2005/03/24
|
|
|
|
#pragma once
|
|
|
|
#include <toolkit/ILock.h>
|
|
#include <toolkit/khash.h>
|
|
#include <time.h>
|
|
#include <list>
|
|
|
|
/*
|
|
#include <windows.h>
|
|
#include "LRUCacheContainer.h"
|
|
//#include <string>
|
|
|
|
struct RESOURCE
|
|
{
|
|
RESOURCE( int n ) : num( n )
|
|
{
|
|
printf( "리소스 #%d 생성됨.\n", num );
|
|
}
|
|
|
|
~RESOURCE()
|
|
{
|
|
printf( "리소스 #%d 삭제됨.\n", num );
|
|
}
|
|
|
|
|
|
int num;
|
|
};
|
|
|
|
int main( int c, char* v[] )
|
|
{
|
|
XLRUCacheContainer< char, hashPr_mod_int, RESOURCE* > container( 3 );
|
|
|
|
container.Add( 0, new RESOURCE( 0 ) ); // bAutoAddRef 가 디폴트 true 이므로ADD 시 ref_count 는 자동으로 1로 세팅됨
|
|
container.Add( 1, new RESOURCE( 1 ) );
|
|
container.Add( 2, new RESOURCE( 2 ) );
|
|
|
|
container.ReleaseRef( 1 );
|
|
container.ReleaseRef( 2 );
|
|
|
|
// 3초 지나면 리소스 1과 2가 삭제됨.
|
|
while( true )
|
|
{
|
|
container.Process();
|
|
Sleep( 100 );
|
|
}
|
|
}
|
|
*/
|
|
|
|
#ifdef _MEMORY_LEAK_FIX_
|
|
const unsigned int DEF_WAIT_TIME = 0;
|
|
#else
|
|
const unsigned int DEF_WAIT_TIME = 1800;
|
|
#endif
|
|
|
|
template< typename _O >
|
|
struct XDefaultObjectDestroyer
|
|
{
|
|
void operator()( _O & obj ) { delete obj; }
|
|
};
|
|
|
|
template< typename _ID, typename _KHASHPR, typename _T, typename _DESTROYER = XDefaultObjectDestroyer< _T > >
|
|
struct XLRUCacheContainer
|
|
{
|
|
XLRUCacheContainer( unsigned int expire_time = 600, bool bAutoAddRef = true ) : m_nExpireTime( expire_time ), m_bAutoAddRef( bAutoAddRef ) { m_nLastProcessTime = now(); }
|
|
~XLRUCacheContainer() {}
|
|
|
|
bool Add( _ID id, const _T & t, bool bDontAddRef = false )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
// 이미 있다면 에러!
|
|
assert( !m_hsData.has( id ) );
|
|
|
|
// Hash 에 등록
|
|
_TAG *pTag = new _TAG( now(), id, t );
|
|
m_hsData.add( id, pTag );
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "Add to m_hsData : %s(%d)\n", pTag->id.c_str(), m_hsData.size() );
|
|
#endif
|
|
|
|
#ifndef NDEBUG
|
|
m_vDBG.push_back( pTag );
|
|
#endif
|
|
|
|
pTag->last_access_time = now();
|
|
|
|
if( m_bAutoAddRef && !bDontAddRef ) add_ref( pTag, 1 );
|
|
else addToLRUList( pTag );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Remove( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
_TAG *pTag = NULL;
|
|
if( !m_hsData.lookup( id, pTag ) ) return false;
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "Remove to m_hsData : %s(%d)\n", id.c_str(), m_hsData.size() - 1 );
|
|
#endif
|
|
|
|
removeFromLRUList( pTag );
|
|
m_hsData.erase( id );
|
|
|
|
delete pTag;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsExist( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
return m_hsData.has( id );
|
|
}
|
|
|
|
bool Get( _ID id, _T * t )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
_TAG *pTag = NULL;
|
|
|
|
bool bRtn = m_hsData.lookup( id, pTag );
|
|
|
|
if( bRtn )
|
|
{
|
|
pTag->last_access_time = now();
|
|
|
|
*t = pTag->data;
|
|
|
|
/*
|
|
// LRU 리스트 순위 조절
|
|
if( pTag->ref_count == 0 && !m_hsExceptDataList.has( id ) )
|
|
{
|
|
m_vLRU.erase( pTag->it );
|
|
pTag->it = m_vLRU.insert( m_vLRU.end(), pTag );
|
|
}
|
|
*/
|
|
AddRef( id );
|
|
}
|
|
|
|
return bRtn;
|
|
}
|
|
|
|
int AddRef( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
_TAG *pTag = NULL;
|
|
bool bRtn = m_hsData.lookup( id, pTag );
|
|
if( !bRtn ) return -1;
|
|
|
|
pTag->last_access_time = now();
|
|
|
|
if( pTag->ref_count == 0 )
|
|
{
|
|
removeFromLRUList( pTag );
|
|
}
|
|
|
|
add_ref( pTag, 1 );
|
|
|
|
return pTag->ref_count;
|
|
}
|
|
|
|
int ReleaseRef( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
_TAG *pTag = NULL;
|
|
bool bRtn = m_hsData.lookup( id, pTag );
|
|
if( !bRtn ) return -1;
|
|
|
|
add_ref( pTag, -1 );
|
|
|
|
pTag->last_access_time = now();
|
|
|
|
// 미사용인 자원은 LRU list 에 삽입
|
|
if( pTag->ref_count == 0 )
|
|
{
|
|
#ifdef LRU_DEBUG
|
|
//printf( "Added to LRU List : %s\n", pTag->id.c_str() );
|
|
_performance_print( "Added to LRU List : %s\n", pTag->id.c_str() );
|
|
#endif //LRU_DEBUG
|
|
addToLRUList( pTag );
|
|
}
|
|
|
|
return pTag->ref_count;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
_TAG* pTag = NULL;
|
|
bool res;
|
|
int nCount = 0;
|
|
res = m_hsData.get_first_value( pTag );
|
|
while ( res )
|
|
{
|
|
if ( pTag != NULL )
|
|
{
|
|
deleteObject( pTag->data );
|
|
delete pTag;
|
|
}
|
|
++nCount;
|
|
res = m_hsData.get_next_value( pTag );
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
_oprint( "All Count : %d\n", m_vDBG.size() );
|
|
_oprint( "HAS Count : %d\n", nCount );
|
|
_oprint( "LRU Count : %d\n", m_vLRU.size() );
|
|
#endif
|
|
|
|
m_vLRU.clear();
|
|
m_hsData.clear();
|
|
}
|
|
|
|
void ClearUnreferencedResource()
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
//Thread 로딩이 걸린 것이 있기 때문에 시간 검사 함.
|
|
unsigned int current_time = now();
|
|
m_nLastProcessTime = now();
|
|
unsigned int expire_time = current_time - DEF_WAIT_TIME;
|
|
|
|
std::list< _TAG* >::iterator it;
|
|
for( it = m_vLRU.begin(); it != m_vLRU.end(); )
|
|
{
|
|
if( (*it)->last_access_time >= expire_time )
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "ClearUnreferencedResource delete object : %s\n", (*it)->id.c_str() );
|
|
#endif
|
|
m_hsData.erase( (*it)->id );
|
|
deleteObject( (*it)->data );
|
|
delete (*it);
|
|
it = m_vLRU.erase( it );
|
|
}
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "LRU count : %d\n", m_vLRU.size());
|
|
#endif
|
|
}
|
|
|
|
void Enum( std::vector< _T > & vList )
|
|
{
|
|
bool res;
|
|
KHash< _TAG*, _KHASHPR >::node *_node = NULL;
|
|
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
res = m_hsData.get_first_node( _node );
|
|
while ( res )
|
|
{
|
|
if ( _node->value != NULL )
|
|
{
|
|
vList.push_back( (_node->value)->data );
|
|
}
|
|
res = m_hsData.get_next_node( _node );
|
|
}
|
|
}
|
|
|
|
void PrintList()
|
|
{
|
|
bool res;
|
|
KHash< _TAG*, _KHASHPR >::node *_node = NULL;
|
|
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
// _performance_print( "PrintList 00\n\n" );
|
|
|
|
int nCnt=0;
|
|
res = m_hsData.get_first_node( _node );
|
|
while ( res )
|
|
{
|
|
if ( _node->value != NULL )
|
|
{
|
|
_TAG* pTag = _node->value;
|
|
|
|
// _performance_print( "%s - LTime : %d - Ref : %d\n", pTag->id.c_str(), pTag->last_access_time, pTag->ref_count );
|
|
}
|
|
|
|
res = m_hsData.get_next_node( _node );
|
|
++nCnt;
|
|
}
|
|
|
|
// _performance_print( "%d\n\n PrintList 01 %d \n\n", nCnt, m_vLRU.size() );
|
|
|
|
std::list< _TAG* >::iterator it;
|
|
for( it = m_vLRU.begin(); it != m_vLRU.end(); ++it )
|
|
{
|
|
_TAG* pTag = (*it);
|
|
// _performance_print( "%s - LTime : %d - Ref : %d\n", pTag->id.c_str(), pTag->last_access_time, pTag->ref_count );
|
|
}
|
|
|
|
{
|
|
// _performance_print( "\n ExceptList 03\n\n" );
|
|
|
|
nCnt = 0;
|
|
|
|
bool res;
|
|
KHash< _ID, _KHASHPR >::node *_node = NULL;
|
|
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
res = m_hsExceptDataList.get_first_node( _node );
|
|
while ( res )
|
|
{
|
|
// _performance_print( "%s \n", _node->value.c_str() );
|
|
|
|
res = m_hsExceptDataList.get_next_node( _node );
|
|
++nCnt;
|
|
}
|
|
|
|
// _performance_print( "%d PrintList 04\n\n", nCnt );
|
|
}
|
|
}
|
|
|
|
void Process()
|
|
{
|
|
std::list< _TAG* > vEraseList;
|
|
|
|
{
|
|
#ifdef _MEMORY_LEAK_FIX_
|
|
static size_t MAX_DELETE_COUNT = 100;
|
|
#else
|
|
const size_t MAX_DELETE_COUNT = 30;
|
|
#endif
|
|
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
unsigned int current_time = now();
|
|
|
|
if( m_nLastProcessTime == now() ) return;
|
|
|
|
m_nLastProcessTime = now();
|
|
|
|
unsigned int expire_time = current_time - m_nExpireTime;
|
|
|
|
size_t count = 0;
|
|
std::list< _TAG* >::iterator it;
|
|
std::list< _TAG* >::iterator release_begin = m_vLRU.begin();
|
|
std::list< _TAG* >::iterator release_end = m_vLRU.begin();
|
|
for( it = m_vLRU.begin(); it != m_vLRU.end(); ++it )
|
|
{
|
|
if( count > MAX_DELETE_COUNT ) break;
|
|
if( (*it)->last_access_time >= expire_time ) break;
|
|
|
|
++count;
|
|
m_hsData.erase( (*it)->id );
|
|
}
|
|
|
|
if( count ) release_end = it;
|
|
|
|
if( release_begin != release_end )
|
|
{
|
|
vEraseList.assign( release_begin, release_end );
|
|
m_vLRU.erase( release_begin, release_end );
|
|
}
|
|
}
|
|
|
|
{
|
|
std::list< _TAG* >::iterator it;
|
|
for( it = vEraseList.begin(); it != vEraseList.end(); ++it )
|
|
{
|
|
// 제거대상이 아니면 KIN
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
if( m_hsExceptDataList.has( (*it)->id ) )
|
|
continue;
|
|
}
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "Deleted by LRU : %s(%d)\n", (*it)->id.c_str() );
|
|
#endif // LRU_DEBUG
|
|
deleteObject( (*it)->data );
|
|
delete (*it);
|
|
}
|
|
}
|
|
|
|
#ifdef LRU_DEBUG
|
|
//printf( "LRU count : %d\n", m_vLRU.size() );
|
|
_performance_print( "LRU count : %d\n", m_vLRU.size() );
|
|
_performance_print("m_hsExceptDataList count : %d\n", m_hsExceptDataList.size());
|
|
_performance_print("m_shData count : %d\n", m_hsData.size());
|
|
#endif // LRU_DEBUG
|
|
}
|
|
|
|
void AddToExceptList( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
m_hsExceptDataList.add( id, id );
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "Add to Except List : %s(%d)\n", id.c_str(), m_hsExceptDataList.size());
|
|
#endif
|
|
}
|
|
|
|
void RemoveFromExceptList( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
m_hsExceptDataList.erase( id );
|
|
|
|
_TAG *pTag = NULL;
|
|
if( !m_hsData.lookup( id, pTag ) )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
pTag->last_access_time = now();
|
|
|
|
if( pTag->ref_count == 0 )
|
|
{
|
|
#ifdef LRU_DEBUG
|
|
//printf( "Added to LRU List : %s\n", pTag->id.c_str() );
|
|
_performance_print( "Added to LRU List : %s\n", pTag->id.c_str() );
|
|
#endif //LRU_DEBUG
|
|
addToLRUList( pTag );
|
|
}
|
|
}
|
|
|
|
bool IsInExceptList( _ID id )
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
return m_hsExceptDataList.has( id );
|
|
}
|
|
|
|
void ClearExceptList()
|
|
{
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
return m_hsExceptDataList.clear();
|
|
}
|
|
|
|
void EnumExceptList( std::vector< _ID > & vList )
|
|
{
|
|
bool res;
|
|
KHash< _ID, _KHASHPR >::node *_node = NULL;
|
|
|
|
THREAD_SYNCRONIZE( m_lckIntf );
|
|
|
|
res = m_hsExceptDataList.get_first_node( _node );
|
|
while ( res )
|
|
{
|
|
vList.push_back( _node->value );
|
|
res = m_hsExceptDataList.get_next_node( _node );
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
struct _TAG
|
|
{
|
|
_TAG() : ref_count( 0 ) {}
|
|
_TAG( unsigned int tm, _ID i, _T d ) : last_access_time( tm ), id( i ), data( d ), ref_count( 0 ) {}
|
|
|
|
typename std::list< struct _TAG* >::iterator it;
|
|
unsigned int last_access_time;
|
|
volatile int ref_count;
|
|
_ID id;
|
|
_T data;
|
|
};
|
|
|
|
void addToLRUList( _TAG *pTag )
|
|
{
|
|
/*
|
|
#ifndef NDEBUG
|
|
printf( "LRU count : %d (+1)\n", m_vLRU.size()+1 );
|
|
#endif
|
|
*/
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "LRU count : %d (+1)\n", m_vLRU.size()+1 );
|
|
#endif
|
|
// except 리스트에 있으면 LRU 관리 대상이 아니다.
|
|
if( m_hsExceptDataList.has( pTag->id ) ) return;
|
|
|
|
// 비어 있으면 그냥 추가
|
|
if( m_vLRU.empty() )
|
|
{
|
|
m_vLRU.push_back( pTag );
|
|
pTag->it = m_vLRU.begin();
|
|
return;
|
|
}
|
|
|
|
// 맨 뒤에 넣는 경우
|
|
if( m_vLRU.back()->last_access_time <= pTag->last_access_time )
|
|
{
|
|
pTag->it = m_vLRU.insert( m_vLRU.end(), pTag );
|
|
return;
|
|
}
|
|
|
|
// 자리 찾아서 넣자
|
|
std::list< _TAG* >::iterator it;
|
|
size_t index = 0;
|
|
for( it = m_vLRU.begin(); it != m_vLRU.end(); ++it, ++index )
|
|
{
|
|
if( (*it)->last_access_time < pTag->last_access_time ) continue;
|
|
|
|
pTag->it = m_vLRU.insert( it, pTag );
|
|
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "Insert LRU : %s\n", pTag->id.c_str());
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
void removeFromLRUList( _TAG *pTag )
|
|
{
|
|
pTag->it = m_vLRU.end();
|
|
|
|
for( std::list< _TAG* >::iterator it = m_vLRU.begin(); it != m_vLRU.end(); ++it )
|
|
{
|
|
if( (*it) != pTag ) continue;
|
|
|
|
m_vLRU.erase( it );
|
|
break;
|
|
}
|
|
/*
|
|
#ifndef NDEBUG
|
|
printf( "LRU count : %d\n", m_vLRU.size() );
|
|
#endif
|
|
*/
|
|
#ifdef LRU_DEBUG
|
|
_performance_print( "LRU count : %d\n", m_vLRU.size() );
|
|
#endif
|
|
}
|
|
|
|
unsigned int now()
|
|
{
|
|
return (unsigned int)time( NULL );
|
|
}
|
|
|
|
void add_ref( struct _TAG *pTag, int count )
|
|
{
|
|
pTag->ref_count += count;
|
|
if( pTag->ref_count < 0 )
|
|
{
|
|
assert( 0 && "pTag->ref_count < 0" );
|
|
}
|
|
|
|
// _performance_print( "Add_ref %s [%d] [%d]\n", pTag->id.c_str(), pTag->ref_count, count );
|
|
}
|
|
|
|
void deleteObject( _T & t )
|
|
{
|
|
_DESTROYER temp;
|
|
temp( t );
|
|
}
|
|
|
|
bool m_bAutoAddRef;
|
|
unsigned int m_nLastProcessTime;
|
|
unsigned int m_nExpireTime;
|
|
XCriticalSection m_lckIntf;
|
|
KHash< _TAG*, _KHASHPR > m_hsData;
|
|
KHash< _ID, _KHASHPR > m_hsExceptDataList;
|
|
std::list< _TAG* > m_vLRU; ///< 최근에 사용된 녀석은 뒤에 있다.
|
|
|
|
#ifndef NDEBUG
|
|
std::vector< _TAG* > m_vDBG;
|
|
#endif
|
|
|
|
}; |