persistentstorage/dbms/udbms/UD_CACHE.CPP
changeset 0 08ec8eefde2f
child 55 44f437012c90
equal deleted inserted replaced
-1:000000000000 0:08ec8eefde2f
       
     1 // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // DBMS object cache
       
    15 // 
       
    16 //
       
    17 
       
    18 #include "UD_STD.H"
       
    19 #include "D32CACHE.H"
       
    20 #include <e32svr.h>
       
    21 
       
    22 NONSHARABLE_CLASS(RDbCache::CCache) : private CBase
       
    23 	{
       
    24 public:
       
    25 	static CCache* OpenL( TInt aSize, TBool aUseTimer );
       
    26 	void Close();
       
    27 //
       
    28 	void Flush();
       
    29 	void Hold( CBase* aObject, TUint aMicroSeconds );
       
    30 	void Release ( const CBase& aObject );
       
    31 private:
       
    32 	struct TEntry
       
    33 		{
       
    34 		TEntry* iNext;
       
    35 		TInt iDelta;
       
    36 		CBase* iObject;
       
    37 		};
       
    38 	enum { ETimerPriority = -10 };
       
    39 	enum { ETimerPeriod = 0x100000 };	// ~1.0s
       
    40 private:
       
    41 	static inline TInt TlsHandle();
       
    42 	CCache( TInt aSize );
       
    43 	~CCache();
       
    44 //
       
    45 	static CCache* NewL( TInt aSize, TBool aUseTimer );
       
    46 	inline void Open();
       
    47 	void Expire( TInt aElapsedTime );
       
    48 	void Remove( TEntry*& aRef );
       
    49 	void ExpireFirst();
       
    50 	static void DoFlush( TAny* aPtr );
       
    51 private:
       
    52 	TInt iRef;
       
    53 	CPeriodic* iTimer;
       
    54 	TTimeIntervalMicroSeconds32 iTickPeriod;
       
    55 	TUint iZeroTime;
       
    56 	TEntry* iCache;
       
    57 	TEntry* iFree;
       
    58 	TEntry iEntries[1];		// or maybe more
       
    59 	};
       
    60 
       
    61 // Class CDbObjectCache
       
    62 
       
    63 inline TInt RDbCache::CCache::TlsHandle()
       
    64 // use the address of a static function for the handle
       
    65 	{ return TInt( NewL ); }
       
    66 
       
    67 
       
    68 RDbCache::CCache::CCache( TInt aSize )
       
    69 //
       
    70 // Initialise the free entry list
       
    71 //
       
    72 	{
       
    73 	TEntry* entry = iEntries;
       
    74 	while ( --aSize != 0 )
       
    75 		{
       
    76 		entry[1].iNext = entry;
       
    77 		++entry;
       
    78 		}
       
    79 	iFree = entry;
       
    80 	}
       
    81 
       
    82 RDbCache::CCache::~CCache()
       
    83 	{
       
    84 	__ASSERT( iRef < 0 );
       
    85 // empty the cache (destory the items now)
       
    86 	Expire( KMaxTInt );
       
    87 	__ASSERT( iCache == 0 );
       
    88 	delete iTimer;
       
    89 	UserSvr::DllFreeTls( TlsHandle() );
       
    90 	}
       
    91 
       
    92 RDbCache::CCache* RDbCache::CCache::NewL( TInt aSize, TBool aUseTimer )
       
    93 //
       
    94 // Construct a cache with aSize slots and one referee
       
    95 //
       
    96 	{
       
    97 	CCache* cache = new( ELeave, sizeof( TEntry ) * ( aSize - 1 ) ) CCache( aSize );	// get the extra size for the cache entries, leaves on error
       
    98 	CleanupClosePushL( *cache );
       
    99 	User::LeaveIfError( UserHal::TickPeriod( cache->iTickPeriod ) );
       
   100 	User::LeaveIfError( UserSvr::DllSetTls( TlsHandle(), cache ) );
       
   101 	if (aUseTimer)
       
   102 		cache->iTimer = CPeriodic::NewL( ETimerPriority );
       
   103 	CleanupStack::Pop();
       
   104 	return cache;
       
   105 	}
       
   106 
       
   107 inline void RDbCache::CCache::Open()
       
   108 // add a referee
       
   109 	{ ++iRef; }
       
   110 
       
   111 void RDbCache::CCache::Close()
       
   112 //
       
   113 // remove a referee and delete as required
       
   114 //
       
   115 	{
       
   116 	__ASSERT( iRef >= 0 );
       
   117 	if ( --iRef < 0 )
       
   118 		delete this;
       
   119 	}
       
   120 
       
   121 RDbCache::CCache* RDbCache::CCache::OpenL( TInt aSize, TBool aUseTimer )
       
   122 //
       
   123 // Grab a reference to the cache, constructing it if required
       
   124 //
       
   125 	{
       
   126 	CCache* cache = ( CCache* )UserSvr::DllTls( TlsHandle() );
       
   127 	if (!cache)
       
   128 		return NewL( aSize, aUseTimer );
       
   129 	cache->Open();
       
   130 	return cache;
       
   131 	}
       
   132 
       
   133 void RDbCache::CCache::Hold( CBase* aObject, TUint aMicroSeconds )
       
   134 //
       
   135 // Hold aObject in the cache or destroy it
       
   136 //
       
   137 	{
       
   138 	Flush();		// Destroy expired entries and re-assess Zero-time
       
   139 	TInt ticks = aMicroSeconds / TUint( iTickPeriod.Int() );
       
   140 	TEntry* entry = iFree;
       
   141 	if ( entry == 0 )
       
   142 		{	// no free slots: check the first cache entry
       
   143 		__ASSERT( iCache );
       
   144 		if ( iCache->iDelta > ticks )
       
   145 			{				// aObject expires first
       
   146 			delete aObject;
       
   147 			return;
       
   148 			}
       
   149 		ExpireFirst();		// remove the first entry and use it
       
   150 		entry = iFree;
       
   151 		}
       
   152 	iFree = entry->iNext;	// move the free list pointer to the next entry
       
   153 	//
       
   154 	// find the insertion point in the cache delta-list
       
   155 	TEntry** pcache = &iCache;
       
   156 	TEntry* cache;
       
   157 	for ( ; ; )
       
   158 		{
       
   159 		__ASSERT( ticks >= 0 );
       
   160 		cache = *pcache;
       
   161 		if ( !cache )
       
   162 			break;				// add to end
       
   163 		TInt t = ticks - cache->iDelta;
       
   164 		if ( t < 0 )
       
   165 			{					// add to the list here
       
   166 			cache->iDelta = -t;	// reduce the following delta
       
   167 			break;
       
   168 			}
       
   169 		ticks = t;				// reduce the entry delta
       
   170 		pcache = &cache->iNext;
       
   171 		}
       
   172 	*pcache = entry;				// set up the entry
       
   173 	entry->iDelta = ticks;
       
   174 	entry->iNext = cache;
       
   175 	entry->iObject = aObject;
       
   176 	// kick the timer if we need to
       
   177 	if ( iTimer && !iTimer->IsActive() )
       
   178 		iTimer->Start( ETimerPeriod, ETimerPeriod, TCallBack( ( TInt (*)(TAny*) )DoFlush, this ) );
       
   179 	}
       
   180 
       
   181 void RDbCache::CCache::Remove( RDbCache::CCache::TEntry*& aRef )
       
   182 //
       
   183 // Remove the entry at aRef from the cache
       
   184 //
       
   185 	{
       
   186 	TEntry& entry = *aRef;
       
   187 	TEntry* next = entry.iNext;
       
   188 	entry.iNext = iFree;
       
   189 	iFree = &entry;
       
   190 	aRef = next;
       
   191 	if ( next )
       
   192 		next->iDelta += entry.iDelta;
       
   193 	else if ( iTimer )	// the cache is now empty, so stop the timer if we have one
       
   194 		iTimer->Cancel();
       
   195 	}
       
   196 
       
   197 void RDbCache::CCache::ExpireFirst()
       
   198 //
       
   199 // Expire the first entry in the cache
       
   200 //
       
   201 	{
       
   202 	__ASSERT( iCache != 0 );
       
   203 	// the ordering here is important. Removing the entry first allows the
       
   204 	// object d'tor to call Release() without causing re-entrancy problems.
       
   205 	CBase* object = iCache->iObject;
       
   206 	Remove( iCache );
       
   207 	delete object;
       
   208 	}
       
   209 
       
   210 void RDbCache::CCache::Release( const CBase& aObject )
       
   211 //
       
   212 // Remove the cache entry for aObject, if it is in the cache
       
   213 //
       
   214 	{
       
   215 	TEntry** pcache = &iCache;
       
   216 	for ( ; ; )
       
   217 		{
       
   218 		TEntry* entry = *pcache;
       
   219 		if ( !entry )
       
   220 			return;
       
   221 		if ( entry->iObject == &aObject )
       
   222 			{
       
   223 			Remove( *pcache );
       
   224 			return;
       
   225 			}
       
   226 		pcache = &entry->iNext;
       
   227 		}
       
   228 	}
       
   229 
       
   230 void RDbCache::CCache::Expire( TInt aElapsedTime )
       
   231 //
       
   232 // Destroy entries which expire with aElapsedTime
       
   233 //
       
   234 	{
       
   235 	__ASSERT( aElapsedTime > 0 );
       
   236 	if ( iCache && ( iCache->iDelta -= aElapsedTime ) < 0 )
       
   237 		{
       
   238 		Open();		// This allows the cache to be owned by an object in the cache
       
   239 		do ExpireFirst();
       
   240 			while ( iCache && iCache->iDelta < 0 );
       
   241 		Close();	// The cache may be destroyed now
       
   242 		}
       
   243 	}
       
   244 
       
   245 void RDbCache::CCache::Flush()
       
   246 //
       
   247 // Check the execution clock and destroy any expired entries
       
   248 //
       
   249 // Care has to be taken to handle the 32-bit wraparound of the tick-count
       
   250 // e.g. iZeroTime = 0xffffffffu, now = 0
       
   251 //
       
   252 	{
       
   253 	TUint now = User::TickCount();
       
   254 	TUint elapsed = now - iZeroTime;
       
   255 	iZeroTime = now;
       
   256 	if ( elapsed )
       
   257 		Expire( elapsed <= TUint( KMaxTInt ) ? elapsed : TUint( KMaxTInt ) );
       
   258 	}
       
   259 
       
   260 void RDbCache::CCache::DoFlush( TAny* aPtr )
       
   261 //
       
   262 // Callback for the timer
       
   263 //
       
   264 	{
       
   265 	static_cast<CCache*>( aPtr )->Flush();
       
   266 	}
       
   267 
       
   268 
       
   269 // Class RDbCache
       
   270 
       
   271 TInt RDbCache::Open( TInt aSize, TBool aUseTimer )
       
   272 //
       
   273 // Get a handle on the cache
       
   274 //
       
   275 	{
       
   276 	__ASSERT( aSize > 0 );
       
   277 	TRAPD( r, iCache = CCache::OpenL( aSize, aUseTimer ) );
       
   278 	return r;
       
   279 	}
       
   280 
       
   281 void RDbCache::Close()
       
   282 //
       
   283 // Close this handle on the cache
       
   284 //
       
   285 	{
       
   286 	CCache* cache = iCache;
       
   287 	if ( cache )
       
   288 		{
       
   289 		iCache = 0;
       
   290 		cache->Close();
       
   291 		}
       
   292 	}
       
   293 
       
   294 void RDbCache::Hold( CBase* aObject, TUint aMicroSeconds )
       
   295 //
       
   296 // Hold aObject on the cache, if open
       
   297 // We are now responsible for deleting the object
       
   298 //
       
   299 	{
       
   300 	if ( iCache )
       
   301 		iCache->Hold( aObject, aMicroSeconds );
       
   302 	else
       
   303 		delete aObject;	// no cache available
       
   304 	}
       
   305 
       
   306 void RDbCache::Release( const CBase& aObject ) const
       
   307 //
       
   308 // Retrieve aObject from the cache
       
   309 //
       
   310 	{
       
   311 	if ( iCache )
       
   312 		iCache->Release( aObject );
       
   313 	}
       
   314 
       
   315 void RDbCache::Flush()
       
   316 //
       
   317 // Destroy any cached objects which have expired
       
   318 //
       
   319 	{
       
   320 	if ( iCache )
       
   321 		iCache->Flush();
       
   322 	}
       
   323 
       
   324 
       
   325