//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #ifndef DATACACHE_H #define DATACACHE_H #ifdef _WIN32 #pragma once #endif #include "datamanager.h" #include "utlhash.h" #include "mempool.h" #include "tier0/tslist.h" #include "datacache_common.h" #include "tier3/tier3.h" //----------------------------------------------------------------------------- // // Data Cache class declarations // //----------------------------------------------------------------------------- class CDataCache; class CDataCacheSection; //----------------------------------------------------------------------------- struct DataCacheItemData_t { const void * pItemData; unsigned size; DataCacheClientID_t clientId; CDataCacheSection * pSection; }; //------------------------------------- #define DC_NO_NEXT_LOCKED ((DataCacheItem_t *)-1) #define DC_MAX_THREADS_FRAMELOCKED 6 struct DataCacheItem_t : DataCacheItemData_t { DataCacheItem_t( const DataCacheItemData_t &data ) : DataCacheItemData_t( data ), hLRU( INVALID_MEMHANDLE ) { memset( pNextFrameLocked, 0xff, sizeof(pNextFrameLocked) ); } static DataCacheItem_t *CreateResource( const DataCacheItemData_t &data ) { return new DataCacheItem_t(data); } static unsigned int EstimatedSize( const DataCacheItemData_t &data ) { return data.size; } void DestroyResource(); DataCacheItem_t *GetData() { return this; } unsigned int Size() { return size; } memhandle_t hLRU; DataCacheItem_t *pNextFrameLocked[DC_MAX_THREADS_FRAMELOCKED]; DECLARE_FIXEDSIZE_ALLOCATOR_MT(DataCacheItem_t); }; //------------------------------------- typedef CDataManager CDataCacheLRU; //----------------------------------------------------------------------------- // CDataCacheSection // // Purpose: Implements a sub-section of the global cache. Subsections are // areas of the cache with thier own memory constraints and common // management. //----------------------------------------------------------------------------- class CDataCacheSection : public IDataCacheSection { public: CDataCacheSection( CDataCache *pSharedCache, IDataCacheClient *pClient, const char *pszName ); ~CDataCacheSection(); IDataCache *GetSharedCache(); IDataCacheClient *GetClient() { return m_pClient; } const char *GetName() { return szName; } //-------------------------------------------------------- // IDataCacheSection methods //-------------------------------------------------------- virtual void SetLimits( const DataCacheLimits_t &limits ); const DataCacheLimits_t &GetLimits(); virtual void SetOptions( unsigned options ); virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ); inline unsigned GetNumBytes() { return m_status.nBytes; } inline unsigned GetNumItems() { return m_status.nItems; } inline unsigned GetNumBytesLocked() { return m_status.nBytesLocked; } inline unsigned GetNumItemsLocked() { return m_status.nItemsLocked; } inline unsigned GetNumBytesUnlocked() { return m_status.nBytes - m_status.nBytesLocked; } inline unsigned GetNumItemsUnlocked() { return m_status.nItems - m_status.nItemsLocked; } virtual void EnsureCapacity( unsigned nBytes, unsigned nItems = 1 ); //-------------------------------------------------------- virtual bool Add( DataCacheClientID_t clientId, const void *pItemData, unsigned size, DataCacheHandle_t *pHandle ); virtual bool AddEx( DataCacheClientID_t clientId, const void *pItemData, unsigned size, unsigned flags, DataCacheHandle_t *pHandle ); virtual DataCacheHandle_t Find( DataCacheClientID_t clientId ); virtual DataCacheRemoveResult_t Remove( DataCacheHandle_t handle, const void **ppItemData = NULL, unsigned *pItemSize = NULL, bool bNotify = false ); virtual bool IsPresent( DataCacheHandle_t handle ); //-------------------------------------------------------- virtual void *Lock( DataCacheHandle_t handle ); virtual int Unlock( DataCacheHandle_t handle ); virtual void *Get( DataCacheHandle_t handle, bool bFrameLock = false ); virtual void *GetNoTouch( DataCacheHandle_t handle, bool bFrameLock = false ); virtual void GetAndLockMultiple( void **ppData, int nCount, DataCacheHandle_t *pHandles ); virtual void LockMutex(); virtual void UnlockMutex(); //-------------------------------------------------------- virtual int BeginFrameLocking(); virtual bool IsFrameLocking(); virtual void *FrameLock( DataCacheHandle_t handle ); virtual int EndFrameLocking(); //-------------------------------------------------------- virtual int GetLockCount( DataCacheHandle_t handle ); virtual int BreakLock( DataCacheHandle_t handle ); //-------------------------------------------------------- virtual int *GetFrameUnlockCounterPtr(); int m_nFrameUnlockCounter; //-------------------------------------------------------- virtual bool Touch( DataCacheHandle_t handle ); virtual bool Age( DataCacheHandle_t handle ); //-------------------------------------------------------- virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ); virtual unsigned Purge( unsigned nBytes ); unsigned PurgeItems( unsigned nItems ); //-------------------------------------------------------- virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT ); virtual void UpdateSize( DataCacheHandle_t handle, unsigned int nNewSize ); virtual unsigned int GetOptions(); private: friend void DataCacheItem_t::DestroyResource(); virtual void OnAdd( DataCacheClientID_t clientId, DataCacheHandle_t hCacheItem ) {} virtual DataCacheHandle_t DoFind( DataCacheClientID_t clientId ); virtual void OnRemove( DataCacheClientID_t clientId ) {} memhandle_t GetFirstUnlockedItem(); memhandle_t GetFirstLockedItem(); memhandle_t GetNextItem( memhandle_t ); DataCacheItem_t *AccessItem( memhandle_t hCurrent ); bool DiscardItem( memhandle_t hItem, DataCacheNotificationType_t type ); bool DiscardItemData( DataCacheItem_t *pItem, DataCacheNotificationType_t type ); void NoteAdd( int size ); void NoteRemove( int size ); void NoteLock( int size ); void NoteUnlock( int size ); void NoteSizeChanged( int oldSize, int newSize ); // for debugging only, under user cvar enabling, causes datacache stress flushing void ForceFlushDebug( bool bFlush ); struct FrameLock_t { int m_iLock; DataCacheItem_t *m_pFirst; int m_iThread; }; //typedef CThreadLocal CThreadFrameLock; CDataCacheLRU & m_LRU; FrameLock_t * m_FrameLocks[MAX_THREADS_SUPPORTED]; DataCacheStatus_t m_status; DataCacheLimits_t m_limits; IDataCacheClient * m_pClient; unsigned m_options; CDataCache * m_pSharedCache; char szName[DC_MAX_CLIENT_NAME + 1]; CTSSimpleList m_FreeFrameLocks; protected: CThreadFastMutex & m_mutex; }; //----------------------------------------------------------------------------- // CDataCacheSectionFastFind // // Purpose: A section variant that allows clients to have cache support tracking // efficiently (a true cache, not just an LRU) //----------------------------------------------------------------------------- class CDataCacheSectionFastFind : public CDataCacheSection { public: CDataCacheSectionFastFind(CDataCache *pSharedCache, IDataCacheClient *pClient, const char *pszName ) : CDataCacheSection( pSharedCache, pClient, pszName ) { m_Handles.Init( 1024 ); } private: virtual DataCacheHandle_t DoFind( DataCacheClientID_t clientId ); virtual void OnAdd( DataCacheClientID_t clientId, DataCacheHandle_t hCacheItem ); virtual void OnRemove( DataCacheClientID_t clientId ); CUtlHashFast m_Handles; }; //----------------------------------------------------------------------------- // CDataCache // // Purpose: The global shared cache. Manages sections and overall budgets. // //----------------------------------------------------------------------------- class CDataCache : public CTier3AppSystem< IDataCache > { typedef CTier3AppSystem< IDataCache > BaseClass; public: CDataCache(); //-------------------------------------------------------- // IAppSystem methods //-------------------------------------------------------- virtual bool Connect( CreateInterfaceFn factory ); virtual void Disconnect(); virtual void *QueryInterface( const char *pInterfaceName ); virtual InitReturnVal_t Init(); virtual void Shutdown(); //-------------------------------------------------------- // IDataCache methods //-------------------------------------------------------- virtual void SetSize( int nMaxBytes ); virtual void SetOptions( unsigned options ); virtual void SetSectionLimits( const char *pszSectionName, const DataCacheLimits_t &limits ); virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ); //-------------------------------------------------------- virtual IDataCacheSection *AddSection( IDataCacheClient *pClient, const char *pszSectionName, const DataCacheLimits_t &limits = DataCacheLimits_t(), bool bSupportFastFind = false ); virtual void RemoveSection( const char *pszClientName, bool bCallFlush = true ); virtual IDataCacheSection *FindSection( const char *pszClientName ); //-------------------------------------------------------- void EnsureCapacity( unsigned nBytes ); virtual unsigned Purge( unsigned nBytes ); virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ); //-------------------------------------------------------- virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT, const char *pszSection = NULL ); //-------------------------------------------------------- inline unsigned GetNumBytes() { return m_status.nBytes; } inline unsigned GetNumItems() { return m_status.nItems; } inline unsigned GetNumBytesLocked() { return m_status.nBytesLocked; } inline unsigned GetNumItemsLocked() { return m_status.nItemsLocked; } inline unsigned GetNumBytesUnlocked() { return m_status.nBytes - m_status.nBytesLocked; } inline unsigned GetNumItemsUnlocked() { return m_status.nItems - m_status.nItemsLocked; } virtual int GetSectionCount( void ); virtual const char *GetSectionName( int iIndex ); private: //----------------------------------------------------- friend class CDataCacheSection; //----------------------------------------------------- DataCacheItem_t *AccessItem( memhandle_t hCurrent ); bool IsInFlush() { return m_bInFlush; } int FindSectionIndex( const char *pszSection ); // Utilities used by the data cache report void OutputItemReport( memhandle_t hItem, void *pXboxData = NULL ); static bool SortMemhandlesBySizeLessFunc( const memhandle_t& lhs, const memhandle_t& rhs ); //----------------------------------------------------- CDataCacheLRU m_LRU; DataCacheStatus_t m_status; CUtlVector m_Sections; bool m_bInFlush; CThreadFastMutex & m_mutex; }; //--------------------------------------------------------- extern CDataCache g_DataCache; //----------------------------------------------------------------------------- inline DataCacheItem_t *CDataCache::AccessItem( memhandle_t hCurrent ) { return m_LRU.GetResource_NoLockNoLRUTouch( hCurrent ); } //----------------------------------------------------------------------------- inline IDataCache *CDataCacheSection::GetSharedCache() { return m_pSharedCache; } inline DataCacheItem_t *CDataCacheSection::AccessItem( memhandle_t hCurrent ) { return m_pSharedCache->AccessItem( hCurrent ); } // Note: if status updates are moved out of a mutexed section, will need to change these to use interlocked instructions inline void CDataCacheSection::NoteSizeChanged( int oldSize, int newSize ) { int nBytes = ( newSize - oldSize ); m_status.nBytes += nBytes; m_status.nBytesLocked += nBytes; ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytes, nBytes ); ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytesLocked, nBytes ); } inline void CDataCacheSection::NoteAdd( int size ) { m_status.nBytes += size; m_status.nItems++; ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytes, size ); ThreadInterlockedIncrement( &m_pSharedCache->m_status.nItems ); } inline void CDataCacheSection::NoteRemove( int size ) { m_status.nBytes -= size; m_status.nItems--; ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytes, -size ); ThreadInterlockedDecrement( &m_pSharedCache->m_status.nItems ); } inline void CDataCacheSection::NoteLock( int size ) { m_status.nBytesLocked += size; m_status.nItemsLocked++; ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytesLocked, size ); ThreadInterlockedIncrement( &m_pSharedCache->m_status.nItemsLocked ); } inline void CDataCacheSection::NoteUnlock( int size ) { m_status.nBytesLocked -= size; m_status.nItemsLocked--; ThreadInterlockedExchangeAdd( &m_pSharedCache->m_status.nBytesLocked, -size ); ThreadInterlockedDecrement( &m_pSharedCache->m_status.nItemsLocked ); // something has been unlocked, assume cached pointers are now invalid m_nFrameUnlockCounter++; } //----------------------------------------------------------------------------- #endif // DATACACHE_H