userlibandfileserver/fileserver/sfile/sf_cache_client.cpp
changeset 0 a41df078684a
child 43 c1f20ce4abcf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_cache_client.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,746 @@
+// 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_client.cpp
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include "sf_std.h"
+#include <e32uid.h>
+#include <e32wins.h>
+#include <f32file.h>
+#include "sf_cache_man.h"
+#include "sf_cache_client.h"
+#include "sf_file_cache_defs.h"
+
+// This macro chnages the code so that new cachelines are stolen from the queue (if it is full)
+// thus reducing the calls to RChunk::Commit
+#define SYMBIAN_REUSE_STALE_CACHELINES
+
+enum TCacheClientFault
+	{
+	ECacheClientSegmentNotFound0,
+	ECacheClientSegmentNotFound1,
+	ECacheClientSegmentNotFound2,
+	ECacheClientSegmentNotFound3,
+	ECacheClientSegmentNotFound4,
+	ECacheClientSegmentNotFound5,
+	ECacheClientSegmentNotFound6,
+	ECacheClientSegmentNotFound7,
+	ECacheClientLruInsertFailed,
+	ECacheClientInsertFailed,
+	ECacheClientRemovingBusySegment,
+	ECacheClientRemovingLockedSegment,
+	ECacheClientRemovingDirtySegment,
+	ECacheClientInvalidSegmentCount,
+	ECacheClientAlreadyLocked,
+	ECacheClientSegmentNotLocked,
+	ECacheClientSegmentNotDirty,
+	ECacheClientSegmentAlreadyUnlocked,
+	ECacheClientSegmentBadLockCount,
+	ECacheClientTotalByteCountInvalid,
+	ECacheClientUnexpectedHole,
+	ECacheClientSegmentAlreadyAllocated
+	};
+
+
+LOCAL_C void Fault(TCacheClientFault aFault)
+//
+// Report a fault in the cache client
+//
+	{
+	User::Panic(_L("FSCACHECLIENT"), aFault);
+	}
+
+
+const TInt KSegmentArrayGranularity = 4;
+
+
+//******************************
+// CCacheClient
+//******************************
+
+CCacheClient* CCacheClient::NewL(CCacheManager& aManager)
+	{
+	CCacheClient* client = new(ELeave) CCacheClient(aManager);
+	
+	CleanupStack::PushL(client);
+	client->ConstructL();
+	CleanupStack::Pop(1, client);
+
+	return client;
+	}
+
+void CCacheClient::ConstructL()
+	{
+	iCacheLines = new(ELeave) RPointerArray<CCacheManager::TCacheLine>(KSegmentArrayGranularity);
+	
+	iLastCacheLineIndex = -1;
+	}
+
+CCacheClient::CCacheClient(CCacheManager& aManager) : iManager(aManager)
+	{
+	__CACHE_PRINT(_L("CACHECLIENT: CCacheClient()"));
+	
+	iMaxBytesCached = 0;
+	}
+
+/** 
+Purge() - free all cachelines
+if aPurgeDirty == ETrue then discard all dirty data 
+				  E.g. if called from the destructor or following media removal
+*/
+void CCacheClient::Purge(TBool aPurgeDirty)
+	{
+	TInt cacheLineCount = iCacheLines->Count();
+	TInt n;
+	for (n=0; n<cacheLineCount; n++)
+		{
+		CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[n];
+
+		
+		// Purge any locked and/or dirty cachlines if we still own them 
+		// Freeing any locked or dirty pages implies that flushing the file 
+		// has failed for some reason (media removal for example).
+		//
+		// NB if we own a cacheline and it is "in use" it's an error
+		// NB we need to check if it's locked first and then if we own it
+		// to guard against it being stolen by another thread.
+		if (aPurgeDirty && iManager.DirtyCount(this, cacheLine) != 0 && cacheLine.iClient == this)
+			{
+#ifdef _DEBUG
+			if (iManager.LockCount(this, cacheLine) != 0)
+				__CACHE_PRINT(_L("CACHECLIENT: purging with locked segments"));
+			if (iManager.DirtyCount(this, cacheLine) != 0)
+				__CACHE_PRINT(_L("CACHECLIENT: purging dirty segments"));
+			__ASSERT_DEBUG(!iManager.InUse(cacheLine), Fault(ECacheClientRemovingBusySegment));
+#endif
+			if (iManager.DirtyCount(this, cacheLine) != 0)
+				iManager.ClearDirty(this, cacheLine);
+			while (iManager.LockCount(this, cacheLine) != 0)
+				iManager.UnlockCacheLine(this, cacheLine);
+			}	// if aPurgeDirty
+
+		TBool success = iManager.FreeCacheLine(this, cacheLine);
+		
+		// if (aPurgeDirty == ETrue), then it is an error
+		// if a cacheline could not be freed (because  it was locked).
+		if (aPurgeDirty)
+			{
+			__ASSERT_ALWAYS(success, Fault(ECacheClientRemovingLockedSegment));
+			}
+		}
+
+	}
+
+CCacheClient::~CCacheClient()
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: ~CCacheClient() this %08X"), this);
+
+	if (iCacheLines)
+		{
+		Purge(ETrue);
+		iCacheLines->Close();	
+		}
+
+	delete iCacheLines;
+	}
+
+void CCacheClient::SetMaxSegments(TInt aMaxSegments)
+	{
+	iMaxBytesCached = aMaxSegments << iManager.SegmentSizeLog2();
+	}
+
+
+TInt CCacheClient::SegmentSize()
+	{
+	return iManager.SegmentSize();
+	}
+
+TInt CCacheClient::SegmentSizeLog2()
+	{
+	return iManager.SegmentSizeLog2();
+	}
+
+TInt64 CCacheClient::SegmentSizeMask()
+	{
+	return iManager.SegmentSizeMask();
+	}
+
+TInt CCacheClient::CacheLineSize()
+	{
+	return iManager.CacheLineSize();
+	}
+
+TInt CCacheClient::CacheLineSizeInSegments()
+	{
+	return iManager.CacheLineSizeInSegments();
+	}
+
+TUint8* CCacheClient::FindAndLockSegments(TInt64 aPos, TInt& aSegmentCount, TInt& aFilledSegmentCount, TInt& aLockError, TBool aMakeDirty)
+	{
+	TInt maxSegments = Min(aSegmentCount, CacheLineSizeInSegments());
+	aSegmentCount = 0;
+	aFilledSegmentCount = 0;
+
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	aLockError = KErrNone;
+
+	if (!cacheLine)
+		return NULL;
+
+	TInt offset = (TInt) (aPos - cacheLine->iPos);
+	TInt len = CacheLineLen(cacheLine) - offset;
+	
+	TInt segmentSizeLog2 = iManager.SegmentSizeLog2();
+
+	TInt startSegmentNumber = offset >> segmentSizeLog2;
+	TInt filledCount = iManager.FillCount(this, *cacheLine);
+	
+	// round up to a whole number of segments
+	aSegmentCount = ((len + iManager.SegmentSize() - 1) >> segmentSizeLog2);
+	aSegmentCount = Min(aSegmentCount, maxSegments);
+
+	// get filled count
+	aFilledSegmentCount = (filledCount > startSegmentNumber)?filledCount - startSegmentNumber:0;
+
+	__CACHE_PRINT4(_L("CACHECLIENT: FindAndLockSegments(), Client %08X pos %d addr %08X len %d"), 
+		this, I64LOW(cacheLine->iPos), cacheLine->iAddr, CacheLineLen(cacheLine));
+
+
+	aLockError = LockSegments(aPos, aSegmentCount, aMakeDirty);
+	if (aLockError != KErrNone)
+		return NULL;
+
+	return cacheLine->iAddr + offset;
+	}
+
+TUint8* CCacheClient::FindSegment(TInt64 aPos)
+	{
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	if (!cacheLine)
+		return NULL;
+
+	TInt offset = (TInt) (aPos - cacheLine->iPos);
+	return cacheLine->iAddr + offset;
+	}
+
+TUint8* CCacheClient::AllocateAndLockSegments(TInt64 aPos, TInt& aSegmentCount, TBool aMakeDirty, TBool aExtendExisting)
+	{
+	__ASSERT_DEBUG(aSegmentCount >= 0, Fault(ECacheClientInvalidSegmentCount));
+
+	TInt segmentSizeLog2 = iManager.SegmentSizeLog2();
+	TInt segmentSize = iManager.SegmentSize();
+	TInt cacheLineSize = iManager.CacheLineSize();
+
+	aPos = (aPos >> segmentSizeLog2) << segmentSizeLog2;
+
+
+	// count number of uncached segments
+	TInt maxSegments = Min(aSegmentCount, CacheLineSizeInSegments());
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	// This is test code and changes the behaviour so that we always allocate
+	// as large a cacheline as possible, so as to increase the likelihood of "holes"
+	if (iManager.AllocateMaxSegments())
+		{
+		maxSegments = cacheLineSize >> segmentSizeLog2;
+		}
+#endif
+
+	// make sure requested range doesn't overlap with an existing cacheline - 
+	// if it does, shrink requested length down
+	TInt64 endPos = aPos + (maxSegments << segmentSizeLog2);
+	TInt cacheLineCount = iCacheLines->Count();
+	for (TInt index=0; index < cacheLineCount; index++)
+		{
+		const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[index];
+		TInt64 startPos = cacheLine->iPos;
+		if (cacheLine->iClient == this && startPos >= aPos && startPos < endPos)
+			{
+			endPos = cacheLine->iPos;
+			}
+		}
+	TInt segmentCount = (TInt) ((endPos - aPos) >> segmentSizeLog2);
+	
+	__ASSERT_DEBUG(segmentCount > 0, Fault(ECacheClientSegmentAlreadyAllocated));
+	
+	aSegmentCount = Min(segmentCount, aSegmentCount);
+
+	iLastCacheLineIndex = -1;
+
+	TUint8* segmentAddr = NULL;
+
+	__CACHE_PRINT2(_L("CACHECLIENT: AllocateAndLockSegments(%ld, %d)"), aPos, segmentCount);
+
+	// can we extend an existing cacheline ? (this saves wasting virtual memory)
+	// NB if we're about the make the new segment(s) dirty, only extend if last segment in existing
+	// cacheline is dirty, otherwise we'll end up unnecessarily marking lots of clean segments as dirty
+	if (aExtendExisting)
+		{
+		TInt64 posLastSegment = aPos - segmentSize;
+		const CCacheManager::TCacheLine* cacheLineOld;
+		TInt r;
+		if ((posLastSegment >= 0) && ((cacheLineOld = FindCacheLine(posLastSegment)) != NULL))
+			{
+			TInt prevSegmentNumber = StartSegment(cacheLineOld, posLastSegment);
+			TInt fillCountOld = iManager.FillCount(this, *cacheLineOld);
+			TInt fillCountNew = segmentCount + prevSegmentNumber+1;	// fillCountOld;
+			if (prevSegmentNumber+1 == fillCountOld &&
+				fillCountNew <= (cacheLineSize >> segmentSizeLog2) &&
+				(!aMakeDirty || (prevSegmentNumber+1 == cacheLineOld->iDirtySegments))
+				)
+				{
+				__CACHE_PRINT(_L("CACHE: extending cacheline"));
+
+				segmentAddr = cacheLineOld->iAddr + (TInt) (aPos - cacheLineOld->iPos);
+
+				// Lock the cacheline and then try to extend it
+				r = LockSegments(cacheLineOld->iPos, cacheLineOld->iAllocatedSegments, ETrue);
+				if (r == KErrNone)
+					{
+					r = iManager.ExtendCacheLine(this, *cacheLineOld, fillCountNew);
+//RDebug::Print(_L("GROW addr %08X, prevSegmentNumber %d, fillCount %d, fillCountNew %d, r %d\n"),
+//			  cacheLineOld->iAddr, prevSegmentNumber, fillCountOld, fillCountNew, r);
+					if (r == KErrNone)
+						{
+						// If we've exceeded max, use LRU queue to discard old cachelines
+						RemoveLru(cacheLineOld);
+						return segmentAddr;
+						}
+					__CACHE_PRINT(_L("LockSegments FAIL"));
+					UnlockSegments(posLastSegment);	
+					}
+				}
+			}
+		}
+
+	// reserve space in the segment array & LRU queue so we don't have to 
+	// worry about failing to append
+	if (iCacheLines->Reserve(1) != KErrNone)
+		return NULL;
+
+	const CCacheManager::TCacheLine* cacheLineNew = NULL;
+	TBool reUsedCacheLineGrown = EFalse;
+
+#ifdef SYMBIAN_REUSE_STALE_CACHELINES
+	// try to re-use an old cacheline if possible to avoid having to commit a new one
+	// as this involves calling RChunk::Commit() which takes some time....
+	TInt lenNewCacheLine = segmentCount << segmentSizeLog2;
+	TInt totalBytesCached = CachedBytes() + lenNewCacheLine;
+	
+	cacheLineCount = iCacheLines->Count();
+	if (cacheLineCount > 0)
+		{
+		const CCacheManager::TCacheLine* cacheLineOld = (*iCacheLines)[cacheLineCount - 1];
+		TInt lenOldCacheLine = cacheLineOld->iFilledSegments << segmentSizeLog2;
+		reUsedCacheLineGrown = segmentCount > cacheLineOld->iAllocatedSegments;
+		if (totalBytesCached + lenNewCacheLine - lenOldCacheLine > iMaxBytesCached &&
+			cacheLineOld->iClient == this &&
+			cacheLineOld->iLockCount == 0)
+			{
+			if (iManager.ReAllocateAndLockCacheLine(this, aPos, *cacheLineOld, segmentCount) == KErrNone)
+				{
+				cacheLineNew = cacheLineOld;
+				}
+			}
+		}
+#endif	// SYMBIAN_REUSE_STALE_CACHELINES
+	TBool reUsingCacheline = cacheLineNew ? (TBool)ETrue:(TBool)EFalse;
+
+	// Request a free segment from cache manager
+	if (reUsingCacheline)
+		{
+		iCacheLines->Remove(cacheLineCount-1);
+		}
+	else
+		{
+		TInt r = iManager.AllocateAndLockCacheLine(this, aPos, cacheLineNew, segmentCount);
+		if (r != KErrNone)
+			{
+			__CACHE_PRINT1(_L("CACHECLIENT: AllocateSegment() failed r %d"), r);
+			return NULL;
+			}
+		// check the address isn't already in the iCacheLines - 
+		// this can happen if it was stolen from us and we then steal it back
+		TInt indexDup = iCacheLines->Find(cacheLineNew);
+		if (indexDup != KErrNotFound)
+			iCacheLines->Remove(indexDup);
+		}
+
+
+	segmentAddr = cacheLineNew->iAddr;
+
+	// Add to top of LRU queue
+	TInt r = iCacheLines->Insert(cacheLineNew, 0);
+	__ASSERT_ALWAYS(r == KErrNone, Fault(ECacheClientInsertFailed));
+
+	__CACHE_PRINT4(_L("CACHECLIENT: AllocateAndLockSegments(), Client %08X pos %d addr %08X len %d"), 
+		this, I64LOW(cacheLineNew->iPos), cacheLineNew->iAddr, CacheLineLen(cacheLineNew));
+
+	// assume LRU in use ...
+	iLastCacheLineIndex = 0;
+
+	if (!reUsingCacheline || reUsedCacheLineGrown)
+		RemoveLru(cacheLineNew);
+
+	return segmentAddr;
+	}
+
+
+
+TInt CCacheClient::StartSegment(const CCacheManager::TCacheLine* aCacheLine, TInt64 aPos)
+	{
+	TInt offset = (TInt) (aPos - aCacheLine->iPos);
+	return offset >> iManager.SegmentSizeLog2();
+	}
+
+TInt CCacheClient::CacheLineLen(const CCacheManager::TCacheLine* aCacheLine)
+	{
+	return aCacheLine->iAllocatedSegments << iManager.SegmentSizeLog2();
+	}
+
+
+void CCacheClient::MarkSegmentsAsFilled(TInt64 aPos, TInt aSegmentCount)
+	{
+	__CACHE_PRINT2(_L("CACHECLIENT: Mark Filled Segments(%ld, %d)"), aPos, aSegmentCount);
+
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound0));
+
+	iManager.SetFilled(this, *cacheLine,  StartSegment(cacheLine, aPos) + aSegmentCount);
+	}
+
+void CCacheClient::MarkSegmentsAsDirty(TInt64 aPos, TInt aSegmentCount)
+	{
+	__CACHE_PRINT2(_L("CACHECLIENT: Mark Dirty Segments(%ld, %d)"), aPos, aSegmentCount);
+
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound1));
+
+	iManager.SetDirty(this, *cacheLine,  StartSegment(cacheLine, aPos) + aSegmentCount);
+	}
+
+TInt CCacheClient::FindFirstDirtySegment(TInt64& aPos, TUint8*& aAddr)
+	{
+	TInt cacheLineCount = iCacheLines->Count();
+
+	aPos = KMaxTInt64;
+	TInt segmentCount = 0;
+	for (TInt index = 0; index< cacheLineCount; index++)
+		{
+		CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[index];
+
+		TInt dirtyCount = iManager.DirtyCount(this, cacheLine);
+		if ((dirtyCount > 0 && cacheLine.iPos < aPos))
+			{
+			aPos = cacheLine.iPos;
+			aAddr = cacheLine.iAddr;
+			segmentCount = dirtyCount;
+			}
+		}
+
+	return segmentCount;
+	}
+
+
+void CCacheClient::MarkSegmentsAsClean(TInt64 aPos)
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: MarkSegmentsAsClean(%d)"), I64LOW(aPos));
+	
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound2));
+
+	// this sets the dirty count to zero, unlocks the cacheline and 
+	// marks all the dirty pages as filled 
+	iManager.ClearDirty(this, *cacheLine);
+
+	RemoveLru(cacheLine);
+	}
+
+
+void CCacheClient::RemoveEmptySegments(const CCacheManager::TCacheLine* aCacheLine)
+	{
+	iManager.RemoveEmptySegments(this, *aCacheLine);
+	}
+
+TInt CCacheClient::LockSegments(TInt64 aPos, TInt aSegmentCount, TBool aMakeDirty)
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: LockCacheLine(%d, %d)"), I64LOW(aPos));
+
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound3));
+
+
+	TInt startSegmentNumber = StartSegment(cacheLine, aPos);
+	
+	TInt lockError;
+	if (aMakeDirty)
+		lockError = iManager.LockCacheLine(this, *cacheLine, 0, cacheLine->iAllocatedSegments);
+	else
+		lockError = iManager.LockCacheLine(this, *cacheLine, startSegmentNumber, aSegmentCount);
+
+	if (lockError == KErrInUse)
+		{
+		return lockError;
+		}
+	else if (lockError != KErrNone)
+		{
+		RemoveCacheLine(cacheLine);
+		return lockError;
+		}
+
+	// Check for "holes".i.e. empty segments before this one.
+	// If found, discard the empty segments and return KErrNotFound
+	TInt filledCount = iManager.FillCount(this, *cacheLine);
+	if (startSegmentNumber > filledCount)
+		{
+		__CACHE_PRINT(_L("CACHE: found hole"));
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iManager.Stats().iHoleCount++;
+#endif
+
+
+		RemoveEmptySegments(cacheLine);
+		iManager.UnlockCacheLine(this, *cacheLine);
+		if (CacheLineLen(cacheLine) == 0)
+			FreeCacheLine(cacheLine);
+		return KErrNotFound;
+		}
+
+	// Move this entry to top of LRU queue
+	iCacheLines->Remove(iLastCacheLineIndex);
+#ifdef _DEBUG
+	TInt r = 
+#endif
+	iCacheLines->Insert(cacheLine, 0);
+	__ASSERT_DEBUG(r == KErrNone, Fault(ECacheClientLruInsertFailed));
+	iLastCacheLineIndex = 0;
+
+	
+	return KErrNone;
+	}
+
+
+/**
+UnlockSegments()
+*/
+void CCacheClient::UnlockSegments(TInt64 aPos)
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: UnlockSegments(%d, %d)"), I64LOW(aPos));
+
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound4));
+
+	iManager.UnlockCacheLine(this, *cacheLine);
+	}
+
+
+TInt CCacheClient::FindCacheLineIndex(TInt64 aPos)
+	{
+	TInt cacheLineCount = iCacheLines->Count();
+
+	if 	(iLastCacheLineIndex != -1 && iLastCacheLineIndex < cacheLineCount)
+		{
+		const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[iLastCacheLineIndex];
+
+		if (cacheLine->iClient == this && 
+			aPos >= cacheLine->iPos &&
+			aPos < cacheLine->iPos+CacheLineLen(cacheLine))
+			return iLastCacheLineIndex;
+		}		
+
+	for (TInt index=0; index < cacheLineCount; index++)
+		{
+		const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[index];
+
+		if (cacheLine->iClient == this && 
+			aPos >= cacheLine->iPos &&
+			(aPos < cacheLine->iPos+CacheLineLen(cacheLine)))
+			{
+			iLastCacheLineIndex = index;
+			return index;
+			}
+		}
+		
+	return KErrNotFound;
+	}
+
+const CCacheManager::TCacheLine*  CCacheClient::FindCacheLine(TInt64 aPos)
+	{
+	TInt index = FindCacheLineIndex(aPos);
+
+	return (index == KErrNotFound) ? NULL : (*iCacheLines)[index];
+	}
+
+
+TBool CCacheClient::SegmentEmpty(TInt64 aPos)
+	{
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound5));
+
+	TInt startSegment = StartSegment(cacheLine, aPos);
+	TInt fillCount = iManager.FillCount(this, *cacheLine);
+
+
+	return startSegment >= fillCount ? (TBool)ETrue : (TBool)EFalse;
+	}
+
+
+TBool CCacheClient::SegmentDirty(TInt64 aPos)
+	{
+	const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos);
+
+	__ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound6));
+
+	TInt startSegment = StartSegment(cacheLine, aPos);
+	TInt dirtyCount = iManager.DirtyCount(this, *cacheLine);
+
+	return startSegment >= dirtyCount ? (TBool)ETrue : (TBool)EFalse;
+	}
+
+// return whether there are too many locked pages globally (for all files)
+// or too many for this file
+TBool CCacheClient::TooManyLockedSegments()
+	{
+	if (iManager.TooManyLockedSegments())
+		return ETrue;
+	if (LockedBytes() > iMaxBytesCached)
+		return ETrue;
+	return EFalse;
+	}
+
+
+TInt CCacheClient::CachedBytes()
+	{
+	TInt cacheLineCount = iCacheLines->Count();
+
+	TInt totalBytesCached = 0;
+	for (TInt n=0; n < cacheLineCount; n++)
+		totalBytesCached+= ((*iCacheLines)[n]->iFilledSegments << iManager.SegmentSizeLog2());
+	return totalBytesCached;
+	}
+
+TInt CCacheClient::LockedBytes()
+	{
+	TInt cacheLineCount = iCacheLines->Count();
+	TInt segmentSizeLog2 = iManager.SegmentSizeLog2();
+
+	TInt totalBytesLocked = 0;
+	for (TInt n=0; n < cacheLineCount; n++)
+		{
+		CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[n];
+		if (cacheLine.iLockCount > 0)
+			totalBytesLocked+=  cacheLine.iAllocatedSegments << segmentSizeLog2;
+		}
+	return totalBytesLocked;
+	}
+
+void CCacheClient::RemoveLru(const CCacheManager::TCacheLine* aCacheLineToExclude)
+	{
+	if (iMaxBytesCached == 0)	// LRU queue ?
+		return;
+
+	// If we've exceeded max, use LRU queue to remove stuff
+
+	TInt cacheLineCount = iCacheLines->Count();
+	TInt segmentSizeLog2 = iManager.SegmentSizeLog2();
+
+	// calculate the total bytes cached in all cachelines we own
+	// NB it's difficult to keep a track of this in this class because cachelines can be stolen (!)
+	TInt totalBytesCached = CachedBytes();
+	for (TInt lastIndex = cacheLineCount - 1; 
+		lastIndex >= 0 && totalBytesCached > iMaxBytesCached;
+		lastIndex--)
+		{
+		const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[lastIndex];
+
+//		__CACHE_PRINT3(_L("Test cacheline index %d pos %ld len %d"), lastIndex, cacheLine.iPos, cacheLine.iLen);
+
+		if (cacheLine != aCacheLineToExclude)
+			{
+			// if removing a cacheline will bring the total to UNDER the "maximum"
+			// then don't remove it and abort - to do otherwise could remove
+			// data that has just been read-ahead ....
+			TInt lenCacheLine = cacheLine->iFilledSegments << segmentSizeLog2;
+			if (totalBytesCached - lenCacheLine	<= iMaxBytesCached)
+				{
+				__CACHE_PRINT(_L("CACHECLIENT: cacheline too big to remove, aborting.."));
+				break;
+				}
+
+			__CACHE_PRINT3(_L("CACHECLIENT: Removing LRU index %d pos %ld len %d"), lastIndex, cacheLine->iPos, CacheLineLen(cacheLine));
+			TInt bytesFreed = cacheLine->iFilledSegments << iManager.SegmentSizeLog2();
+			if (FreeCacheLine(cacheLine))
+				{
+				totalBytesCached-= bytesFreed;
+				__ASSERT_ALWAYS(totalBytesCached >= 0, Fault(ECacheClientTotalByteCountInvalid));
+				}
+			}
+		}
+	}
+
+
+
+TBool CCacheClient::FreeCacheLine(const CCacheManager::TCacheLine* aCacheLine)
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: FreeCacheLine(%d) )"), I64LOW(aCacheLine->iPos));
+
+
+	TBool success = iManager.FreeCacheLine(this, *aCacheLine);
+
+	if (success)
+		RemoveCacheLine(aCacheLine);
+
+	return success;
+	}
+
+void CCacheClient::RemoveCacheLine(const CCacheManager::TCacheLine* aCacheLine)
+	{
+	__CACHE_PRINT1(_L("CACHECLIENT: RemoveCacheLine(%d) )"), I64LOW(aCacheLine->iPos));
+
+	TInt index = iCacheLines->Find(aCacheLine);
+
+	__ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheClientSegmentNotFound7));
+
+	iCacheLines->Remove(index);
+	}
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+void CCacheClient::DumpCache()
+	{
+	RDebug::Print(_L("**** CACHECLIENT: CacheLines ****\n"));
+	TInt cacheLineCount = iCacheLines->Count();
+
+	for (TInt index = 0; index< cacheLineCount; index++)
+		{
+		CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[index];
+		iManager.DumpCacheLine(cacheLine);
+		}
+	}
+#endif