photosgallery/viewframework/medialists/src/glxgarbagecollector.cpp
changeset 0 4e91876724a2
child 13 71da52165949
--- /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 <glxassert.h>
+#include <glxtracer.h>
+#include <glxlog.h>
+
+#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<CGlxCache>& 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<CGlxCache>& 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<TAny*>( 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<TMPXAttribute>& 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<TMPXAttribute>& 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;	
+    }