/*++ cintrnl.h This file contains internal definitions for the cache library - this stuff should not be of interest for most users. --*/ #ifndef _CINTRNL_H_ #define _CINTRNL_H_ class CScheduleThread { /*++ Class Description : This is a base class for those objects which wish to be called on a regularily scheduled basis. The constructors for this class will automatically put the object in a doubly linked list walked by a background thread which periodically executes a virtual function. --*/ private : // // Special constructor for the 'Head' element of the // doubly linked list this class maintains. // CScheduleThread( BOOL fSpecial ) ; protected : // // Has the scheduler been initialized ? // static BOOL s_fInitialized ; // // Crit sect protecting doubly linked list. // static CRITICAL_SECTION s_critScheduleList ; // // Handle to event used to terminate background thread. // static HANDLE s_hShutdown ; // // Handle to background thread. // static HANDLE s_hThread ; // // The head element of the doubly linked list. // static CScheduleThread s_Head ; // // The thread which calls our virtual functions // static DWORD WINAPI ScheduleThread( LPVOID lpv ) ; // // Previous and Next pointers which maintain doubly linked // list of scheduled itesm. // class CScheduleThread* m_pPrev ; class CScheduleThread* m_pNext ; protected : // // Derived classes should override this function - // it will be called on a regular basis by the scheduler thread. // virtual void Schedule( void ) {} // // Constructor and Destructor automagically manage // insertion into doubly linked list of other scheduled items. // These are protected as we want people to buid only // derived objects which use this. // CScheduleThread() ; // // Member functions which put us into the regular schedule ! // void AddToSchedule() ; void RemoveFromSchedule() ; public : // // Initialize the class - don't construct // any derived objects before this is called. // static BOOL Init() ; // // Terminate class and background thread ! // static void Term() ; // // Global which tells clients how frequently they // will be called ! // static DWORD dwNotificationSeconds ; // // Destructor is protected - we should only be invoked // by derived class constructors // virtual ~CScheduleThread() ; } ; class CacheState : public ICacheRefInterface { /*++ Class Description : This class will provide the base support for LRU removal of items in the cache ! Base Class : CQElement - we put these objects into a TLockQueue to amortize LRU operations ! --*/ private : // // The following operations are not allowed ! // CacheState() ; CacheState( CacheState& ) ; CacheState& operator=( CacheState& ) ; // // Tell us if this entry is older than the specified time ! // BOOL OlderThan( FILETIME& filetime ) { return CompareFileTime( &m_LastAccess, &filetime ) <= 0 ; } protected : // // The LRU List which holds all of these items gets to // know are innards ! // friend class CLRUList ; // // For debug purposes - make the memory recognizable ! // DWORD m_dwSignature ; // // The lock used to protect operations within the derived // classes of this object - most operations within this class // don't require locking, however all derived classes need // various locking services, so we provide one lock for all users ! // CACHELOCK m_lock ; // // Our signaure, and constants for supporting us implementing two // kinds of reference counts ! // enum CACHESTATE_CONSTANTS { CACHESTATE_SIGNATURE = 'hcaC', CLIENT_REF = 0x10000, CLIENT_BITS = 0xFFFF0000 } ; // // Keep a reference count - number of references holding // this in memory ! // NOTE : we hold two types of references in here - // regular references added by AddRef() and Release() // which are references from other caches // As well as client references. To Add a client reference // add CLIENT_REF atomically to this ! // volatile long m_cRefs ; // // a long that we use as a lock for calls to LRUReference // volatile long m_lLRULock ; // // The last time this was touched - helps LRU algorithm ! // FILETIME m_LastAccess ; // // The LRU List that owns this object ! // public: class CLRUList* m_pOwner ; // // Pointers to maintain doubly linked list of // items in the LRU chain ! // protected: DLIST_ENTRY m_LRUList ; // // structure to keep track of all the References to a // particular item ! - note m_lock is used to protect // access to this list // DLIST_ENTRY m_ReferencesList ; // // The field used to chain in Hash Table buckets // DLIST_ENTRY m_HashList ; // // This Constructor is protected as we only ever want to see // classes derived from this thing created ! // CacheState( class CLRUList*, long cClientRefs = 1 ) ; // // Destructor must be virtual - lots of derived classes ! // virtual ~CacheState() ; // // Touch the structure in a fashion so that the LRU state // is updated ! // void LRUReference( class CLRUList* pLRU ) ; // // calling this function insures that the correct Destructor is called ! // void virtual Destroy( void* pv ) = 0 ; // // Reference counting support - Add a reference // long AddRef() ; // // Keeping track of clients - remove a client ref ! // long CheckIn( class CAllocatorCache* pAlloc = 0 ) ; // // Keeping track of clients - remove a client ref ! // long CheckInNoLocks( class CAllocatorCache* pAlloc = 0 ) ; virtual BOOL IsMasterReference() { return TRUE ; } virtual CacheState* GetMasterReference() { return this ; } CacheState* AddRefMaster( ) { TraceFunctEnter( "CacheState::AddRefMaster()" ) ; m_lock.ShareLock() ; CacheState* pReturn = GetMasterReference() ; if( pReturn ) { long l = pReturn->AddRef() ; DebugTrace( DWORD_PTR(pReturn), "Added a reference to %x this %x", pReturn, this ) ; } m_lock.ShareUnlock() ; return pReturn ; } public : #ifdef DEBUG // // The number of these things that have been allocated ! // static long g_cCacheState ; #endif // // Remove a reference - when we return 0 we're destroyed ! // long Release( class CAllocatorCache *pAlloc, void* pv ) ; // // Provided to deal with failures during initialization of items // being insert into the cache - this function ensures that the // cache item ends up on the list for destruction ! // void FailedCheckOut( class CLRUList* p, long cClientRefs, CAllocatorCache* pAllocator, void* pv ) ; // // For allocating these we use this special operator new // which goes through our allocation cache ! // // NOTE : We take a reference because the cache MUST be provided ! // void* operator new( size_t size, class CAllocatorCache& cache ) { return cache.Allocate( size ) ; } // // Exclusive Lock ourselves ! // void ExclusiveLock() { m_lock.ExclusiveLock() ; } // // Unlock ourselves // void ExclusiveUnlock() { m_lock.ExclusiveUnlock() ; } // // Keeping track of clients - Add a client ref ! // long CheckOut( class CLRUList* p, long cClientRefs = 1 ) ; long ExternalCheckIn( ) ; long ExternalCheckInNoLocks( ) ; // // Check to see whether this element is still referenced // by the hash table containing the cache ! // BOOL InCache() { return !m_HashList.IsEmpty() ; } // // Check to see whether any Cache clients have a // reference to this item ! // Return TRUE if any clients have added a reference ! // BOOL IsCheckedOut() { _ASSERT( m_dwSignature == CACHESTATE_SIGNATURE ) ; return (m_cRefs & CLIENT_BITS) != 0 ; } // // Return TRUE if this item is in our LRU list ! // BOOL IsInLRUList() { _ASSERT( m_dwSignature == CACHESTATE_SIGNATURE ) ; // return whether we are in the LRU List ! return !m_LRUList.IsEmpty() ; } #ifdef DEBUG // // This is used in _ASSERT's and stuff to check that the cache is correctly ordered // by time ! // BOOL IsOlder( FILETIME filetimeIn, FILETIME& filetimeOut ) ; #endif // // The following support functions are used to support manipulating // these objects in the various kinds of doubly linked lists we may reside in // // BOOL FLockCandidate( BOOL fExpireChecks, FILETIME& filetime, BOOL& fToYoung ) ; // // The following function locks an element of the cache. // If this is the master cache entry, we will also look all associated items and return TRUE. // If this is not the master, we lock only the one entry ! // BOOL FLockExpungeCandidate( CacheState*& pMaster ) ; // // This function pairs with FLockExpungeCandidate() // void ReleaseLocks( CacheState* pMaster ) ; // // This is the other half of FLockExpungeCandidate - // we ensure the destruction of the selected item ! // void FinishCandidate( CacheState* pMaster ) ; // // The following is not thread safe and is only for use during // shutdown, where there should be no thread issues ! // void IsolateCandidate() ; // // This removes the item from the LRU List - Cache lock must be held exclusive or partially ! // void RemoveFromLRU() ; // // Define the Pointer to Function type that is so usefull // for using the DLIST templates ! // typedef DLIST_ENTRY* (*PFNDLIST)( class CacheState* pState ) ; // // Helper function for Doubly linked lists of these items in the // Cache's hash tables ! // inline static DLIST_ENTRY* HashDLIST( CacheState* p ) { return &p->m_HashList ; } // // Helper function for Doubly linked lists of these items in the // LRU lists // inline static DLIST_ENTRY* LRUDLIST( CacheState* p ) { return &p->m_LRUList ; } // // Helper function for Doubly linked lists of these items in // the reference lists - the list of items referencing the same // cache item ! // inline static DLIST_ENTRY* REFSDLIST( CacheState* p ) { return &p->m_ReferencesList ; } } ; // // Now Define some Doubly Linked Lists that we can use to manipulate the // items in the cache in various ways ! // typedef TDListHead< CacheState, &CacheState::LRUDLIST > LRULIST ; typedef TDListHead< CacheState, &CacheState::REFSDLIST > REFSLIST ; typedef TDListIterator< REFSLIST > REFSITER ; class CacheTable : public CScheduleThread { /*++ Class Description : This class defines a call back interface which we hand to the LRU List to manipulate the items in the cache ! --*/ public : // // Get the lock we use for manipulating the lock state of the table ! // virtual CACHELOCK& GetLock() = 0 ; // // Remove an item from the Cache's hash table ! // virtual BOOL RemoveEntry( CacheState* pEntry ) = 0 ; // // Ask if we want to remove this particular item ! // virtual BOOL QueryRemoveEntry( CacheState* pEntry ) = 0 ; } ; class CLRUList { /*++ Class Description : This class implements our LRU algorithms and selects the elements that are to be deleted from the cache ! --*/ private : // // The head of the LRU List ! // LRULIST m_LRUList ; // // A list of items that have been touched in the LRU List ! // TLockQueue< CacheState > m_lqModify ; // // Maximum number of elements we should hold in this cache ! // DWORD m_cMaxElements ; // // Number of items we inserted ! // DWORD m_dwAverageInserts ; // // Number of items NOT in the LRU List ! // DWORD m_cCheckedOut ; // // The number of subtract from the current time to // determine our expire date ! // ULARGE_INTEGER m_qwExpire ; // // This function actually selects the items that will be expired // void SelectExpirations( DLIST_ENTRY& expireList ) ; // // Do the expires ! // DWORD DoExpirations( DLIST_ENTRY& expireList ) ; // // Don't allow copies! // CLRUList( CLRUList& ) ; CLRUList& operator=( CLRUList& ) ; public : // // Number of items in the cache ! // long m_cItems ; //CLRUList( ULARGE_INTEGER qwExpire ) ; CLRUList() ; // // Initialize the LRU List with the parameters // controlling the maximum number of elements and the time to live ! // void Init( DWORD cMaxInstances, DWORD cLifeTimeSeconds ) ; // // Something has changed - put it in the list of items needing // to be examined ! // // This function should only be called when the containing cache has // a lock on the cache ! // void AddWorkQueue( CacheState* pbase ) ; // // This function examines each item in the Work Queue and does // appropriate processing ! // void ProcessWorkQueue( CAllocatorCache* pAllocCache, LPVOID lpv ) ; // // This function drains each item out of the Work Queue and releases // them - called during shutdown/destruction of a cache ! // void DrainWorkQueue() ; // // Bump the number of items in the cache ! // long IncrementItems() { return InterlockedIncrement( &m_cItems ) ; } // // Decrease the number of items in the cache ! // long DecrementItems() { return InterlockedDecrement( &m_cItems ) ; } // // Do the work required to expire an item ! // void Expire( CacheTable* pTable, CAllocatorCache* pCache, DWORD& countExpired, void* pv //per cache data ! ) ; BOOL Empty( CacheTable* pTable, CAllocatorCache* pCache, void* pv ) ; // // Remove a random set of elements in the table ! // void ExpungeItems( CacheTable* pTable, DWORD& countExpunged ) ; } ; template< class Data > class CCacheItemBase : public CacheState { /*++ Class Desceription : This class provides the interface defination for items in the cache which may or may not hold the key as well within the cache ! Arguments : Data - the item we will hold within the Cache --*/ protected: // // Constructor - initialize stuff to zero ! // CCacheItemBase( class CLRUList* p, Data* pData, BOOL fClientRef ) : CacheState( p, fClientRef ), m_pData( pData ) { } ~CCacheItemBase() { // // Somebody else must free our data elements ! // _ASSERT(m_pData == 0 ) ; } BOOL IsMasterReference() { return m_pData == 0 || m_pData->m_pCacheRefInterface == this ; } CacheState* GetMasterReference( ) { if( m_pData == 0 ) { return this ; } return (CacheState*)m_pData->m_pCacheRefInterface ; } public : // // The actual item we are holding within the Cache ! // Data* m_pData ; // // Check that our data items match ! // used for _ASSERT's // BOOL IsMatch( Data* p ) { return p == m_pData ; } // // Must be able extract the Data Item ! // Data* PublicData( class CLRUList* p ) { /*++ Routine Description : This function adds a client reference to an item in the cache, and sets up the necessary LRU Manipulations! We also return the data pointer to the client, and add a reference to ourselves to keep track of ! Arguments : p - the LRUList that owns us ! Return Value : Pointer to the Data item we contain - can be NULL ! --*/ TraceFunctEnter( "CCacheItemBase::PublicData" ) ; _ASSERT( p != 0 ) ; _ASSERT( p == m_pOwner ) ; Data* pReturn = 0 ; m_lock.ShareLock() ; DebugTrace( (DWORD_PTR)this, "m_pData %x m_pCacheRef %x", m_pData, m_pData ? m_pData->m_pCacheRefInterface : 0 ) ; if( m_pData ) { CacheState* pState = (CacheState*)m_pData->m_pCacheRefInterface ; pState->CheckOut( p ) ; pReturn = m_pData ; } // // If there's no data in this item - it must not be checked out ! // _ASSERT( pReturn || !IsCheckedOut() ) ; m_lock.ShareUnlock() ; return pReturn ; } // // This is called when the locks are NOT held - // do all the correct logic for setting this item up ! // BOOL SetData( Data* pData, CLRUList* pList, long cClientRefs ) { /*++ Routine Description : Make this Cache Element point to some piece of data that was provided by the end user. NOTE : The piece of data may be referenced by another cache - in which case we must work well with it !!!! Arguments : pData - the Item the client wants us to refer to with our key pList - the LRU List containing us ! fReference - Is the client putting the item in the cache and not keeping his reference, this is TRUE if --*/ TraceFunctEnter( "CCacheItemBase::SetData" ) ; BOOL fReturn = FALSE ; _ASSERT( pData != 0 ) ; _ASSERT( pList != 0 ) ; _ASSERT( pList == m_pOwner ) ; DebugTrace( (DWORD_PTR)this, "pData %x pList %x cClientRefs %x", pData, pList, cClientRefs ) ; // // Is this an item referenced by another cache - // NOTE : IF it is an item from another Cache it MUST be checked out with a // client reference ! The m_pCacheRefInterface of an item MUST NEVER CHANGED // as long as an item is checked out by clients, so the following dereference // is safe ! // CCacheItemBase* p = (CCacheItemBase*)pData->m_pCacheRefInterface ; DebugTrace( (DWORD_PTR)this, "Examined Item - m_pCacheRefInterface %x", p ) ; if( p != 0 ) { // // In this scenario it is meaningless to access for a client reference - // the client MUST have gotten one from the other cache ! // _ASSERT( cClientRefs == 0 ) ; _ASSERT( p->IsCheckedOut() ) ; // // Ok make our cache reference the same item ! // fReturn = p->AddCacheReference( this, pData ) ; // // This can fail if the user tries to insert the same name twice ! // //_ASSERT( fReturn ) ; } else { m_lock.ExclusiveLock() ; if( !m_pData ) { // // The Cache should never have refences outstanding with // the data pointer equal to NULL - so _ASSERT that we're // not checked out by a client ! // _ASSERT( !IsCheckedOut() ) ; // // Now point to the data the client wants us to point at ! // m_pData = pData ; pData->m_pCacheRefInterface = this ; // // It worked - return TRUE ! // fReturn = TRUE ; // // Now we are checked out by a client ! // if( cClientRefs ) CheckOut( pList, cClientRefs ) ; } m_lock.ExclusiveUnlock() ; } return fReturn ; } // // Add an item to the list of caches referencing // this cache item ! // BOOL InsertRef( CCacheItemBase* p, Data* pData, long cClientRefs = 0 ) { /*++ Routine Description : --*/ TraceFunctEnter( "CCacheItemBase::InsertRef" ) ; _ASSERT( p != 0 ) ; _ASSERT( pData != 0 ) ; _ASSERT( IsCheckedOut() ) ; _ASSERT( pData->m_pCacheRefInterface == this ) ; BOOL fReturn = FALSE ; REFSITER refsiter( &m_ReferencesList ) ; DebugTrace( (DWORD_PTR)this, "m_pData %x p %x p->m_pData %x cClientRefs %x", m_pData, p, p->m_pData, cClientRefs ) ; if( m_pData != 0 ) { // // Now grab the second lock ! // // This _ASSERT isn't valid, the user can insert names that are already in use ! // //_ASSERT( p->m_pData == 0 || p->m_pData == m_pData ) ; if( p->m_pData == 0 ) { // // Insert p into the list of item in the list ! // refsiter.InsertBefore( p ) ; // // We've added another reference to ourself ! // This comes from another Cache item, so count it specially ! // long l = AddRef() ; DebugTrace( (DWORD_PTR)this, "AddRef result %x this %x p %x", l, this, p ) ; // // Give a reference to the data we are holding ! // p->m_pData = m_pData ; fReturn = TRUE ; } else if( p->m_pData == m_pData ) { fReturn = TRUE ; } // This _ASSERT isn't valid, the user can insert names that are already in use ! // //_ASSERT( p->m_pData == m_pData ) ; } if( fReturn && cClientRefs ) { CheckOut( m_pOwner, cClientRefs ) ; } return fReturn ; } // // Add an item to the list of caches referencing // this cache item ! // virtual BOOL AddCacheReference( class ICacheRefInterface* pInterface, void* pv, BOOL fReference = FALSE ) { /*++ Routine Description : --*/ TraceFunctEnter( "CCacheItemBase::AddCacheReference" ) ; Data* pData = (Data*)pv ; CCacheItemBase* p = (CCacheItemBase*)pInterface ; BOOL fReturn = TRUE ; _ASSERT( pData != 0 ) ; _ASSERT( p != 0 ) ; REFSITER refsiter( &m_ReferencesList ) ; DebugTrace( (DWORD_PTR)this, "pInterface %x pv %x fReference %x", pInterface, pv, fReference ) ; m_lock.ExclusiveLock() ; p->m_lock.ExclusiveLock() ; fReturn = InsertRef( p, pData, fReference ) ; p->m_lock.ExclusiveUnlock() ; _ASSERT( m_pData == pData ) ; m_lock.ExclusiveUnlock() ; return fReturn ; } // // Remove an item from the list of caches referencing // this cache item ! // virtual BOOL RemoveCacheReference( BOOL fQueue ) { TraceFunctEnter( "CCacheItemBase::RemoveCacheReference" ) ; DebugTrace( (DWORD_PTR)this, "m_pData %x m_pData->m_pCacheRefInterface %x", m_pData, m_pData ? m_pData->m_pCacheRefInterface: 0 ) ; m_pData = 0 ; // // Well now - we're on our own - we should be expired ! // if( fQueue ) LRUReference( m_pOwner ) ; // // p no longer has a reference - you can remove it ! // return FALSE ; } // // Remove all references to the cache item ! // virtual BOOL RemoveAllReferences( ) { return FALSE ; } } ; template< class Data, class Key, class Constructor, class PerCacheData > class CCacheItemKey : public CCacheItemBase< Data > { /*++ Class Description : This class item holds the key of the item we are referencing within the cache ! We don't assume that the data objects hold the keys for us - we do this ourselves. --*/ private : // // This is the type of the key we are going to be holding ! // Key m_key ; // // Make all these constructors and copiers private ! // CCacheItemKey() ; CCacheItemKey& operator=( CCacheItemKey& ) ; protected : // // Destroy ourselves // void Destroy( void* pv ) { TraceFunctEnter( "CCacheItemKey::Destroy" ) ; DebugTrace( (DWORD_PTR)this, "m_pData %x pv %x", m_pData, pv ) ; PerCacheData* p = (PerCacheData*)pv ; if( m_pData ) Constructor::StaticRelease( m_pData, pv ) ; m_pData = 0 ; CCacheItemKey::~CCacheItemKey() ; } public : ~CCacheItemKey() { if( m_pData ) { Constructor::StaticRelease( m_pData, 0 ) ; m_pData = 0 ; } } // // Can only create by initializing the key ! // CCacheItemKey( class CLRUList* p, Key& k, Data* pData, long cClientRefs ) : CCacheItemBase( p, pData, cClientRefs ), m_key( k ) { } Key* GetKey() { return &m_key ; } // // This is called when no locks are held - assumes // that we may find that data is in the item, or that // we need to build it ! // Data* FindOrCreate( CACHELOCK& cachelock, Constructor& constructor, PerCacheData& cachedata, CLRUList* plrulist, class CacheStats* pStats ) { /*++ Routine Description : This function executes our creation protocol with the client. The constructor.Create() function is called to create a partially constructed Item for the cache. We will check if the returned item is referenced in another cache - if it is we will build the list of CacheState objects referencing the same cache item. The caller assumes that cachelock is released by the time we return ! Arguments : cachelock - The lock for the containing cache, we get this so that we can minimize the time it is held ! constructor - The object that can build an item for the cache ! cachedata - Some client data that they get for free ! plrulist - The LRU list that we should use ! Return Value : Pointer to a Data item if successfull ! --*/ TraceFunctEnter( "CCacheItemKey::FindOrCreate" ) ; _ASSERT( plrulist == m_pOwner || plrulist == 0 ) ; DebugTrace( (DWORD_PTR)this, "plrulist %x", plrulist ) ; Data* pReturn = 0 ; // // First look to see if there happens to be something here already ! // m_lock.ShareLock() ; if( m_pData ) { DebugTrace( (DWORD_PTR)this, "m_pData %x m_pData->m_pCacheRefInterface %x", m_pData, m_pData->m_pCacheRefInterface ) ; // // We've found this item constructed in the cache - // so check it out, AND put on the LRU work list ! // CacheState* pState = (CacheState*)m_pData->m_pCacheRefInterface ; pState->CheckOut( plrulist ) ; pReturn = m_pData ; } m_lock.ShareUnlock() ; DebugTrace( (DWORD_PTR)this, "pReturn %x", pReturn ) ; // // Did we find something ! // if( pReturn ) { // // Because caller assumes this is unlocked - do so now ! // cachelock.PartialUnlock() ; } else { // // An item with no data - Must not be checked out ! // _ASSERT( !IsCheckedOut() ) ; _ASSERT( m_pData == 0 ) ; // // Partially build the data object we want to hold ! // Data* pData = constructor.Create( m_key, cachedata ) ; DebugTrace( (DWORD_PTR)this, "Created pData %x", pData ) ; if( !pData ) { // // We failed - release our locks and go away ! // cachelock.PartialUnlock() ; } else { // // Figure out if we got a reference to an item already in the cache ! // CCacheItemBase* p = (CCacheItemBase*)pData->m_pCacheRefInterface ; // // Grab the locks in the correct order for what we might need ! // if( p ) { p->ExclusiveLock() ; // // If he gave us an item, it must be checked out of whatever cache // it came from - this ensures that it will not be destroyed while we // access it, because we are not at the point of adding our own reference // yet ! // _ASSERT( p->IsCheckedOut() ) ; _ASSERT( p->IsMatch( pData ) ) ; } m_lock.ExclusiveLock() ; // // Don't need to hold onto the cache anymore ! // cachelock.PartialUnlock() ; DebugTrace( (DWORD_PTR)this, "Create path - pData %x p %x m_pData %x", pData, p, m_pData ) ; // // Now do whatever is necessary to finish initialization ! - // must always call Init() unless we're going to give up on this thing ! // // // We should not change state as long as we've been holding either // the cachelock or our item lock up until this point - which is the case ! // _ASSERT( m_pData == 0 ) ; if( pData->Init( m_key, constructor, cachedata ) ) { if( !p ) { m_pData = pData ; pData->m_pCacheRefInterface = this ; pReturn = m_pData ; CheckOut( m_pOwner ) ; } else { // // NOTE : If the client's constructor gave us an object from another // cache they MUST add the client's reference for us - so we pass 0 to // InsertRef(), so that we don't add yet another reference. // if( p->InsertRef( this, pData, 0 ) ) { // // Insert Ref should setup our m_pData pointer ! // _ASSERT( m_pData == pData ) ; pReturn = m_pData ; } } } DebugTrace( (DWORD_PTR)this, "Create path - pReturn %x", pReturn ) ; // // NOTE : // If pReturn==0 indicating some kind of error, than there should // have been no client refs added at this point ! // _ASSERT( pReturn || !IsCheckedOut() ) ; _ASSERT( pReturn == m_pData ) ; if( p ) { // // If he gave us an item, it must be checked out of whatever cache // it came from - this ensures that it will not be destroyed while we // access it, because we are not at the point of adding our own reference // yet ! // _ASSERT( p->IsCheckedOut() ) ; p->ExclusiveUnlock() ; } if( pReturn ) { IncrementStat( pStats, CACHESTATS::ITEMS ) ; } else { // // Release the data item back to the user // NOTE - Don't have the cachelock so can't give them // the cachedata at this point ! // constructor.Release( pData, 0 ) ; // // Insure that we get onto the expiration list - this CACHEENTRY // should be removed at some point ! // FailedCheckOut( plrulist, FALSE, 0, 0 ) ; } // // Release the locks - this can be done before we go down the // error path because we know that we won't get destroyed ! // m_lock.ExclusiveUnlock() ; } } return pReturn ; } } ; #endif // _CINTRNL_H_