sl@0: // Copyright (c) 2008-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: // sl@0: sl@0: /** sl@0: @file sl@0: @internalComponent sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include "EComDebug.h" sl@0: #include "ImplementationInformation.h" // CompareTUidValues sl@0: #include "callback.h" sl@0: #include "resolvercache.h" sl@0: #include "EComPatchDataConstantv2.h" sl@0: sl@0: const TInt KCacheQueueGranularity = 4; sl@0: sl@0: // sl@0: // TCounterTicks class sl@0: // sl@0: inline TCounterTicks::TCounterTicks(TUint aTicks) sl@0: : iTicks(aTicks) sl@0: { sl@0: } sl@0: sl@0: /** This substraction calculate elapsed ticks: aEndTick - this */ sl@0: inline sl@0: TUint TCounterTicks::ElapsedSinceThis(TUint aEndTick) const sl@0: { sl@0: TUint diff; sl@0: if (aEndTick < iTicks) // wrap around occurred sl@0: { sl@0: diff = KMaxTUint - iTicks + aEndTick + 1; sl@0: } sl@0: else sl@0: { sl@0: diff = aEndTick - iTicks; sl@0: } sl@0: return diff; sl@0: } sl@0: sl@0: /** default constructor which initializes all member data to 0. */ sl@0: RResolverCacheEntry::RResolverCacheEntry() sl@0: : iLastUse( TCounterTicks(0) ), iLruRank(0), iResolverUid(KNullUid), sl@0: iNewLFuncPtr(NULL), iFlags(EEntryFlagsNone) sl@0: { sl@0: iLibrary.SetHandle(KNullHandle); sl@0: } sl@0: sl@0: /** construct RResolverCacheEntry with specific data sl@0: @param aResolverUid TUid of the resolver. sl@0: @param aLib RLibrary with handle open on the resolver DLL. sl@0: @param aNewL Function ptr to instantiate the resolver. sl@0: @param aFlags Special conditions about the cache entry. sl@0: */ sl@0: RResolverCacheEntry::RResolverCacheEntry(const TUid aResolverUid, sl@0: RLibrary aLib, sl@0: TProxyNewLPtr aNewL, sl@0: TUint32 aFlags) sl@0: : iLastUse( TCounterTicks(0) ), iLruRank(0), iResolverUid(aResolverUid), sl@0: iNewLFuncPtr(aNewL), iFlags(aFlags) sl@0: { sl@0: iLibrary.SetHandle(aLib.Handle()); sl@0: } sl@0: sl@0: /** unallocate resources owned by the RResolverCacheEntry object */ sl@0: void RResolverCacheEntry::Close() sl@0: { sl@0: iLibrary.Close(); sl@0: } sl@0: sl@0: /** This method compares two RResolverCacheEntry objects. sl@0: @param aEntry1 first RResolverCacheEntry object. sl@0: @param aEntry2 second RResolverCacheEntry object. sl@0: @return 0 means the two objects are equal. sl@0: positive value means first object is greater than the second. sl@0: negative vlaue means second object is greater than the first. sl@0: */ sl@0: TInt RResolverCacheEntry::CompareUid(const RResolverCacheEntry& aEntry1, sl@0: const RResolverCacheEntry& aEntry2) sl@0: { sl@0: return CompareTUidValues(aEntry1.iResolverUid.iUid, sl@0: aEntry2.iResolverUid.iUid); sl@0: } sl@0: sl@0: /** Compare the age of two cache entries. sl@0: @param aOther The RResolverCacheEntry to compare with. sl@0: @param aCurrTick The current system tick. It simplifies handling sl@0: of system tick wrap around to zero. sl@0: @return ETrue means "this" object is older than aOther. sl@0: EFalse means aOther is older than "this". sl@0: */ sl@0: TBool RResolverCacheEntry::ThisIsOlder(const RResolverCacheEntry& aOther, sl@0: TUint aCurrTick) const sl@0: { sl@0: if (iLastUse.iTicks == aOther.iLastUse.iTicks) sl@0: { sl@0: return iLruRank < aOther.iLruRank; sl@0: } sl@0: sl@0: // Because of counter wrap around, it is not safe to directly sl@0: // compare the two ticks. Drag in the current system tick. sl@0: return iLastUse.ElapsedSinceThis(aCurrTick) > sl@0: aOther.iLastUse.ElapsedSinceThis(aCurrTick); sl@0: } sl@0: sl@0: //================================= sl@0: // CCustomResolverCache class sl@0: //================================= sl@0: sl@0: /** CCustomResolverCache constructor sl@0: @param aCacheSize Maximum number of entries allowed to cache. sl@0: */ sl@0: CCustomResolverCache::CCustomResolverCache(TUint32 aCacheSize) sl@0: : CTimer(CActive::EPriorityStandard), sl@0: iResolvers(KCacheQueueGranularity), sl@0: iMaxCacheSize(aCacheSize) sl@0: { sl@0: } sl@0: sl@0: /** static factory method to instantiate CCustomResolverCache sl@0: @param aCacheSize Maximum number of entries allowed to cache. sl@0: @param aCacheTimeout Cache timeout in microseconds. sl@0: @leave Any of the system wide error codes. sl@0: */ sl@0: CCustomResolverCache* CCustomResolverCache::NewL(TUint32 aCacheSize, TUint32 aCacheTimeout) sl@0: { sl@0: CCustomResolverCache* self = new (ELeave) CCustomResolverCache(aCacheSize); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aCacheTimeout); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: /** Standard two phase construction to complete construction of sl@0: the CCustomResolverCache object. sl@0: @param aCacheTimeout Cache timeout in microseconds. sl@0: @leave Any of the system wide error codes. sl@0: */ sl@0: void CCustomResolverCache::ConstructL(TUint32 aCacheTimeout) sl@0: { sl@0: User::LeaveIfError(HAL::Get(HALData::ESystemTickPeriod, sl@0: iSystemTickPeriod)); sl@0: iEntryTimeToLive = (aCacheTimeout + iSystemTickPeriod - 1) / iSystemTickPeriod; sl@0: sl@0: CTimer::ConstructL(); sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: /** CCustomResolverCache destructor */ sl@0: CCustomResolverCache::~CCustomResolverCache() sl@0: { sl@0: Cancel(); sl@0: for (TInt i = iResolvers.Count() - 1; i >= 0; i--) sl@0: { sl@0: iResolvers[i].Close(); sl@0: } sl@0: iResolvers.Reset(); sl@0: } sl@0: sl@0: /** Implement the CActive RunL pure virtual */ sl@0: void CCustomResolverCache::RunL() sl@0: { sl@0: TUint currTickCount = User::TickCount(); sl@0: TUint lruAge = 0; sl@0: sl@0: for (TInt i = iResolvers.Count() - 1; i >= 0; i--) sl@0: { sl@0: RResolverCacheEntry& resolverEntry = iResolvers[i]; sl@0: TUint age = resolverEntry.iLastUse.ElapsedSinceThis(currTickCount); sl@0: if (age >= iEntryTimeToLive) sl@0: { sl@0: resolverEntry.Close(); sl@0: iResolvers.Remove(i); sl@0: } sl@0: else if (age > lruAge) sl@0: { sl@0: lruAge = age; sl@0: } sl@0: } sl@0: sl@0: if (iResolvers.Count() > 0) sl@0: { sl@0: After( iSystemTickPeriod * (iEntryTimeToLive - lruAge + 1) ); sl@0: } sl@0: sl@0: #ifdef __ECOMSERVER_TESTING__ sl@0: // In unit testing notify test bed that cache timer has fired. sl@0: iTimerExpireCB.CallBack(0, NULL); sl@0: #endif sl@0: } sl@0: sl@0: /** Search of a resolver UID in cache sl@0: @param aResolverUid the resolver to search for. sl@0: @return If find is successful, index of the resolver in RArray of cached resolvers. sl@0: Return KErrNotFound if resolver is not in cache. sl@0: */ sl@0: TInt CCustomResolverCache::FindResolver(const TUid aResolverUid) const sl@0: { sl@0: RResolverCacheEntry trgt; sl@0: trgt.iResolverUid = aResolverUid; sl@0: TLinearOrder comparator(RResolverCacheEntry::CompareUid); sl@0: return iResolvers.FindInOrder(trgt, comparator); sl@0: } sl@0: sl@0: /** Add a resolver library to cache sl@0: @param aResolverUid Implementation UID of the resolver. sl@0: @param aLib The RLibrary object which has the resolver loaded. The handle sl@0: of the RLibrary is owned by the cache if call is successful. sl@0: @param aNewL value for the iNewLFuncPtr member data of RResolverCacheEntry. sl@0: @param aFlags value for the iFlags member data of RResolverCacheEntry. sl@0: @return KErrNone if the data is added to cache. sl@0: KErrNoMemory if fail to insert the data in RArray. sl@0: */ sl@0: TInt CCustomResolverCache::CacheResolver(const TUid aResolverUid, sl@0: RLibrary aLib, sl@0: TProxyNewLPtr aNewL, sl@0: TUint32 aFlags) sl@0: { sl@0: if (iResolvers.Count() == iMaxCacheSize) sl@0: { sl@0: EvictLeastRecentlyUsed(); sl@0: } sl@0: sl@0: RResolverCacheEntry entry(aResolverUid, aLib, aNewL, aFlags); sl@0: SetLastUseTime(entry); sl@0: sl@0: #ifdef ECOM_TRACE sl@0: __ECOM_TRACE2("ECOM: adding custom resolver 0x%X to cache. New queue size will be %d.\n", aResolverUid.iUid, 1 + iResolvers.Count()); sl@0: #endif sl@0: sl@0: TLinearOrder comparator(RResolverCacheEntry::CompareUid); sl@0: TInt err = iResolvers.InsertInOrder(entry, comparator); sl@0: sl@0: // if cache was empty before need to start timer sl@0: if (err == KErrNone && ! IsActive()) sl@0: { sl@0: After( iSystemTickPeriod * (iEntryTimeToLive + 1) ); sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: /** Check if both queue size and cache timeout are non zero */ sl@0: TBool CCustomResolverCache::CachingEnabled() const sl@0: { sl@0: return (iMaxCacheSize && iEntryTimeToLive); sl@0: } sl@0: sl@0: /** Remove a cached entry sl@0: @param aIndex position of entry in the array. sl@0: */ sl@0: void CCustomResolverCache::Remove(TInt aIndex) sl@0: { sl@0: iResolvers[aIndex].Close(); sl@0: iResolvers.Remove(aIndex); sl@0: } sl@0: sl@0: /** Search for a resolverUID. If found, return the NewL pointer and sl@0: update time to live of the entry. sl@0: @param aResolverUid the resolver to lookup sl@0: @param aNewLFuncPtr output parameter. If lookup successful it has sl@0: the function pointer to instantiate the resolver. sl@0: @return True if resolver is in cache. False otherwise. sl@0: @post If cache hit, the timestamp of the entry is updated. sl@0: */ sl@0: TBool CCustomResolverCache::CacheLookup(const TUid aResolverUid, sl@0: TProxyNewLPtr& aNewLFuncPtr) sl@0: { sl@0: TInt i = FindResolver(aResolverUid); sl@0: if (i >= 0) sl@0: { sl@0: aNewLFuncPtr = iResolvers[i].iNewLFuncPtr; sl@0: SetLastUseTime(iResolvers[i]); sl@0: } sl@0: return (i >= 0); sl@0: } sl@0: sl@0: /** Remove a resolver from cache. sl@0: @param aResolverUid Identify the resolver to remove. sl@0: @return ETrue if aResolverUid is found in cache. EFalse means the sl@0: resolver is not in cache. sl@0: */ sl@0: TBool CCustomResolverCache::Remove(const TUid aResolverUid) sl@0: { sl@0: TInt i = FindResolver(aResolverUid); sl@0: if (i >= 0) sl@0: { sl@0: Remove(i); sl@0: } sl@0: return (i >= 0); sl@0: } sl@0: sl@0: /** Remove cached entries with flags set. sl@0: @param aMask If an entry has any of the bits in aMask set, it is removed. sl@0: */ sl@0: void CCustomResolverCache::RemoveItemsWithFlags(TUint32 aMask) sl@0: { sl@0: for (TInt i = iResolvers.Count() - 1; i >= 0; i--) sl@0: { sl@0: if (iResolvers[i].iFlags & aMask) sl@0: { sl@0: Remove(i); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** evict the least recently used entry in cache sl@0: */ sl@0: void CCustomResolverCache::EvictLeastRecentlyUsed() sl@0: { sl@0: TUint curr = User::TickCount(); sl@0: TInt index = 0; // set to first entry in cache. sl@0: sl@0: for (TInt i = 1; i < iResolvers.Count(); i++) sl@0: { sl@0: RResolverCacheEntry& resolverEntry = iResolvers[i]; sl@0: if (resolverEntry.ThisIsOlder(iResolvers[index], curr)) sl@0: { sl@0: index = i; sl@0: } sl@0: } sl@0: sl@0: Remove(index); sl@0: } sl@0: sl@0: /** Set the iLastUse field and iLruRank field of the entry. sl@0: The iLruRank field serves as tie breaker when two entries are sl@0: added within the same tick period. sl@0: @param aEntry The entry to update. sl@0: */ sl@0: void CCustomResolverCache::SetLastUseTime(RResolverCacheEntry& aEntry) sl@0: { sl@0: TUint curr = User::TickCount(); sl@0: aEntry.iLastUse.iTicks = curr; sl@0: aEntry.iLruRank = 0; sl@0: sl@0: if (curr == iMostRecentTimestamp) sl@0: { sl@0: // There are entries with same timestamp. So have to step sl@0: // through each cache entry to resolve senority. sl@0: for (TInt i = 0; i < iResolvers.Count(); i++) sl@0: { sl@0: RResolverCacheEntry& another = iResolvers[i]; sl@0: if (another.iLastUse.iTicks == curr && sl@0: another.iResolverUid != aEntry.iResolverUid && sl@0: another.iLruRank >= aEntry.iLruRank) sl@0: { sl@0: aEntry.iLruRank = another.iLruRank + 1; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: iMostRecentTimestamp = curr; sl@0: } sl@0: } sl@0: