Files
2026-06-01 12:46:52 +02:00

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
};