diff -r 000000000000 -r 4e91876724a2 photosgallery/viewframework/medialists/src/glxgarbagecollector.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photosgallery/viewframework/medialists/src/glxgarbagecollector.cpp Thu Dec 17 08:45:44 2009 +0200 @@ -0,0 +1,412 @@ +/* +* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Garbage collector +* +*/ + + + + +#include "glxgarbagecollector.h" + +#include +#include +#include + +#include "glxcache.h" +#include "glxerrormanager.h" +#include "glxmedia.h" +#include "glxmedialist.h" +#include "mglxmediauser.h" +#include "glxthumbnailutility.h" + +/** + * Interval for the periodic timer, in microseconds + */ +const TInt KPeriodicInterval = 500000; +/** + * Start Delay for the periodic timer, in microseconds + */ +const TInt KPeriodicStartDelay = 300000; // It is Changed to 300Micro Second ; To be able to give Others a Chance to Run before it starts +/** + * Max number of items to scan per periodic callback + * @todo Find optimal value for this + */ +const TInt KMaxScannedMediaCountPerPeriodicCallback = 15; // Changed from 30; Still there is Scope for Nominal Value Or Dynamic Value + +/** + * Max number of attributes that an item may have in use + */ +const TInt KMaxAttributesInUse = 50; + +// ----------------------------------------------------------------------------- +// NewL +// ----------------------------------------------------------------------------- +// +CGlxGarbageCollector* CGlxGarbageCollector::NewL( + const RPointerArray& aCaches ) + { + TRACER( "CGlxGarbageCollector::NewL" ); + + CGlxGarbageCollector* self = new( ELeave ) CGlxGarbageCollector( aCaches ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +// ----------------------------------------------------------------------------- +// Constructor +// ----------------------------------------------------------------------------- +// +CGlxGarbageCollector::CGlxGarbageCollector( + const RPointerArray& aCaches ) + : iCaches( aCaches ) + { + TRACER("CGlxGarbageCollector::Default Constructor"); + } + +// ----------------------------------------------------------------------------- +// ConstructL +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::ConstructL() + { + TRACER("CGlxGarbageCollector::ConstructL"); + + // Create callback loop in the lowest priority, so that the garbage + // collection does not slow down the application + iPeriodic = CPeriodic::NewL( CActive::EPriorityIdle ); + iAttributesInUse.ReserveL(KMaxAttributesInUse); + } + +// ----------------------------------------------------------------------------- +// Destructor +// ----------------------------------------------------------------------------- +// +CGlxGarbageCollector::~CGlxGarbageCollector() + { + TRACER("CGlxGarbageCollector::Destructor"); + delete iPeriodic; + iAttributesInUse.Close(); + } + +// ----------------------------------------------------------------------------- +// CleanupL +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::CleanupL() + { + TRACER("CGlxGarbageCollector::CleanupL"); + + if ( !iPeriodic->IsActive() ) + { + iPeriodic->Start( KPeriodicStartDelay, KPeriodicInterval, TCallBack( &PeriodicCallbackL, static_cast( this ) ) ); + } + else + { + // a full cleanup round is required to make the cache fully clean, + // if cleanup is currently ongoing. we don't know whether the cache + // was modified before or after the current (cache or media) index being + // cleaned up. + iRequiresFullCleanupRound = ETrue; + } + } + +// ----------------------------------------------------------------------------- +// CancelCleanup +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::CancelCleanup() + { + TRACER("CGlxGarbageCollector::CancelCleanup"); + + if (iPeriodic->IsActive()) + { + iPeriodic->Cancel(); + // Consideration of Restarting the timer is there; + } + } + +// ----------------------------------------------------------------------------- +// Callback from periodic timer +// ----------------------------------------------------------------------------- +// +TInt CGlxGarbageCollector::PeriodicCallbackL( TAny* aPtr ) + { + TRACER("CGlxGarbageCollector::PeriodicCallback"); + + GLX_ASSERT_DEBUG( aPtr != NULL, Panic( EGlxPanicLogicError ), + "Received null pointer for garbage collector" ); + + // get "this" pointer + static_cast< CGlxGarbageCollector* >( aPtr )->PeriodicCallbackL(); + + // return value ignored for periodic timers + return 0; + } + +// ----------------------------------------------------------------------------- +// Flush Count Pages +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::FlushPagesL(TInt aCount) + { + TBool reachedEnd = CleanupCaches(aCount); + + if ( reachedEnd ) + { + // reset to the start of the caches + iScanningPosition.iCurrentCacheIndex = 0; + iScanningPosition.iNextMediaIndexToCleanup = 0; + + // next time the full round reaches end, the caches will be fully clean + // (unless client calls Cleanup() ) + iRequiresFullCleanupRound = EFalse; + } + } +// ----------------------------------------------------------------------------- +// Callback from periodic timer +// inlined in cpp only => inlines for arm +// ----------------------------------------------------------------------------- +// +inline void CGlxGarbageCollector::PeriodicCallbackL() + { + TRACER("CGlxGarbageCollector::PeriodicCallbackL"); + + TBool reachedEnd = CleanupCaches(); + + // determine whether idle callback should be cancelled (note that check + // needs to be done before resetting iRequiresFullCleanupRound below) + // do not cancel if reached the end, but cache is dirty + if ( reachedEnd && !iRequiresFullCleanupRound ) + { + iPeriodic->Cancel(); + } + + // if scanned to the end of caches, set up for the next cleanup round + if ( reachedEnd ) + { + // reset to the start of the caches + iScanningPosition.iCurrentCacheIndex = 0; + iScanningPosition.iNextMediaIndexToCleanup = 0; + + // next time the full round reaches end, the caches will be fully clean + // (unless client calls Cleanup() ) + iRequiresFullCleanupRound = EFalse; + } + } + +// ----------------------------------------------------------------------------- +// Clean up all caches +// ----------------------------------------------------------------------------- +// +TBool CGlxGarbageCollector::CleanupCaches(TInt aCount) + { + TRACER( "CGlxGarbageCollector::CleanupCaches" ); + + TInt remainingScanCount = 0; + + // set the maximum number of items to scan during this call + // Count is Needed for Flushing Direct + remainingScanCount = aCount*KMaxScannedMediaCountPerPeriodicCallback; + + // Iterate through all cache until scanned enough items (remainingScanCount) + // (unlikely to have many caches, so ok to call RPointerArray::Count() on + // each iteration) + while ( iScanningPosition.iCurrentCacheIndex < iCaches.Count() ) + { + // clean up current cache + remainingScanCount = CleanupCache( + *iCaches[iScanningPosition.iCurrentCacheIndex], remainingScanCount ); + + // exit the loop if reached full scan count, since the above loop might + // have reached the end of the cache. (so don't increment the current + // cache index) + if ( 0 == remainingScanCount ) + { + break; + } + + // set indexes to the beginning of the next cache + iScanningPosition.iCurrentCacheIndex++; + iScanningPosition.iNextMediaIndexToCleanup = 0; + } + + // determine if there is anything more to clean up + TBool reachedEndOfAllCaches = + ( iScanningPosition.iCurrentCacheIndex == iCaches.Count() ); + return reachedEndOfAllCaches; + } + +// ----------------------------------------------------------------------------- +// Clean up cache that is currently under cleanup +// return number of items yet to scan during this idle callback +// ----------------------------------------------------------------------------- +// +TInt CGlxGarbageCollector::CleanupCache( CGlxCache& aCache, + TInt aRemainingScanCount ) + { + TRACER( "CGlxGarbageCollector::CleanupCache" ); + + // set the maximum number of items to scan during this call + TInt remainingScanCount = aRemainingScanCount; + + // don't store cache.Count() locally, as the count changes during the loop + while ( iScanningPosition.iNextMediaIndexToCleanup < aCache.Count() && + remainingScanCount > 0 && ( aCache.IdSpaceId() != KGlxIdSpaceIdRoot ) ) + { + CGlxMedia& media = aCache.Media( iScanningPosition.iNextMediaIndexToCleanup ); + + if ( media.UserCount() > 0 ) + { + // the media object still has users. Check if any attributes + // it has can be deleted. + TRAPD( err, CleanupMediaL( aCache, media ) ); + + // skip the media object if cleanup failed. This allows cleanup to + // continue also when has no free memory. Skip also if there + // are still attributes in use. + if ( ( media.Count() == 0 && !err ) || ( err > 0 ) ) + { + // the media object has no more attributes. It can be deleted. + // don't increment iNextMediaIndexToCleanup since the index + // of the next item will be decreased by one due to removal + aCache.Delete( iScanningPosition.iNextMediaIndexToCleanup ); + GLX_LOG_INFO1( "CGlxGarbageCollector - Deleted Media Id=%d since no attributes left", media.Id().Value()); + } + else if( err < 0 ) + { + // scan next item + iScanningPosition.iNextMediaIndexToCleanup++; + } + else + { + // scan next item + iScanningPosition.iNextMediaIndexToCleanup++; + } + } + else + { + // the media object has no more users in present cache. It may be deleted + if( !MediaInUse( media ) ) + { + // Bug Fix @ EDDG-7V3CJA:: Recheck other caches for UserCount + aCache.Delete( iScanningPosition.iNextMediaIndexToCleanup ); + GLX_LOG_INFO1( "CGlxGarbageCollector - Deleted Media Id=%d since no users left", media.Id().Value()); + } + } + remainingScanCount--; + } + + return remainingScanCount; + } + +// ----------------------------------------------------------------------------- +// Perform cleanup on provided media object +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::CleanupMediaL( CGlxCache& aCache, CGlxMedia& aMedia ) + { + TRACER("CGlxGarbageCollector::CleanupMediaL"); + + // check which attributes can be deleted. + iAttributesInUse.Reset(); + + GetAttributesInUseL( aMedia, iAttributesInUse ); + if ( iAttributesInUse.Count() == 0 ) + { + User::Leave(1); + } + GlxErrorManager::ClearExpiredAndUnusedErrorsL( aMedia, iAttributesInUse ); + + // add the error attribute to the list of attributes that are in use, so + // that it won't be deleted by the loop below. + // (if there are no errors left, GlxErrorManager::ClearExpiredAndNotInUseErrorsL + // would have deleted the error attribute from aMedia, so checking for it + // does no harm) + iAttributesInUse.AppendL( GlxErrorManager::ErrorAttribute() ); + + // delete all attributes that are not being used + DeleteOtherAttributes( aCache, aMedia, iAttributesInUse ); + } + +// ----------------------------------------------------------------------------- +// GetAttributesInUseL +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::GetAttributesInUseL( const CGlxMedia& aMedia, + RArray& aAttributes ) const + { + TRACER("CGlxGarbageCollector::GetAttributesInUseL"); + + // get needed attributes from each user of the media object + // ( unlikely to have many users, so calling CGlxMedia::UserCount and + // CGlxMedia::Id within the loop ) + for ( TInt userIndex = 0; userIndex < aMedia.UserCount(); userIndex++ ) + { + aMedia.User( userIndex ).GetRequiredAttributesL( aMedia.IndexInUser( userIndex ), + aAttributes ); + } + } + +// ----------------------------------------------------------------------------- +// Delete all attributes from a media object except those specified +// ----------------------------------------------------------------------------- +// +void CGlxGarbageCollector::DeleteOtherAttributes( CGlxCache& aCache, CGlxMedia& aMedia, + const RArray& aAttributesToKeep ) const + { + TRACER("CGlxGarbageCollector::DeleteOtherAttributes"); + + // loop backwards so can delete attributes during the loop + for ( TInt attrIndex = aMedia.Count() - 1; attrIndex >= 0; attrIndex-- ) + { + // delete the attribute if it is not in use + const TMPXAttribute& attrib = aMedia.Attribute(attrIndex); + if ( KErrNotFound == aAttributesToKeep.Find( attrib, TMPXAttribute::Match ) ) + { + GLX_LOG_INFO( "CGlxGarbageCollector::DeleteOtherAttributes() - Deleted attribute" ); + aMedia.DeleteAttribute( attrIndex ); + if (GlxThumbnailUtility::IsFullThumbnail(attrib)) + { + GLX_DEBUG2("CGlxGarbageCollector::DeleteOtherAttributes(*** TN ***) aMediaId(%d)", + aMedia.Id().Value()); + aCache.CleanupMedia(aMedia.Id()); + } + } + } + } + +// ----------------------------------------------------------------------------- +// Check for User Count for media to be deleted in the remaining caches +// ----------------------------------------------------------------------------- +// +TBool CGlxGarbageCollector::MediaInUse(const CGlxMedia& aMedia) const + { + // Bug Fix @ EDDG-7V3CJA :: If media has non zero user count in any of the caches + // then deletion of texture is to be avoided. + for(TInt cacheCount = 0; cacheCount < iCaches.Count(); cacheCount++ ) + { + if ( ( iCaches[cacheCount]->Media( aMedia.Id() ) ) + && ( iCaches[cacheCount]->IdSpaceId() != KGlxIdSpaceIdRoot ) ) + { + if ( ( iCaches[cacheCount]->Media(aMedia.Id() ) )->UserCount() > 0 ) + { + return ETrue; + } + } + } + return EFalse; + }