// code by Testors 2005/03/24 #pragma once #include #include #include #include /* #include #include "LRUCacheContainer.h" //#include 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 };