// 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 = EFalse;	// off by default unless switched on by KControlIoSimulateLockFailureMode
#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;
	}
