userlibandfileserver/fileserver/sfile/sf_cache_man.cpp
changeset 0 a41df078684a
child 176 af6ec97d9189
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_cache_man.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1198 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// f32\sfile\sf_cache_man.cpp
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include "sf_std.h"
+#include <e32uid.h>
+#include <e32wins.h>
+#include <f32file.h>
+#include <hal.h>
+#include "sf_cache_man.h"
+#include "sf_cache_client.h"
+#include "sf_file_cache.h"
+#include "sf_file_cache_defs.h"
+
+//#undef __SIMULATE_LOCK_FAILURES__
+//#define __RAMDOM_LOCK_FAILURES__
+//#define __PSEUDO_RANDOM_FAILURES__		
+//#define __LOCK_ENTIRE_CACHELINE__
+
+#ifdef __RAMDOM_LOCK_FAILURES__	
+#include <e32math.h>
+#endif
+
+#define CACHE_NUM_TO_ADDR(n) (iBase + (n << iCacheLineSizeLog2))
+#define ADDR_TO_CACHELINE_ADDR(a) ((((a - iBase) >> iCacheLineSizeLog2) << iCacheLineSizeLog2) + iBase)
+#define ADDR_TO_INDEX(a) ((a - iBase) >> iCacheLineSizeLog2)
+
+#if defined(_DEBUG)
+	#define __CACHELINE_INVARIANT(aCacheLine)						\
+	__ASSERT_DEBUG(												\
+		aCacheLine.iDirtySegments <= aCacheLine.iFilledSegments &&	\
+		aCacheLine.iFilledSegments <= aCacheLine.iAllocatedSegments,	\
+		Fault(EInvalidCacheLine));
+
+#else
+	#define __CACHELINE_INVARIANT(aCacheLine)
+#endif
+
+const TInt KSegmentSize = 4096;
+const TInt KSegmentSizeLog2 = 12;
+const TInt64 KSegmentSizeMask = MAKE_TINT64(0x7FFFFFFF,0xFFFFF000);
+
+const TInt KCacheLineSizeLog2 = 17;	// 128K
+const TInt KCacheLineSize = (1 << KCacheLineSizeLog2 );
+const TInt KCacheLineSizeInSegments = (KCacheLineSize >> KSegmentSizeLog2);
+
+CCacheManager* CCacheManagerFactory::iCacheManager = NULL;
+
+
+LOCAL_C void Fault(TCacheManagerFault aFault)
+//
+// Report a fault in the cache manager
+//
+	{
+	User::Panic(_L("FSCACHEMAN"), aFault);
+	}
+
+
+/*
+Indicates if a number passed in is a power of two
+
+@param aNum number to be tested
+@return Flag to indicate the result of the test
+*/
+LOCAL_C TBool IsPowerOfTwo(TInt aNum)
+	{
+	return (aNum != 0 && (aNum & -aNum) == aNum);
+	}
+
+
+//********************
+// CCacheManagerFactory
+//********************
+void CCacheManagerFactory::CreateL()
+	{
+	__ASSERT_ALWAYS(iCacheManager == NULL, Fault(ECacheAlreadyCreated));
+
+	if (TGlobalFileCacheSettings::Enabled())
+		{
+		iCacheManager = CCacheManager::NewCacheL(TGlobalFileCacheSettings::CacheSize());
+		}
+	else
+		{
+		Destroy(); 
+		}
+	}
+
+CCacheManager* CCacheManagerFactory::CacheManager()
+	{
+	return iCacheManager;
+	}
+
+TInt CCacheManagerFactory::Destroy()
+	{
+	delete iCacheManager;
+	iCacheManager = NULL;
+	return KErrNone;
+	}
+
+//********************
+// CCacheManager
+//********************
+CCacheManager* CCacheManager::NewCacheL(TInt aCacheSize)
+	{
+	CCacheManager* cacheManager = new (ELeave) CCacheManager(aCacheSize);
+
+	CleanupStack::PushL(cacheManager);
+	cacheManager->ConstructL();
+	CleanupStack::Pop(1, cacheManager);
+
+	return cacheManager;
+	}
+
+CCacheManager::~CCacheManager()
+	{
+	CacheLock();
+
+	iFreeQueue.Close();
+	iUsedQueue.Close();
+
+	delete [] iCacheLines;
+
+	CacheUnlock();
+	iLock.Close();
+
+	iChunk.Close();
+	}
+
+
+CCacheManager::CCacheManager(TUint aCacheSize)
+	{
+	// Get the page size
+	TInt pageSize;
+	TInt r = HAL::Get(HAL::EMemoryPageSize, pageSize);
+	if (r != KErrNone)
+		pageSize = KSegmentSize;
+	__ASSERT_ALWAYS(IsPowerOfTwo(pageSize), Fault(EIllegalPageSize));
+
+	// For the time being we only a support page size of 4K and we assume
+	// that page size = segment size, since the extra overhead of supporting 
+	// variable page sizes now is non-trivial.
+	//
+	// If a processor with a different page size needs to be supported 
+	// in the future, then we need to either rework this code to be able to
+	// devide up pages into smaller segments or analyze the impact of having 
+	// a different page size on performance.
+	__ASSERT_ALWAYS(pageSize == KSegmentSize, Fault(EIllegalPageSize));
+
+	iCacheLineSize = KCacheLineSize;
+	iCacheLineSizeLog2 = KCacheLineSizeLog2;
+
+	iCacheSize = aCacheSize;
+
+	iSegmentsPerCacheLine = 1 << (iCacheLineSizeLog2 - KSegmentSizeLog2);
+
+	iMaxLockedSegments = TGlobalFileCacheSettings::MaxLockedSize() >> KSegmentSizeLog2;
+
+#ifdef __SIMULATE_LOCK_FAILURES__
+	iSimulateLockFailureMode = ETrue;
+#endif
+	}
+
+void CCacheManager::ConstructL()
+	{
+
+	// calculate the low-memory threshold below which we fail any attempt to allocate memory
+	TMemoryInfoV1Buf meminfo;
+	TInt r = UserHal::MemoryInfo(meminfo);
+	__ASSERT_ALWAYS(r==KErrNone,Fault(EMemoryInfoFailed));
+
+	iLowMemoryThreshold = (meminfo().iTotalRamInBytes * TGlobalFileCacheSettings::LowMemoryThreshold()) / 100;
+	__CACHE_PRINT4(_L("CACHEMAN: totalRAM %d freeRAM %d KDefaultLowMemoryThresholdPercent %d iLowMemoryThreshold %d"), 
+		meminfo().iTotalRamInBytes, meminfo().iFreeRamInBytes, KDefaultLowMemoryThreshold, iLowMemoryThreshold);
+	__CACHE_PRINT1(_L("CACHEMAN: iCacheSize %d"), iCacheSize);
+	TChunkCreateInfo createInfo;
+	createInfo.SetCache(iCacheSize);
+	createInfo.SetOwner(EOwnerProcess);
+	r = iChunk.Create(createInfo);
+	User::LeaveIfError(r);
+
+	
+	TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
+	if (mm < EMemModelTypeFlexible)
+		{
+		// On memory models before flexible, File System has to register chunks that will be DMA-ed.
+		// On flexible memory model, local media is using new physical pinning Kernel interface that
+		// doesn't require registration of the chunk.
+		UserSvr::RegisterTrustedChunk(iChunk.Handle());
+		}
+	
+
+	__ASSERT_ALWAYS(iCacheSize > KSegmentSize, Fault(EIllegalCacheSize));
+
+	r = iLock.CreateLocal();
+	User::LeaveIfError(r);
+
+	iNumOfCacheLines = iCacheSize >> iCacheLineSizeLog2;
+	iBase = Base();
+
+	iCacheLines = new (ELeave) TCacheLine[iNumOfCacheLines];
+
+	for (TInt n=0; n<iNumOfCacheLines; n++)
+		{
+		// coverity[var_decl]
+		TCacheLine cacheLine;
+		cacheLine.iAddr = CACHE_NUM_TO_ADDR(n);
+		cacheLine.iAllocatedSegments = 0;
+		cacheLine.iFilledSegments = 0;
+		cacheLine.iDirtySegments = 0;
+		cacheLine.iLockCount = 0;
+		cacheLine.iLockedSegmentStart = 0;
+		cacheLine.iLockedSegmentCount = 0;
+		cacheLine.iClient = NULL;
+		// coverity[uninit_use]
+		iCacheLines[n] = cacheLine;
+
+		r = iFreeQueue.Append(&iCacheLines[n]);
+		User::LeaveIfError(r);
+		}
+
+	}
+
+CCacheClient* CCacheManager::CreateClientL()
+	{
+	CCacheClient* client = CCacheClient::NewL(*this);
+	return client;
+	}
+
+void CCacheManager::RegisterClient(CCacheClient& /*aClient*/)
+	{
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	iStats.iFileCount++;
+#endif
+	}
+
+void CCacheManager::DeregisterClient(CCacheClient& /*aClient*/)
+	{
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	iStats.iFileCount--;
+#endif
+	}
+
+
+void CCacheManager::FreeMemoryChanged(TBool aMemoryLow)
+	{
+	iMemoryLow = aMemoryLow;
+	}
+
+TBool CCacheManager::TooManyLockedSegments()
+	{
+	return (iLockedSegmentCount >= iMaxLockedSegments)?(TBool)ETrue:(TBool)EFalse;
+	}
+
+TInt CCacheManager::SegmentSize()
+	{
+	return KSegmentSize;
+	}
+
+TInt CCacheManager::SegmentSizeLog2()
+	{
+	return KSegmentSizeLog2;
+	}
+
+TInt64 CCacheManager::SegmentSizeMask()
+	{
+	return KSegmentSizeMask;
+	}
+
+TInt CCacheManager::CacheLineSize()
+	{
+	return KCacheLineSize;
+	}
+
+TInt CCacheManager::CacheLineSizeInSegments()
+	{
+	return KCacheLineSizeInSegments;
+	}
+
+TInt CCacheManager::CacheLineSizeLog2()
+	{
+	return KCacheLineSizeLog2;
+	}
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+TFileCacheStats& CCacheManager::Stats()
+	{
+	iStats.iFreeCount = iFreeQueue.Count();
+	iStats.iUsedCount = iUsedQueue.Count();
+	iStats.iLockedSegmentCount = iLockedSegmentCount;
+	iStats.iFilesOnClosedQueue = TClosedFileUtils::Count();
+	__ASSERT_DEBUG(iStats.iFreeCount >= 0, Fault(EInvalidStats));
+	__ASSERT_DEBUG(iStats.iUsedCount >= 0, Fault(EInvalidStats));
+	__ASSERT_DEBUG(iStats.iLockedSegmentCount >= 0, Fault(EInvalidStats));
+	__ASSERT_DEBUG(iStats.iFilesOnClosedQueue >= 0, Fault(EInvalidStats));
+	return iStats;
+	}
+#endif	// #if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+
+
+void CCacheManager::CacheLock()
+	{
+	iLock.Wait();
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	iManagerLocked = ETrue;
+#endif
+	}
+
+void CCacheManager::CacheUnlock()
+	{
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	iManagerLocked = EFalse;
+#endif
+	iLock.Signal();
+	}
+
+
+TInt CCacheManager::AllocateAndLockCacheLine(CCacheClient* aClient, TInt64 aPos, const TCacheLine*& aCacheLine, TInt aSegmentCount)
+	{
+	__ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidAllocCount));
+
+	// check for low system memory
+	TMemoryInfoV1Buf meminfo;
+	TInt r = UserHal::MemoryInfo(meminfo);
+	__ASSERT_ALWAYS(r==KErrNone,Fault(EMemoryInfoFailed));
+	if (iMemoryLow || (meminfo().iFreeRamInBytes < iLowMemoryThreshold))
+		{
+		__CACHE_PRINT(_L("CACHEMAN: free RAM below threshold !!!"));
+		return KErrNoMemory;
+		}
+
+	CacheLock();
+
+	__CACHE_PRINT1(_L("CACHEMAN: Allocate %d segments, Lock %d"), aSegmentCount);
+	__CACHE_PRINT2(_L("CACHEMAN: iFreeQueue %d iUsedQueue %d"), iFreeQueue.Count(), iUsedQueue.Count());
+
+#ifdef  __SIMULATE_LOCK_FAILURES__
+	if (SimulatedFailure(iAllocFailureCount))
+		{
+		__CACHE_PRINT(_L("CACHEMAN: simulating allocation failure"));
+		CacheUnlock();
+		return KErrNoMemory;
+		}
+#endif
+
+	// any cachelines free ?
+	TInt freeCacheLines = iFreeQueue.Count();
+	TCacheLine* cacheLine = NULL;
+	if (freeCacheLines == 0 && !StealCacheLine(aClient))
+		{
+		CacheUnlock();
+		return KErrNotFound;
+		}
+
+	cacheLine = iFreeQueue[0];
+
+	__CACHELINE_INVARIANT((*cacheLine));
+	__ASSERT_DEBUG( cacheLine->iAllocatedSegments == 0, Fault(EInvalidAllocCount));
+	__ASSERT_DEBUG( cacheLine->iFilledSegments == 0, Fault(EInvalidFillCount));
+	__ASSERT_DEBUG( cacheLine->iDirtySegments == 0, Fault(EInvalidFillCount));
+	__ASSERT_DEBUG( cacheLine->iLockCount == 0, Fault(EInvalidLockCount));
+	__ASSERT_DEBUG( cacheLine->iLockedSegmentStart == 0, Fault(EInvalidLockedPageStart));
+	__ASSERT_DEBUG( cacheLine->iLockedSegmentCount == 0, Fault(EInvalidLockedPageCount));
+	__ASSERT_DEBUG( cacheLine->iClient == NULL, Fault(EInvalidClient));
+
+	TUint8* addr = cacheLine->iAddr;
+
+	// Commit it
+	r = Commit(addr, aSegmentCount);
+	if (r != KErrNone)
+		{
+		CacheUnlock();
+		return r;
+		}
+	cacheLine->iAllocatedSegments = (TUint8) (aSegmentCount);
+	cacheLine->iLockedSegmentStart = 0;
+	cacheLine->iLockedSegmentCount = (TUint8) aSegmentCount;
+
+	// Add to used queue
+	r = iUsedQueue.InsertInAddressOrder(cacheLine);
+	if (r != KErrNone)
+		{
+		Decommit(addr, aSegmentCount);
+		CacheUnlock();
+		return r;
+		}
+
+	cacheLine->iClient = aClient;
+	cacheLine->iPos = aPos;
+
+	// Remove from free queue
+	iFreeQueue.Remove(0);
+
+	// RChunk will lock segments initially unless explicitly unlocked
+
+	cacheLine->iLockCount++;
+	
+	__CACHE_PRINT2(_L("CACHEMAN: LockCount for %08X is %d"), cacheLine->iAddr, cacheLine->iLockCount);
+
+	CacheUnlock();
+
+	aCacheLine = cacheLine;
+	return r;
+
+	}
+
+/**
+CCacheManager::ExtendCacheLine()
+
+Attempts to extend the length of an existing cacheline by committing extra segments
+The cacheline must be owned and locked already.
+
+@param aCacheLine A reference to a locked, owned cacheline
+@param aSegmentCount The new length of the cacheline (including the existing segments)
+					 This must be greater then the existing length.
+
+@return KErrNone if successful
+		or other system wide error code.
+*/
+TInt CCacheManager::ExtendCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine, TInt aSegmentCount)
+	{
+	CacheLock();
+
+	__ASSERT_DEBUG(aSegmentCount > 0, Fault(EInvalidSegmentCount));
+
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	__ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+	__ASSERT_DEBUG(aSegmentCount > cacheLine.iAllocatedSegments, Fault(EInvalidSegmentCount));
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EInvalidLockCount));	// must be locked already
+	__ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+	// ensure all pages in cachline are locked
+	__ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(EInvalidLockRange));
+	__ASSERT_DEBUG(cacheLine.iLockedSegmentCount == cacheLine.iAllocatedSegments, Fault(EInvalidLockRange));
+
+	__CACHE_PRINT2(_L("CACHEMAN: ExtendCacheLine(%08X, %d)"), cacheLine.iAddr, aSegmentCount);
+
+	// Commit the new segments
+	TUint8* addrNewSegment = cacheLine.iAddr + (cacheLine.iAllocatedSegments << KSegmentSizeLog2);
+	TInt segmentCountNew = aSegmentCount - cacheLine.iAllocatedSegments;
+
+	TInt r = Commit(addrNewSegment, segmentCountNew);
+	if (r == KErrNone)
+		{
+		cacheLine.iAllocatedSegments = cacheLine.iLockedSegmentCount = (TUint8) aSegmentCount;
+		}
+	
+
+	CacheUnlock();
+	return r;
+	}
+
+/**
+CCacheManager::ReUseCacheLine()
+
+Attempts to lock and then extend or shrink an already owned cacheline, ready for re-use.
+If successful the cacheline is returned locked and all segments are marked as empty.
+The cacheline must initially be unlocked.
+
+@param aCacheLine A reference to an unlocked cacheline
+@param aSegmentCount The new length of the cacheline;
+					 this may be greater or less than the existing length.
+
+@return KErrNone if successful
+		or other system wide error code.
+*/
+TInt CCacheManager::ReAllocateAndLockCacheLine(CCacheClient* aClient, TInt64 aPos, const TCacheLine& aCacheLine, TInt aSegmentCount)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+
+	__ASSERT_DEBUG(aSegmentCount > 0, Fault(EInvalidSegmentCount));
+	__ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+
+	__CACHE_PRINT2(_L("CACHEMAN: ReUseCacheLine(%08X, %d)"), cacheLine.iAddr, aSegmentCount);
+
+	TInt r;
+	// old cacheline same size or bigger ?
+	if (cacheLine.iAllocatedSegments >= aSegmentCount)
+		{
+		r = LockCacheLine(aClient, aCacheLine, 0, aSegmentCount);
+		if (r!= KErrNone)
+			return r;
+		if (cacheLine.iAllocatedSegments > aSegmentCount)
+			{
+			cacheLine.iFilledSegments = (TUint8) aSegmentCount;
+			RemoveEmptySegments(aClient, aCacheLine);
+			}
+		}
+	// old cacheline smaller
+	else 
+		{
+		r = LockCacheLine(aClient, aCacheLine, 0, cacheLine.iAllocatedSegments);
+		if (r != KErrNone)
+			return r;
+		r = ExtendCacheLine(aClient, aCacheLine, aSegmentCount);
+		if (r != KErrNone)
+			{
+			UnlockCacheLine(aClient, aCacheLine);
+			return r;
+			}
+		}
+	cacheLine.iFilledSegments = 0;
+	cacheLine.iPos = aPos;
+
+	return KErrNone;
+	}
+
+
+/**
+CCacheManager::LockCacheLine()
+
+@return KErrNone on success
+*/
+TInt CCacheManager::LockCacheLine(
+	CCacheClient* aClient, 
+	const TCacheLine& aCacheLine,
+	TInt aLockedPageStart,
+	TInt aLockedPageCount)
+	{
+	CacheLock();
+
+
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	
+	// has the cacheline been stolen ?
+	if (cacheLine.iClient != aClient)
+		{
+		__CACHE_PRINT(_L("CCacheManager::LockCacheLine(), Cacheline Stolen !\n"));
+		CacheUnlock();
+		return KErrNotFound;
+		}
+	
+	// validate lock range
+	__ASSERT_DEBUG(aLockedPageStart >= 0, Fault(EInvalidLockRange));
+	__ASSERT_DEBUG(aLockedPageStart + aLockedPageCount <= cacheLine.iAllocatedSegments, Fault(EInvalidLockRange));
+	__ASSERT_DEBUG(aLockedPageCount <= iSegmentsPerCacheLine, Fault(EInvalidLockRange));
+	// if already locked, don't allow lock range to grow down or up (to simplify code)
+	__ASSERT_DEBUG(cacheLine.iLockCount == 0 || 
+			aLockedPageStart >= cacheLine.iLockedSegmentStart, 
+		Fault(EInvalidLockRange));
+	__ASSERT_DEBUG(cacheLine.iLockCount == 0 || 
+		aLockedPageStart + aLockedPageCount <= cacheLine.iLockedSegmentStart + cacheLine.iLockedSegmentCount, 
+		Fault(EInvalidLockRange));
+	
+	__CACHE_PRINT1(_L("CACHEMAN: LockCacheLine(%08X, %d)"), cacheLine.iAddr);
+
+	if (InUse(aCacheLine))
+		{
+		__CACHE_PRINT(_L("CCacheManager::LockCacheLine(), Cacheline in use !\n"));
+		CacheUnlock();
+		return KErrInUse;
+		}
+
+	TInt lockErr = KErrNone;
+	
+	// increment the lock count
+	// if not already locked, lock requested segments
+	if (cacheLine.iLockCount++ == 0)
+		{
+#ifdef __LOCK_ENTIRE_CACHELINE__
+		lockErr = Lock(cacheLine.iAddr, cacheLine.iAllocatedSegments);
+#else
+		__ASSERT_DEBUG(cacheLine.iDirtySegments == 0, Fault(ELockingAndAlreadyDirty));
+		lockErr = Lock(	cacheLine.iAddr + (aLockedPageStart<<KSegmentSizeLog2), aLockedPageCount);
+#endif
+
+		if (lockErr == KErrNone)
+			{
+			cacheLine.iLockedSegmentStart = (TUint8) aLockedPageStart;
+			cacheLine.iLockedSegmentCount = (TUint8) aLockedPageCount;
+			}
+		else	// if (lockErr != KErrNone)
+			{
+			__CACHE_PRINT2(_L("CACHEMAN: LockCacheLine(%08X) failure %d"), cacheLine.iAddr, lockErr);
+			cacheLine.iLockCount--;
+			// NB a lock failure implies segment is decomitted
+			FreeCacheLine(cacheLine);
+			}
+		}
+	// if already locked (because cacheline is dirty) ensure lock range 
+	// isn't growing (this isn't allowed to keep the code simpler) 
+	else
+		{
+		__ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(EInvalidLockRange));
+		__ASSERT_DEBUG(cacheLine.iLockedSegmentCount >= aLockedPageStart + aLockedPageCount, Fault(EInvalidLockRange));
+		}
+
+	CacheUnlock();
+	return lockErr;
+	}
+
+/**
+UnlockCacheLine()
+*/
+TBool CCacheManager::UnlockCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	CacheLock();
+
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EInvalidLockCount));
+	__ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EUnlockingUnownedCacheline));
+
+	
+	__CACHE_PRINT2(_L("CACHEMAN: UnlockCacheLine(%08X, %d)"), cacheLine.iAddr, cacheLine.iAllocatedSegments);
+
+
+	// decrement the lock count
+	TBool success = ETrue;
+
+	if (cacheLine.iLockCount > 1)
+		{
+		cacheLine.iLockCount--;
+		}
+	else
+		{
+		if (cacheLine.iDirtySegments == 0)
+			{
+			cacheLine.iLockCount--;
+#ifdef __LOCK_ENTIRE_CACHELINE__
+			Unlock(cacheLine.iAddr, cacheLine.iAllocatedSegments);
+#else
+			Unlock(
+				cacheLine.iAddr + (cacheLine.iLockedSegmentStart<<KSegmentSizeLog2), 
+				cacheLine.iLockedSegmentCount);
+#endif
+
+			cacheLine.iLockedSegmentStart = cacheLine.iLockedSegmentCount = 0;
+			}
+		else
+			{
+			__CACHE_PRINT(_L("CACHEMAN: UnlockCacheLine - not unlocking segment dirty"));
+			success = EFalse;
+			}
+		}
+
+	CacheUnlock();
+
+	return success;
+	}
+
+
+
+TBool CCacheManager::StealCacheLine(CCacheClient* aClient)
+	{
+	__ASSERT_DEBUG(iManagerLocked, Fault(EManagerNotLocked));
+
+	TInt usedQueueSize = iUsedQueue.Count();
+
+	#define INC_INDEX(x) (x++, x = (x >= usedQueueSize ? 0 : x))
+
+	__CACHE_PRINT2(_L("CACHEMAN: StealCacheLine, iNextCacheLineToSteal %d used count %d"), iNextCacheLineToSteal, iUsedQueue.Count());
+
+	TInt iInitIndex = iNextCacheLineToSteal;
+
+	// Loop through all used cachelines, starting at the last stolen 
+	// cacheline index + 1 and ending when we find a suitable cacheline 
+	// to steal or we have looped back to the start
+	for (INC_INDEX(iNextCacheLineToSteal);
+		iNextCacheLineToSteal != iInitIndex; 
+		INC_INDEX(iNextCacheLineToSteal))
+		{
+		TCacheLine& cacheLine = *iUsedQueue[iNextCacheLineToSteal];
+		if (cacheLine.iLockCount == 0 && cacheLine.iClient != aClient)
+			{
+			__CACHE_PRINT3(_L("CACHEMAN: StealCacheLine, stealing %d from %08X to %08X"), 
+				  iNextCacheLineToSteal, cacheLine.iClient, aClient);
+			FreeCacheLine(cacheLine);
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+
+TBool CCacheManager::FreeCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	CacheLock();
+
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+
+	// Has the cacheline been stolen ? (Assume success if it has)
+	if (cacheLine.iClient != aClient)
+		{
+		__CACHE_PRINT(_L("CCacheManager::FreeCacheLine(), Cacheline Stolen !!!!\n"));
+		CacheUnlock();
+		return ETrue;		
+		}
+	
+	// Is the cacheline locked ?
+	if (cacheLine.iLockCount > 0)
+		{
+		__CACHE_PRINT(_L("CCacheManager::FreeCacheLine(), attempt to free locked cacheline\n"));
+		CacheUnlock();
+		return EFalse;
+		}
+
+	FreeCacheLine(cacheLine);
+
+	CacheUnlock();
+	return ETrue;
+	}
+
+TInt CCacheManager::LockCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	
+	// cacheline stolen ?
+	if (cacheLine.iClient != aClient)
+		return 0;
+
+	return cacheLine.iLockCount;
+	}
+
+TInt CCacheManager::FillCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	
+	// cacheline stolen ?
+	if (cacheLine.iClient != aClient)
+		return 0;
+
+	return cacheLine.iFilledSegments;
+	}
+
+TInt CCacheManager::DirtyCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+
+	// cacheline stolen ?
+	if (cacheLine.iClient != aClient)
+		return 0;
+
+	return cacheLine.iDirtySegments;
+	}
+
+TBool CCacheManager::InUse(const TCacheLine& aCacheLine)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+
+	// busy if lock count >= 1 and there are no dirty segments
+	// or   if lock count >= 2 and there are dirty segments
+	return (cacheLine.iLockCount - 
+		   (cacheLine.iDirtySegments?1:0)) > 0 ? (TBool)ETrue : (TBool)EFalse;
+	}
+
+void CCacheManager::SetFilled(CCacheClient* aClient, const TCacheLine& aCacheLine,  TInt aSegmentCount)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	__ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+	__ASSERT_DEBUG(aSegmentCount <= cacheLine.iAllocatedSegments , Fault(EInvalidDirtyCount));
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ESetFilledNotLocked));
+	__ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+	
+	cacheLine.iFilledSegments = Max(cacheLine.iFilledSegments, (TUint8) aSegmentCount);
+
+	__ASSERT_DEBUG(cacheLine.iFilledSegments >= cacheLine.iDirtySegments , Fault(EInvalidDirtyCount));
+	}
+
+void CCacheManager::SetDirty(CCacheClient* aClient, const TCacheLine& aCacheLine,  TInt aSegmentCount)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	__ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+	__ASSERT_DEBUG(aSegmentCount <= cacheLine.iAllocatedSegments , Fault(EInvalidDirtyCount));
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ESetDirtyNotLocked));
+	__ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+	// ensure that lock range is valid - we insist on dirty
+	// cachelines having all dirty pages locked up to the the last dirty page
+	__ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(ESetDirtyInvalidLockRange));
+	__ASSERT_DEBUG(cacheLine.iLockedSegmentCount >= aSegmentCount, Fault(ESetDirtyInvalidLockRange));
+	
+	cacheLine.iFilledSegments = Max(cacheLine.iFilledSegments, (TUint8) aSegmentCount);
+	cacheLine.iDirtySegments = Max(cacheLine.iDirtySegments, (TUint8) aSegmentCount);
+
+	__ASSERT_DEBUG(cacheLine.iFilledSegments >= cacheLine.iDirtySegments , Fault(EInvalidDirtyCount));
+	}
+
+void CCacheManager::ClearDirty(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EClearDirtyNotLocked));
+	__ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+	TInt dirtySegments = cacheLine.iDirtySegments;
+	cacheLine.iDirtySegments = 0;
+	SetFilled(aClient, cacheLine, dirtySegments);
+	UnlockCacheLine(aClient, cacheLine);
+	}
+
+
+void CCacheManager::RemoveEmptySegments(CCacheClient* aClient, const TCacheLine& aCacheLine)
+	{
+	CacheLock();
+
+	TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+	__CACHELINE_INVARIANT((cacheLine));
+
+	// has the cacheline been stolen ?
+	if (cacheLine.iClient != aClient)
+		{
+		__CACHE_PRINT(_L("CCacheManager::RemoveEmptySegments((), Cacheline Stolen ! !!!\n"));
+		CacheUnlock();
+		return;
+		}
+		
+	__ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ERemovingEmptyUnlocked));
+
+	// Unlock any locked segments past the last filled segment
+	TInt filledSegmentCount = cacheLine.iFilledSegments;
+	TInt firstSegmentToUnlock;
+	TInt segmentsToUnlock;
+#ifdef __LOCK_ENTIRE_CACHELINE__
+	firstSegmentToUnlock = filledSegmentCount;
+	segmentsToUnlock = cacheLine.iAllocatedSegments - filledSegmentCount;
+#else
+	TInt firstLockedSegment = cacheLine.iLockedSegmentStart;
+	if (firstLockedSegment <= filledSegmentCount)
+		{
+		firstSegmentToUnlock = filledSegmentCount;
+		segmentsToUnlock = firstLockedSegment + cacheLine.iLockedSegmentCount - filledSegmentCount;
+		}
+	else
+		{
+		firstSegmentToUnlock = firstLockedSegment;
+		segmentsToUnlock = cacheLine.iLockedSegmentCount;
+		}
+#endif
+	TUint8* addrFirstSegmentToUnlock = 
+		cacheLine.iAddr + (firstSegmentToUnlock << KSegmentSizeLog2);
+	if (segmentsToUnlock > 0)
+		{
+		Unlock(addrFirstSegmentToUnlock, segmentsToUnlock);
+		cacheLine.iLockedSegmentCount = 
+			(TUint8) (cacheLine.iLockedSegmentCount - segmentsToUnlock);
+		}
+
+	// Decommit any segments past the last filled segment
+	Decommit(
+		cacheLine.iAddr + (filledSegmentCount << KSegmentSizeLog2), 
+		cacheLine.iAllocatedSegments - filledSegmentCount);
+	cacheLine.iAllocatedSegments = (TUint8) filledSegmentCount;
+
+	CacheUnlock();
+	}
+
+
+void CCacheManager::FreeCacheLine(TCacheLine& aCacheLine)
+	{
+	__ASSERT_DEBUG(iManagerLocked, Fault(EManagerNotLocked));
+	aCacheLine.iClient = NULL;
+	__ASSERT_ALWAYS( (aCacheLine.iLockCount == 0), Fault(EFreeingLockedCacheLine));
+	__ASSERT_ALWAYS( (aCacheLine.iDirtySegments == 0), Fault(EFreeingDirtyCacheLine));
+
+	Decommit(aCacheLine.iAddr, aCacheLine.iAllocatedSegments);
+	aCacheLine.iAllocatedSegments = 0;
+	aCacheLine.iFilledSegments = 0;
+	aCacheLine.iLockedSegmentStart = 0;
+	aCacheLine.iLockedSegmentCount = 0;
+
+	// Remove from used queue
+	TInt index = iUsedQueue.FindInAddressOrder(&aCacheLine);
+	__ASSERT_ALWAYS(index != KErrNotFound, Fault(ESegmentNotFound));
+	iUsedQueue.Remove(index);
+
+	// put back on free queue
+//	TInt r = iFreeQueue.Append(&aCacheLine);
+	TInt r = iFreeQueue.InsertInAddressOrder(&aCacheLine);
+	__ASSERT_ALWAYS(r == KErrNone, Fault(EAppendToFreeQueueFailed));
+
+	__CACHE_PRINT2(_L("CACHEMAN: FreeCacheLine, iFreeQueue %d iUsedQueue %d"), iFreeQueue.Count(), iUsedQueue.Count());
+	}
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+void CCacheManager::DumpCacheLine(TCacheLine& aCacheLine)
+	{
+	RDebug::Print(_L("CACHEMAN: Cacheline client %08X addr %08X pos %d alloc %d filled %d dirty %d lock %d \tData: %02X %02X %02X %02X %02X %02X %02X %02X "), 
+		aCacheLine.iClient,
+		aCacheLine.iAddr,
+		I64LOW(aCacheLine.iPos),
+
+		aCacheLine.iAllocatedSegments,
+		aCacheLine.iFilledSegments,
+		aCacheLine.iDirtySegments,
+		aCacheLine.iLockCount,
+
+		aCacheLine.iAddr[0], 
+		aCacheLine.iAddr[1], 
+		aCacheLine.iAddr[2], 
+		aCacheLine.iAddr[3], 
+		aCacheLine.iAddr[4], 
+		aCacheLine.iAddr[5], 
+		aCacheLine.iAddr[6], 
+		aCacheLine.iAddr[7]
+		);
+	}
+
+void CCacheManager::DumpCache()
+	{
+	CacheLock();
+	
+	RPointerArray<CCacheClient> clients;
+
+	RDebug::Print(_L("**** CACHEMAN: CacheLines ****\n"));
+	TInt usedQueueSize = iUsedQueue.Count();
+	TInt n;
+	for (n=0; n<usedQueueSize; n++)
+		{
+		TCacheLine& cacheLine = *iUsedQueue[n];
+		DumpCacheLine(cacheLine);
+
+		clients.InsertInAddressOrder(cacheLine.iClient);
+		}
+
+	TInt clientCount = clients.Count();
+
+	for (n=0; n<clientCount; n++)
+		{
+		RDebug::Print(_L("**** CACHEMAN: CacheClient #%d ****\n"), n);
+		clients[n]->DumpCache();
+		}
+
+	clients.Close();
+
+	CacheUnlock();
+	}
+#endif
+
+
+
+
+TUint8* CCacheManager::Base()
+	{
+	return iChunk.Base();
+	}
+
+
+TInt CCacheManager::Lock(TUint8* const aAddr, TInt aSegmentCount)
+	{
+	TInt r = iChunk.Lock(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Lock(%08X, %d) %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+	__ASSERT_DEBUG(r == KErrNone || r == KErrNoMemory || r == KErrNotFound, Fault(EUnexpectedLockFailure));
+
+	__CACHE_PRINT3(_L("CACHEMAN: LOCK %08X %d %d"), aAddr, aSegmentCount, r);
+
+#ifdef  __SIMULATE_LOCK_FAILURES__
+	if (SimulatedFailure(iLockFailureCount))
+		{
+		__CACHE_PRINT(_L("CACHEMAN: simulating lock failure"));
+		r = KErrNotFound;
+		}
+#endif
+
+	if (r == KErrNone)
+		{
+		iLockedSegmentCount+= aSegmentCount;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEMAN: LOCK FAILED"));
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iStats.iLockFailureCount++;
+#endif
+		}
+
+	return r;
+	}
+
+TInt CCacheManager::Unlock(TUint8* const aAddr, TInt aSegmentCount)
+	{
+	TInt r = iChunk.Unlock(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Unlock(%08X, %d) %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+
+	__CACHE_PRINT3(_L("CACHEMAN: UNLOCK %08X %d %d"), aAddr, aSegmentCount, r);
+	if (r == KErrNone)
+		{
+		iLockedSegmentCount-= aSegmentCount;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEMAN: UNLOCK FAILED"));
+		}
+
+	return r;
+	}
+
+TInt CCacheManager::Commit(TUint8* const aAddr, TInt aSegmentCount)
+	{
+#ifdef  __SIMULATE_LOCK_FAILURES__
+	if (SimulatedFailure(iCommitFailureCount))
+		{
+		__CACHE_PRINT(_L("CACHEMAN: simulating commit failure "));
+		return KErrNoMemory;
+		}
+#endif
+
+	TInt r = iChunk.Commit(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Commit(%08X, %d) %d, "), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+	
+	__CACHE_PRINT3(_L("CACHEMAN: COMMIT: %08X %d %d, "), aAddr, aSegmentCount, r);
+	if (r == KErrNone)
+		{
+		iLockedSegmentCount+= aSegmentCount;
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iStats.iAllocatedSegmentCount+= aSegmentCount;
+#endif
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEMAN: COMMIT FAILED"));
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iStats.iCommitFailureCount++;
+#endif
+		}
+
+
+	__ASSERT_DEBUG(r == KErrNone || r == KErrNoMemory, Fault(EUnexpectedCommitFailure));
+
+	return r;
+	}
+
+TInt CCacheManager::Decommit(TUint8* const aAddr, TInt aSegmentCount)
+	{
+	TInt r = iChunk.Decommit(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Decommit(%08X, %d), %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+
+	__CACHE_PRINT3(_L("CACHEMAN: DECOMMIT: %08X %d %d, "), aAddr, aSegmentCount, r);
+	if (r == KErrNone)
+		{
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iStats.iAllocatedSegmentCount-= aSegmentCount;
+#endif
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEMAN: DECOMMIT FAILED"));
+		}
+
+	return r;
+	}
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+void CCacheManager::SimulateLockFailureMode(TBool aEnable)
+	{
+	iSimulateLockFailureMode = aEnable;
+__CACHE_PRINT1(_L("CACHEMAN: SimulateLockFailureMode: %d, "), iSimulateLockFailureMode);
+	}
+
+void CCacheManager::AllocateMaxSegments(TBool aEnable)
+	{
+	iAllocateMaxSegments = aEnable;
+__CACHE_PRINT1(_L("CACHEMAN: iAllocateMaxSegments: %d, "), iAllocateMaxSegments);
+	}
+
+TBool CCacheManager::AllocateMaxSegments()
+	{
+	return iAllocateMaxSegments;
+	}
+
+void CCacheManager::SimulateWriteFailure()
+	{
+	iSimulateWriteFailure = ETrue;
+	}
+
+TBool CCacheManager::SimulateWriteFailureEnabled()
+	{
+	TBool b = iSimulateWriteFailure;
+	iSimulateWriteFailure = EFalse;
+	return b;
+	}
+
+TBool CCacheManager::SimulatedFailure(TInt& aFailureCount)
+	{
+#ifdef  __SIMULATE_LOCK_FAILURES__
+	if (iSimulateLockFailureMode)
+		{
+#if defined  (__RAMDOM_LOCK_FAILURES__)
+		static TInt FailCount = 15;
+		if (++aFailureCount >= FailCount)
+#elif defined (__PSEUDO_RANDOM_FAILURES__)
+		const TInt FailCounts[] = {15,2,0,21,1,12,24};
+		const TInt FailCountSize = sizeof(FailCounts) / sizeof(TInt);
+		static TInt FailCountIndex = 0; 
+		if (++aFailureCount >= FailCounts[FailCountIndex])
+#else
+		const TInt KFailCount = 15;
+		if (++aFailureCount >= KFailCount)
+#endif
+			{
+			aFailureCount = 0;
+#if defined (__RAMDOM_LOCK_FAILURES__)
+			FailCount = Math::Random() & 0x1F;
+			__CACHE_PRINT1(_L("FailCount %d"), FailCount);
+#endif
+
+#if defined (__PSEUDO_RANDOM_FAILURES__)
+			FailCountIndex = (FailCountIndex +1) % FailCountSize ;
+			__CACHE_PRINT1(_L("FailCount %d"), FailCounts[FailCountIndex]);
+#endif
+
+			return ETrue;
+			}
+		}
+#endif
+	return EFalse;
+	}
+#endif	// defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+//************************************
+// TGlobalFileCacheSettings
+//************************************
+
+//TGlobalCacheFlags TGlobalFileCacheSettings::iFlags = TGlobalCacheFlags(ECacheEnabled);
+TInt32 TGlobalFileCacheSettings::iCacheSize	= KDefaultGlobalCacheSize << KByteToByteShift;
+TInt32 TGlobalFileCacheSettings::iMaxLockedSize	= KDefaultGlobalCacheMaxLockedSize << KByteToByteShift;
+TInt32 TGlobalFileCacheSettings::iLowMemoryThreshold = KDefaultLowMemoryThreshold;
+TBool TGlobalFileCacheSettings::iEnabled	= KDefaultGlobalCacheEnabled; 
+
+_LIT8(KLitSectionNameFileCache,"FileCache");
+
+void TGlobalFileCacheSettings::ReadPropertiesFile()
+	{
+	F32Properties::GetBool(KLitSectionNameFileCache, _L8("GlobalCacheEnabled"), iEnabled);
+	
+	// Get size of cache in kilobytes
+	TInt32 cacheSizeInKBytes;
+	if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("GlobalCacheSize"), cacheSizeInKBytes))
+		iCacheSize = cacheSizeInKBytes << KByteToByteShift;
+
+	// Get maximum amount of locked data i.e data unavailable for paging
+	TInt32 maxLockedSize;
+	if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("GlobalCacheMaxLockedSize"), maxLockedSize))
+		iMaxLockedSize = maxLockedSize << KByteToByteShift;
+
+	// Get low memory threshold
+	TInt32 lowMemoryThreshold;
+	if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("LowMemoryThreshold"), lowMemoryThreshold))
+		iLowMemoryThreshold = lowMemoryThreshold;
+
+	}
+
+
+TBool TGlobalFileCacheSettings::Enabled()
+	{
+	return iEnabled;
+	}
+
+TInt TGlobalFileCacheSettings::CacheSize()
+	{
+	return iCacheSize;
+	}
+TInt TGlobalFileCacheSettings::MaxLockedSize()
+	{
+	return iMaxLockedSize;
+	}
+
+TInt TGlobalFileCacheSettings::LowMemoryThreshold()
+	{
+	return iLowMemoryThreshold;
+	}