//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: A cache of a bunch of CSharedObjects // //============================================================================= #include "stdafx.h" #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" namespace GCSDK { #ifdef GC static GCConVar add_object_clean_do_has_element( "add_object_clean_do_has_element", "0", 0, "Enables AddObjectClean() checking that the cache doesn't already have this pointer" ); #endif //---------------------------------------------------------------------------- // Purpose: Constructor //---------------------------------------------------------------------------- CSharedObjectTypeCache::CSharedObjectTypeCache( int nTypeID ) : m_nTypeID( nTypeID ) { } //---------------------------------------------------------------------------- // Purpose: Destructor //---------------------------------------------------------------------------- CSharedObjectTypeCache::~CSharedObjectTypeCache() { for ( int i = 0; i < m_vecObjects.Count(); i++ ) { // NULL the entry so that this SO isn't found during // cleanup assertion checking. CSharedObject *pObj = m_vecObjects[ i ]; m_vecObjects[ i ] = NULL; #ifdef GC if ( pObj->BShouldDeleteByCache() ) { #if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA --pObj->m_nRefCount; AssertMsg1( pObj->m_nRefCount == 0, "Destroying shared object %s that's still in use!", pObj->GetDebugString().String() ); #endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA delete pObj; } #else delete pObj; #endif } m_vecObjects.Purge(); } //---------------------------------------------------------------------------- // Purpose: Common shared add-to-cache code shared between AddObject() and // AddObjectClean(). //---------------------------------------------------------------------------- void CSharedObjectTypeCache::AddObjectInternal( CSharedObject *pObject ) { Assert( pObject ); m_vecObjects.AddToTail( pObject ); #ifdef GC #if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA AssertMsg1( pObject->m_nRefCount >= 0, "AddObjectInternal(): Invalid ref count for shared object %s", pObject->GetDebugString().String() ); ++pObject->m_nRefCount; #endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA #endif } //---------------------------------------------------------------------------- // Purpose: Adds a shared object of the appropriate type to this type cache. //---------------------------------------------------------------------------- bool CSharedObjectTypeCache::AddObject( CSharedObject *pObject ) { Assert( pObject ); Assert( m_nTypeID == pObject->GetTypeID() ); if( m_vecObjects.HasElement( pObject ) ) return false; AddObjectInternal( pObject ); return true; } //---------------------------------------------------------------------------- // Purpose: Adds an object without dirtying. This is done when the object // is just being loaded from SQL or memcached, so it's safe not to do the // has element check. //---------------------------------------------------------------------------- bool CSharedObjectTypeCache::AddObjectClean( CSharedObject *pObject ) { Assert( m_nTypeID == pObject->GetTypeID() ); #ifdef GC if ( add_object_clean_do_has_element.GetBool() ) { Assert( !m_vecObjects.HasElement( pObject ) ); if( m_vecObjects.HasElement( pObject ) ) return false; } #endif AddObjectInternal( pObject ); return true; } //---------------------------------------------------------------------------- // Purpose: Destroys the object matching the one passed in. This could be the // same one or simply one with matching index fields. //---------------------------------------------------------------------------- CSharedObject *CSharedObjectTypeCache::RemoveObject( const CSharedObject & soIndex ) { Assert( m_nTypeID == soIndex.GetTypeID() ); // This is probably harmless, but it's most likely a bug int nIndex = FindSharedObjectIndex( soIndex ); if( m_vecObjects.IsValidIndex( nIndex ) ) { return RemoveObjectByIndex( nIndex ); } else { return NULL; } } CSharedObject *CSharedObjectTypeCache::RemoveObjectByIndex( uint32 nObj ) { CSharedObject *pObj = m_vecObjects[nObj]; #ifdef GC #if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA AssertMsg1( pObj->m_nRefCount > 0, "Invalid ref count for shared object %s", pObj->GetDebugString().String() ); --pObj->m_nRefCount; #endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA #endif // GC m_vecObjects.Remove( nObj ); return pObj; } //---------------------------------------------------------------------------- // Purpose: Empties the object lists and deletes all elements //---------------------------------------------------------------------------- void CSharedObjectTypeCache::DestroyAllObjects() { for ( int i = 0; i < m_vecObjects.Count(); i++ ) { #ifdef GC if ( m_vecObjects[i]->BShouldDeleteByCache() ) { #if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA --m_vecObjects[i]->m_nRefCount; AssertMsg1( m_vecObjects[i]->m_nRefCount == 0, "Destroying shared object %s that's still in use!", m_vecObjects[i]->GetDebugString().String() ); #endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA delete m_vecObjects[i]; } #else delete m_vecObjects[i]; #endif } m_vecObjects.Purge(); } //---------------------------------------------------------------------------- // Purpose: Empties the object lists but doesn't delete any of the objects //---------------------------------------------------------------------------- void CSharedObjectTypeCache::RemoveAllObjectsWithoutDeleting() { m_vecObjects.RemoveAll(); } //---------------------------------------------------------------------------- // Purpose: Makes sure there's room in the object vector for the suggested // number of items //---------------------------------------------------------------------------- void CSharedObjectTypeCache::EnsureCapacity( uint32 nItems ) { m_vecObjects.EnsureCapacity( nItems ); } //---------------------------------------------------------------------------- // Purpose: Searches the object list for an object that matches the provided // object on its index fields. //---------------------------------------------------------------------------- CSharedObject *CSharedObjectTypeCache::FindSharedObject( const CSharedObject & soIndex ) { int nIndex = FindSharedObjectIndex( soIndex ); if( m_vecObjects.IsValidIndex( nIndex ) ) return m_vecObjects[nIndex]; else return NULL; } //---------------------------------------------------------------------------- // Purpose: Searches the object list for an object that matches the provided // object on its index fields. //---------------------------------------------------------------------------- int CSharedObjectTypeCache::FindSharedObjectIndex( const CSharedObject & soIndex ) const { FOR_EACH_VEC( m_vecObjects, nObj ) { if( m_vecObjects[nObj]->BIsKeyEqual( soIndex ) ) return nObj; } return -1; } //---------------------------------------------------------------------------- // Purpose: Dumps all the objects in the type cache //---------------------------------------------------------------------------- void CSharedObjectTypeCache::Dump() const { EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\tTypeCache for %d (%d objects):\n", GetTypeID(), m_vecObjects.Count() ); FOR_EACH_VEC( m_vecObjects, nObj ) { m_vecObjects[nObj]->Dump(); } } //---------------------------------------------------------------------------- // Purpose: Claims all the memory for the cache and its objects //---------------------------------------------------------------------------- #ifdef DBGFLAG_VALIDATE void CSharedObjectTypeCache::Validate( CValidator &validator, const char *pchName ) { VALIDATE_SCOPE(); ValidateObj( m_vecObjects ); FOR_EACH_VEC( m_vecObjects, nIndex ) { m_vecObjects[nIndex]->Validate( validator, "m_vecObjects[n]" ); } } #endif //---------------------------------------------------------------------------- // Purpose: Constructor //---------------------------------------------------------------------------- CSharedObjectCache::CSharedObjectCache( ) : m_mapObjects( DefLessFunc(int) ) , m_ulVersion( 0 ) { } //---------------------------------------------------------------------------- // Purpose: Destructor //---------------------------------------------------------------------------- CSharedObjectCache::~CSharedObjectCache() { FOR_EACH_MAP( m_mapObjects, nTypeIndex ) { delete m_mapObjects[nTypeIndex]; } m_mapObjects.Purge(); } //---------------------------------------------------------------------------- // Purpose: Returns the type cache for the specified type ID, returning NULL // if the cache didn't previously exist. //---------------------------------------------------------------------------- CSharedObjectTypeCache *CSharedObjectCache::FindBaseTypeCache( int nClassID ) const { int nIndex = m_mapObjects.Find( nClassID ); CSharedObjectTypeCache *pTypeCache = NULL; if( m_mapObjects.IsValidIndex( nIndex ) ) { pTypeCache = m_mapObjects[nIndex]; } return pTypeCache; } //---------------------------------------------------------------------------- // Purpose: Returns the type cache for the specified type ID, creating a new // cache and returning it if one didn't previously exist. Never intended // to return NULL. //---------------------------------------------------------------------------- CSharedObjectTypeCache *CSharedObjectCache::CreateBaseTypeCache( int nClassID ) { //see if we already have an existing one CSharedObjectTypeCache *pCache = FindBaseTypeCache( nClassID ); if( pCache ) return pCache; //nope, need to create one CSharedObjectTypeCache* pTypeCache = AllocateTypeCache( nClassID ); m_mapObjects.Insert( nClassID, pTypeCache ); #if 0 // Kyle says: this is the newer way of managing caches on Dota but we haven't // brought any of it over yet m_CacheObjects.AddToTail( pTypeCache ); //sort this cache for faster access std::sort( m_CacheObjects.begin(), m_CacheObjects.end(), SortCacheByTypeID ); #endif return pTypeCache; } //---------------------------------------------------------------------------- // Purpose: Adds a shared object to the cache. //---------------------------------------------------------------------------- bool CSharedObjectCache::AddObject( CSharedObject *pSharedObject ) { CSharedObjectTypeCache *pTypeCache = CreateBaseTypeCache( pSharedObject->GetTypeID() ); if ( !pTypeCache->AddObject( pSharedObject ) ) return false; MarkDirty(); return true; } //---------------------------------------------------------------------------- // Purpose: Removes the object matching the one passed in from this cache, // without destroying the actual object. //---------------------------------------------------------------------------- CSharedObject *CSharedObjectCache::RemoveObject( const CSharedObject & soIndex ) { CSharedObjectTypeCache *pTypeCache = FindBaseTypeCache( soIndex.GetTypeID() ); if( !pTypeCache ) return NULL; MarkDirty(); return pTypeCache->RemoveObject( soIndex ); } //---------------------------------------------------------------------------- // Purpose: Empties the object lists but doesn't delete any of the objects //---------------------------------------------------------------------------- void CSharedObjectCache::RemoveAllObjectsWithoutDeleting() { FOR_EACH_MAP_FAST( m_mapObjects, nType ) { m_mapObjects[nType]->RemoveAllObjectsWithoutDeleting(); } MarkDirty(); } //---------------------------------------------------------------------------- // Purpose: Searches the object list for an object that matches the provided // object on its index fields. //---------------------------------------------------------------------------- CSharedObject *CSharedObjectCache::FindSharedObject( const CSharedObject & soIndex ) { CSharedObjectTypeCache *pTypeCache = FindBaseTypeCache( soIndex.GetTypeID() ); if( pTypeCache ) return pTypeCache->FindSharedObject( soIndex ); else return NULL; } //---------------------------------------------------------------------------- // Purpose: Dumps all the objects in the type cache //---------------------------------------------------------------------------- void CSharedObjectCache::Dump() const { EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "SharedObjectCache for %s (%d types):\n", GetOwner().Render(), m_mapObjects.Count() ); FOR_EACH_MAP( m_mapObjects, nTypeIndex ) { m_mapObjects[nTypeIndex]->Dump(); } } //---------------------------------------------------------------------------- // Purpose: Claims all the memory for the cache //---------------------------------------------------------------------------- #ifdef DBGFLAG_VALIDATE void CSharedObjectCache::Validate( CValidator &validator, const char *pchName ) { VALIDATE_SCOPE(); ValidateObj( m_mapObjects ); FOR_EACH_MAP( m_mapObjects, nTypeIndex ) { m_mapObjects[nTypeIndex]->Validate( validator, "m_mapObjects[n]" ); } } #endif } // namespace GCSDK