sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // DBMS object cache sl@0: // sl@0: // sl@0: sl@0: #include "UD_STD.H" sl@0: #include "D32CACHE.H" sl@0: //#include sl@0: sl@0: NONSHARABLE_CLASS(RDbCache::CCache) : private CBase sl@0: { sl@0: public: sl@0: static CCache* OpenL( TInt aSize, TBool aUseTimer ); sl@0: void Close(); sl@0: // sl@0: void Flush(); sl@0: void Hold( CBase* aObject, TUint aMicroSeconds ); sl@0: void Release ( const CBase& aObject ); sl@0: private: sl@0: struct TEntry sl@0: { sl@0: TEntry* iNext; sl@0: TInt iDelta; sl@0: CBase* iObject; sl@0: }; sl@0: enum { ETimerPriority = -10 }; sl@0: enum { ETimerPeriod = 0x100000 }; // ~1.0s sl@0: private: sl@0: static inline TInt TlsHandle(); sl@0: CCache( TInt aSize ); sl@0: ~CCache(); sl@0: // sl@0: static CCache* NewL( TInt aSize, TBool aUseTimer ); sl@0: inline void Open(); sl@0: void Expire( TInt aElapsedTime ); sl@0: void Remove( TEntry*& aRef ); sl@0: void ExpireFirst(); sl@0: static void DoFlush( TAny* aPtr ); sl@0: private: sl@0: TInt iRef; sl@0: // CPeriodic* iTimer; sl@0: TTimeIntervalMicroSeconds32 iTickPeriod; sl@0: TUint iZeroTime; sl@0: TEntry* iCache; sl@0: TEntry* iFree; sl@0: TEntry iEntries[1]; // or maybe more sl@0: }; sl@0: sl@0: // Class CDbObjectCache sl@0: sl@0: //inline TInt RDbCache::CCache::TlsHandle() sl@0: //// use the address of a static function for the handle sl@0: // { return TInt( NewL ); } sl@0: sl@0: TAny* gCachePtr; sl@0: sl@0: RDbCache::CCache::CCache( TInt aSize ) sl@0: // sl@0: // Initialise the free entry list sl@0: // sl@0: { sl@0: TEntry* entry = iEntries; sl@0: while ( --aSize != 0 ) sl@0: { sl@0: entry[1].iNext = entry; sl@0: ++entry; sl@0: } sl@0: iFree = entry; sl@0: } sl@0: sl@0: RDbCache::CCache::~CCache() sl@0: { sl@0: __ASSERT( iRef < 0 ); sl@0: // empty the cache (destory the items now) sl@0: Expire( KMaxTInt ); sl@0: __ASSERT( iCache == 0 ); sl@0: // delete iTimer; sl@0: gCachePtr = NULL; sl@0: } sl@0: sl@0: const TInt KTickPeriod = 10000000; sl@0: const TTimeIntervalMicroSeconds32 gTickPeriod(KTickPeriod); sl@0: RDbCache::CCache* RDbCache::CCache::NewL( TInt aSize, TBool aUseTimer ) sl@0: // sl@0: // Construct a cache with aSize slots and one referee sl@0: // sl@0: { sl@0: //#warning !! RDbCache::CCache::NewL not implemented (uses UserHal/UserSvr) !! sl@0: CCache* cache = new( ELeave, sizeof( TEntry ) * ( aSize - 1 ) ) CCache( aSize ); // get the extra size for the cache entries, leaves on error sl@0: CleanupClosePushL( *cache ); sl@0: cache->iTickPeriod = gTickPeriod; sl@0: //User::LeaveIfError( UserHal::TickPeriod( cache->iTickPeriod ) ); sl@0: //User::LeaveIfError( UserSvr::DllSetTls( TlsHandle(), cache ) ); sl@0: gCachePtr = cache; sl@0: // if (aUseTimer) sl@0: // cache->iTimer = CPeriodic::NewL( ETimerPriority ); sl@0: CleanupStack::Pop(); sl@0: return cache; sl@0: } sl@0: sl@0: inline void RDbCache::CCache::Open() sl@0: // add a referee sl@0: { ++iRef; } sl@0: sl@0: void RDbCache::CCache::Close() sl@0: // sl@0: // remove a referee and delete as required sl@0: // sl@0: { sl@0: __ASSERT( iRef >= 0 ); sl@0: if ( --iRef < 0 ) sl@0: delete this; sl@0: } sl@0: sl@0: RDbCache::CCache* RDbCache::CCache::OpenL( TInt aSize, TBool aUseTimer ) sl@0: // sl@0: // Grab a reference to the cache, constructing it if required sl@0: // sl@0: { sl@0: // CCache* cache = ( CCache* )UserSvr::DllTls( TlsHandle() ); sl@0: CCache* cache = ( CCache* )gCachePtr; sl@0: if (!cache) sl@0: return NewL( aSize, aUseTimer ); sl@0: cache->Open(); sl@0: return cache; sl@0: } sl@0: sl@0: void RDbCache::CCache::Hold( CBase* aObject, TUint aMicroSeconds ) sl@0: // sl@0: // Hold aObject in the cache or destroy it sl@0: // sl@0: { sl@0: Flush(); // Destroy expired entries and re-assess Zero-time sl@0: TInt ticks = aMicroSeconds / TUint( iTickPeriod.Int() ); sl@0: TEntry* entry = iFree; sl@0: if ( entry == 0 ) sl@0: { // no free slots: check the first cache entry sl@0: __ASSERT( iCache ); sl@0: if ( iCache->iDelta > ticks ) sl@0: { // aObject expires first sl@0: delete aObject; sl@0: return; sl@0: } sl@0: ExpireFirst(); // remove the first entry and use it sl@0: entry = iFree; sl@0: } sl@0: iFree = entry->iNext; // move the free list pointer to the next entry sl@0: // sl@0: // find the insertion point in the cache delta-list sl@0: TEntry** pcache = &iCache; sl@0: TEntry* cache; sl@0: for ( ; ; ) sl@0: { sl@0: __ASSERT( ticks >= 0 ); sl@0: cache = *pcache; sl@0: if ( !cache ) sl@0: break; // add to end sl@0: TInt t = ticks - cache->iDelta; sl@0: if ( t < 0 ) sl@0: { // add to the list here sl@0: cache->iDelta = -t; // reduce the following delta sl@0: break; sl@0: } sl@0: ticks = t; // reduce the entry delta sl@0: pcache = &cache->iNext; sl@0: } sl@0: *pcache = entry; // set up the entry sl@0: entry->iDelta = ticks; sl@0: entry->iNext = cache; sl@0: entry->iObject = aObject; sl@0: // // kick the timer if we need to sl@0: // if ( iTimer && !iTimer->IsActive() ) sl@0: // iTimer->Start( ETimerPeriod, ETimerPeriod, TCallBack( ( TInt (*)(TAny*) )DoFlush, this ) ); sl@0: } sl@0: sl@0: void RDbCache::CCache::Remove( RDbCache::CCache::TEntry*& aRef ) sl@0: // sl@0: // Remove the entry at aRef from the cache sl@0: // sl@0: { sl@0: TEntry& entry = *aRef; sl@0: TEntry* next = entry.iNext; sl@0: entry.iNext = iFree; sl@0: iFree = &entry; sl@0: aRef = next; sl@0: if ( next ) sl@0: next->iDelta += entry.iDelta; sl@0: // else if ( iTimer ) // the cache is now empty, so stop the timer if we have one sl@0: // iTimer->Cancel(); sl@0: } sl@0: sl@0: void RDbCache::CCache::ExpireFirst() sl@0: // sl@0: // Expire the first entry in the cache sl@0: // sl@0: { sl@0: __ASSERT( iCache != 0 ); sl@0: // the ordering here is important. Removing the entry first allows the sl@0: // object d'tor to call Release() without causing re-entrancy problems. sl@0: CBase* object = iCache->iObject; sl@0: Remove( iCache ); sl@0: delete object; sl@0: } sl@0: sl@0: void RDbCache::CCache::Release( const CBase& aObject ) sl@0: // sl@0: // Remove the cache entry for aObject, if it is in the cache sl@0: // sl@0: { sl@0: TEntry** pcache = &iCache; sl@0: for ( ; ; ) sl@0: { sl@0: TEntry* entry = *pcache; sl@0: if ( !entry ) sl@0: return; sl@0: if ( entry->iObject == &aObject ) sl@0: { sl@0: Remove( *pcache ); sl@0: return; sl@0: } sl@0: pcache = &entry->iNext; sl@0: } sl@0: } sl@0: sl@0: void RDbCache::CCache::Expire( TInt aElapsedTime ) sl@0: // sl@0: // Destroy entries which expire with aElapsedTime sl@0: // sl@0: { sl@0: __ASSERT( aElapsedTime > 0 ); sl@0: if ( iCache && ( iCache->iDelta -= aElapsedTime ) < 0 ) sl@0: { sl@0: Open(); // This allows the cache to be owned by an object in the cache sl@0: do ExpireFirst(); sl@0: while ( iCache && iCache->iDelta < 0 ); sl@0: Close(); // The cache may be destroyed now sl@0: } sl@0: } sl@0: sl@0: void RDbCache::CCache::Flush() sl@0: // sl@0: // Check the execution clock and destroy any expired entries sl@0: // sl@0: // Care has to be taken to handle the 32-bit wraparound of the tick-count sl@0: // e.g. iZeroTime = 0xffffffffu, now = 0 sl@0: // sl@0: { sl@0: TUint now = User::TickCount(); sl@0: TUint elapsed = now - iZeroTime; sl@0: iZeroTime = now; sl@0: if ( elapsed ) sl@0: Expire( elapsed <= TUint( KMaxTInt ) ? elapsed : TUint( KMaxTInt ) ); sl@0: } sl@0: sl@0: void RDbCache::CCache::DoFlush( TAny* aPtr ) sl@0: // sl@0: // Callback for the timer sl@0: // sl@0: { sl@0: static_cast( aPtr )->Flush(); sl@0: } sl@0: sl@0: sl@0: // Class RDbCache sl@0: sl@0: TInt RDbCache::Open( TInt aSize, TBool aUseTimer ) sl@0: // sl@0: // Get a handle on the cache sl@0: // sl@0: { sl@0: __ASSERT( aSize > 0 ); sl@0: TRAPD( r, iCache = CCache::OpenL( aSize, aUseTimer ) ); sl@0: return r; sl@0: } sl@0: sl@0: void RDbCache::Close() sl@0: // sl@0: // Close this handle on the cache sl@0: // sl@0: { sl@0: CCache* cache = iCache; sl@0: if ( cache ) sl@0: { sl@0: iCache = 0; sl@0: cache->Close(); sl@0: } sl@0: } sl@0: sl@0: void RDbCache::Hold( CBase* aObject, TUint aMicroSeconds ) sl@0: // sl@0: // Hold aObject on the cache, if open sl@0: // We are now responsible for deleting the object sl@0: // sl@0: { sl@0: if ( iCache ) sl@0: iCache->Hold( aObject, aMicroSeconds ); sl@0: else sl@0: delete aObject; // no cache available sl@0: } sl@0: sl@0: void RDbCache::Release( const CBase& aObject ) const sl@0: // sl@0: // Retrieve aObject from the cache sl@0: // sl@0: { sl@0: if ( iCache ) sl@0: iCache->Release( aObject ); sl@0: } sl@0: sl@0: void RDbCache::Flush() sl@0: // sl@0: // Destroy any cached objects which have expired sl@0: // sl@0: { sl@0: if ( iCache ) sl@0: iCache->Flush(); sl@0: } sl@0: sl@0: sl@0: