|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // f32\sfile\sf_cache_client.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 /** |
|
19 @file |
|
20 @internalTechnology |
|
21 */ |
|
22 |
|
23 #include <e32std.h> |
|
24 #include <e32std_private.h> |
|
25 #include "sf_std.h" |
|
26 #include <e32uid.h> |
|
27 #include <e32wins.h> |
|
28 #include <f32file.h> |
|
29 #include "sf_cache_man.h" |
|
30 #include "sf_cache_client.h" |
|
31 #include "sf_file_cache_defs.h" |
|
32 |
|
33 // This macro chnages the code so that new cachelines are stolen from the queue (if it is full) |
|
34 // thus reducing the calls to RChunk::Commit |
|
35 #define SYMBIAN_REUSE_STALE_CACHELINES |
|
36 |
|
37 enum TCacheClientFault |
|
38 { |
|
39 ECacheClientSegmentNotFound0, |
|
40 ECacheClientSegmentNotFound1, |
|
41 ECacheClientSegmentNotFound2, |
|
42 ECacheClientSegmentNotFound3, |
|
43 ECacheClientSegmentNotFound4, |
|
44 ECacheClientSegmentNotFound5, |
|
45 ECacheClientSegmentNotFound6, |
|
46 ECacheClientSegmentNotFound7, |
|
47 ECacheClientLruInsertFailed, |
|
48 ECacheClientInsertFailed, |
|
49 ECacheClientRemovingBusySegment, |
|
50 ECacheClientRemovingLockedSegment, |
|
51 ECacheClientRemovingDirtySegment, |
|
52 ECacheClientInvalidSegmentCount, |
|
53 ECacheClientAlreadyLocked, |
|
54 ECacheClientSegmentNotLocked, |
|
55 ECacheClientSegmentNotDirty, |
|
56 ECacheClientSegmentAlreadyUnlocked, |
|
57 ECacheClientSegmentBadLockCount, |
|
58 ECacheClientTotalByteCountInvalid, |
|
59 ECacheClientUnexpectedHole, |
|
60 ECacheClientSegmentAlreadyAllocated |
|
61 }; |
|
62 |
|
63 |
|
64 LOCAL_C void Fault(TCacheClientFault aFault) |
|
65 // |
|
66 // Report a fault in the cache client |
|
67 // |
|
68 { |
|
69 User::Panic(_L("FSCACHECLIENT"), aFault); |
|
70 } |
|
71 |
|
72 |
|
73 const TInt KSegmentArrayGranularity = 4; |
|
74 |
|
75 |
|
76 //****************************** |
|
77 // CCacheClient |
|
78 //****************************** |
|
79 |
|
80 CCacheClient* CCacheClient::NewL(CCacheManager& aManager) |
|
81 { |
|
82 CCacheClient* client = new(ELeave) CCacheClient(aManager); |
|
83 |
|
84 CleanupStack::PushL(client); |
|
85 client->ConstructL(); |
|
86 CleanupStack::Pop(1, client); |
|
87 |
|
88 return client; |
|
89 } |
|
90 |
|
91 void CCacheClient::ConstructL() |
|
92 { |
|
93 iCacheLines = new(ELeave) RPointerArray<CCacheManager::TCacheLine>(KSegmentArrayGranularity); |
|
94 |
|
95 iLastCacheLineIndex = -1; |
|
96 } |
|
97 |
|
98 CCacheClient::CCacheClient(CCacheManager& aManager) : iManager(aManager) |
|
99 { |
|
100 __CACHE_PRINT(_L("CACHECLIENT: CCacheClient()")); |
|
101 |
|
102 iMaxBytesCached = 0; |
|
103 } |
|
104 |
|
105 /** |
|
106 Purge() - free all cachelines |
|
107 if aPurgeDirty == ETrue then discard all dirty data |
|
108 E.g. if called from the destructor or following media removal |
|
109 */ |
|
110 void CCacheClient::Purge(TBool aPurgeDirty) |
|
111 { |
|
112 TInt cacheLineCount = iCacheLines->Count(); |
|
113 TInt n; |
|
114 for (n=0; n<cacheLineCount; n++) |
|
115 { |
|
116 CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[n]; |
|
117 |
|
118 |
|
119 // Purge any locked and/or dirty cachlines if we still own them |
|
120 // Freeing any locked or dirty pages implies that flushing the file |
|
121 // has failed for some reason (media removal for example). |
|
122 // |
|
123 // NB if we own a cacheline and it is "in use" it's an error |
|
124 // NB we need to check if it's locked first and then if we own it |
|
125 // to guard against it being stolen by another thread. |
|
126 if (aPurgeDirty && iManager.DirtyCount(this, cacheLine) != 0 && cacheLine.iClient == this) |
|
127 { |
|
128 #ifdef _DEBUG |
|
129 if (iManager.LockCount(this, cacheLine) != 0) |
|
130 __CACHE_PRINT(_L("CACHECLIENT: purging with locked segments")); |
|
131 if (iManager.DirtyCount(this, cacheLine) != 0) |
|
132 __CACHE_PRINT(_L("CACHECLIENT: purging dirty segments")); |
|
133 __ASSERT_DEBUG(!iManager.InUse(cacheLine), Fault(ECacheClientRemovingBusySegment)); |
|
134 #endif |
|
135 if (iManager.DirtyCount(this, cacheLine) != 0) |
|
136 iManager.ClearDirty(this, cacheLine); |
|
137 while (iManager.LockCount(this, cacheLine) != 0) |
|
138 iManager.UnlockCacheLine(this, cacheLine); |
|
139 } // if aPurgeDirty |
|
140 |
|
141 TBool success = iManager.FreeCacheLine(this, cacheLine); |
|
142 |
|
143 // if (aPurgeDirty == ETrue), then it is an error |
|
144 // if a cacheline could not be freed (because it was locked). |
|
145 if (aPurgeDirty) |
|
146 { |
|
147 __ASSERT_ALWAYS(success, Fault(ECacheClientRemovingLockedSegment)); |
|
148 } |
|
149 } |
|
150 |
|
151 } |
|
152 |
|
153 CCacheClient::~CCacheClient() |
|
154 { |
|
155 __CACHE_PRINT1(_L("CACHECLIENT: ~CCacheClient() this %08X"), this); |
|
156 |
|
157 if (iCacheLines) |
|
158 { |
|
159 Purge(ETrue); |
|
160 iCacheLines->Close(); |
|
161 } |
|
162 |
|
163 delete iCacheLines; |
|
164 } |
|
165 |
|
166 void CCacheClient::SetMaxSegments(TInt aMaxSegments) |
|
167 { |
|
168 iMaxBytesCached = aMaxSegments << iManager.SegmentSizeLog2(); |
|
169 } |
|
170 |
|
171 |
|
172 TInt CCacheClient::SegmentSize() |
|
173 { |
|
174 return iManager.SegmentSize(); |
|
175 } |
|
176 |
|
177 TInt CCacheClient::SegmentSizeLog2() |
|
178 { |
|
179 return iManager.SegmentSizeLog2(); |
|
180 } |
|
181 |
|
182 TInt64 CCacheClient::SegmentSizeMask() |
|
183 { |
|
184 return iManager.SegmentSizeMask(); |
|
185 } |
|
186 |
|
187 TInt CCacheClient::CacheLineSize() |
|
188 { |
|
189 return iManager.CacheLineSize(); |
|
190 } |
|
191 |
|
192 TInt CCacheClient::CacheLineSizeInSegments() |
|
193 { |
|
194 return iManager.CacheLineSizeInSegments(); |
|
195 } |
|
196 |
|
197 TUint8* CCacheClient::FindAndLockSegments(TInt64 aPos, TInt& aSegmentCount, TInt& aFilledSegmentCount, TInt& aLockError, TBool aMakeDirty) |
|
198 { |
|
199 TInt maxSegments = Min(aSegmentCount, CacheLineSizeInSegments()); |
|
200 aSegmentCount = 0; |
|
201 aFilledSegmentCount = 0; |
|
202 |
|
203 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
204 |
|
205 aLockError = KErrNone; |
|
206 |
|
207 if (!cacheLine) |
|
208 return NULL; |
|
209 |
|
210 TInt offset = (TInt) (aPos - cacheLine->iPos); |
|
211 TInt len = CacheLineLen(cacheLine) - offset; |
|
212 |
|
213 TInt segmentSizeLog2 = iManager.SegmentSizeLog2(); |
|
214 |
|
215 TInt startSegmentNumber = offset >> segmentSizeLog2; |
|
216 TInt filledCount = iManager.FillCount(this, *cacheLine); |
|
217 |
|
218 // round up to a whole number of segments |
|
219 aSegmentCount = ((len + iManager.SegmentSize() - 1) >> segmentSizeLog2); |
|
220 aSegmentCount = Min(aSegmentCount, maxSegments); |
|
221 |
|
222 // get filled count |
|
223 aFilledSegmentCount = (filledCount > startSegmentNumber)?filledCount - startSegmentNumber:0; |
|
224 |
|
225 __CACHE_PRINT4(_L("CACHECLIENT: FindAndLockSegments(), Client %08X pos %d addr %08X len %d"), |
|
226 this, I64LOW(cacheLine->iPos), cacheLine->iAddr, CacheLineLen(cacheLine)); |
|
227 |
|
228 |
|
229 aLockError = LockSegments(aPos, aSegmentCount, aMakeDirty); |
|
230 if (aLockError != KErrNone) |
|
231 return NULL; |
|
232 |
|
233 return cacheLine->iAddr + offset; |
|
234 } |
|
235 |
|
236 TUint8* CCacheClient::FindSegment(TInt64 aPos) |
|
237 { |
|
238 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
239 |
|
240 if (!cacheLine) |
|
241 return NULL; |
|
242 |
|
243 TInt offset = (TInt) (aPos - cacheLine->iPos); |
|
244 return cacheLine->iAddr + offset; |
|
245 } |
|
246 |
|
247 TUint8* CCacheClient::AllocateAndLockSegments(TInt64 aPos, TInt& aSegmentCount, TBool aMakeDirty, TBool aExtendExisting) |
|
248 { |
|
249 __ASSERT_DEBUG(aSegmentCount >= 0, Fault(ECacheClientInvalidSegmentCount)); |
|
250 |
|
251 TInt segmentSizeLog2 = iManager.SegmentSizeLog2(); |
|
252 TInt segmentSize = iManager.SegmentSize(); |
|
253 TInt cacheLineSize = iManager.CacheLineSize(); |
|
254 |
|
255 aPos = (aPos >> segmentSizeLog2) << segmentSizeLog2; |
|
256 |
|
257 |
|
258 // count number of uncached segments |
|
259 TInt maxSegments = Min(aSegmentCount, CacheLineSizeInSegments()); |
|
260 |
|
261 #if defined(_DEBUG) || defined(_DEBUG_RELEASE) |
|
262 // This is test code and changes the behaviour so that we always allocate |
|
263 // as large a cacheline as possible, so as to increase the likelihood of "holes" |
|
264 if (iManager.AllocateMaxSegments()) |
|
265 { |
|
266 maxSegments = cacheLineSize >> segmentSizeLog2; |
|
267 } |
|
268 #endif |
|
269 |
|
270 // make sure requested range doesn't overlap with an existing cacheline - |
|
271 // if it does, shrink requested length down |
|
272 TInt64 endPos = aPos + (maxSegments << segmentSizeLog2); |
|
273 TInt cacheLineCount = iCacheLines->Count(); |
|
274 for (TInt index=0; index < cacheLineCount; index++) |
|
275 { |
|
276 const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[index]; |
|
277 TInt64 startPos = cacheLine->iPos; |
|
278 if (cacheLine->iClient == this && startPos >= aPos && startPos < endPos) |
|
279 { |
|
280 endPos = cacheLine->iPos; |
|
281 } |
|
282 } |
|
283 TInt segmentCount = (TInt) ((endPos - aPos) >> segmentSizeLog2); |
|
284 |
|
285 __ASSERT_DEBUG(segmentCount > 0, Fault(ECacheClientSegmentAlreadyAllocated)); |
|
286 |
|
287 aSegmentCount = Min(segmentCount, aSegmentCount); |
|
288 |
|
289 iLastCacheLineIndex = -1; |
|
290 |
|
291 TUint8* segmentAddr = NULL; |
|
292 |
|
293 __CACHE_PRINT2(_L("CACHECLIENT: AllocateAndLockSegments(%ld, %d)"), aPos, segmentCount); |
|
294 |
|
295 // can we extend an existing cacheline ? (this saves wasting virtual memory) |
|
296 // NB if we're about the make the new segment(s) dirty, only extend if last segment in existing |
|
297 // cacheline is dirty, otherwise we'll end up unnecessarily marking lots of clean segments as dirty |
|
298 if (aExtendExisting) |
|
299 { |
|
300 TInt64 posLastSegment = aPos - segmentSize; |
|
301 const CCacheManager::TCacheLine* cacheLineOld; |
|
302 TInt r; |
|
303 if ((posLastSegment >= 0) && ((cacheLineOld = FindCacheLine(posLastSegment)) != NULL)) |
|
304 { |
|
305 TInt prevSegmentNumber = StartSegment(cacheLineOld, posLastSegment); |
|
306 TInt fillCountOld = iManager.FillCount(this, *cacheLineOld); |
|
307 TInt fillCountNew = segmentCount + prevSegmentNumber+1; // fillCountOld; |
|
308 if (prevSegmentNumber+1 == fillCountOld && |
|
309 fillCountNew <= (cacheLineSize >> segmentSizeLog2) && |
|
310 (!aMakeDirty || (prevSegmentNumber+1 == cacheLineOld->iDirtySegments)) |
|
311 ) |
|
312 { |
|
313 __CACHE_PRINT(_L("CACHE: extending cacheline")); |
|
314 |
|
315 segmentAddr = cacheLineOld->iAddr + (TInt) (aPos - cacheLineOld->iPos); |
|
316 |
|
317 // Lock the cacheline and then try to extend it |
|
318 r = LockSegments(cacheLineOld->iPos, cacheLineOld->iAllocatedSegments, ETrue); |
|
319 if (r == KErrNone) |
|
320 { |
|
321 r = iManager.ExtendCacheLine(this, *cacheLineOld, fillCountNew); |
|
322 //RDebug::Print(_L("GROW addr %08X, prevSegmentNumber %d, fillCount %d, fillCountNew %d, r %d\n"), |
|
323 // cacheLineOld->iAddr, prevSegmentNumber, fillCountOld, fillCountNew, r); |
|
324 if (r == KErrNone) |
|
325 { |
|
326 // If we've exceeded max, use LRU queue to discard old cachelines |
|
327 RemoveLru(cacheLineOld); |
|
328 return segmentAddr; |
|
329 } |
|
330 __CACHE_PRINT(_L("LockSegments FAIL")); |
|
331 UnlockSegments(posLastSegment); |
|
332 } |
|
333 } |
|
334 } |
|
335 } |
|
336 |
|
337 // reserve space in the segment array & LRU queue so we don't have to |
|
338 // worry about failing to append |
|
339 if (iCacheLines->Reserve(1) != KErrNone) |
|
340 return NULL; |
|
341 |
|
342 const CCacheManager::TCacheLine* cacheLineNew = NULL; |
|
343 TBool reUsedCacheLineGrown = EFalse; |
|
344 |
|
345 #ifdef SYMBIAN_REUSE_STALE_CACHELINES |
|
346 // try to re-use an old cacheline if possible to avoid having to commit a new one |
|
347 // as this involves calling RChunk::Commit() which takes some time.... |
|
348 TInt lenNewCacheLine = segmentCount << segmentSizeLog2; |
|
349 TInt totalBytesCached = CachedBytes() + lenNewCacheLine; |
|
350 |
|
351 cacheLineCount = iCacheLines->Count(); |
|
352 if (cacheLineCount > 0) |
|
353 { |
|
354 const CCacheManager::TCacheLine* cacheLineOld = (*iCacheLines)[cacheLineCount - 1]; |
|
355 TInt lenOldCacheLine = cacheLineOld->iFilledSegments << segmentSizeLog2; |
|
356 reUsedCacheLineGrown = segmentCount > cacheLineOld->iAllocatedSegments; |
|
357 if (totalBytesCached + lenNewCacheLine - lenOldCacheLine > iMaxBytesCached && |
|
358 cacheLineOld->iClient == this && |
|
359 cacheLineOld->iLockCount == 0) |
|
360 { |
|
361 if (iManager.ReAllocateAndLockCacheLine(this, aPos, *cacheLineOld, segmentCount) == KErrNone) |
|
362 { |
|
363 cacheLineNew = cacheLineOld; |
|
364 } |
|
365 } |
|
366 } |
|
367 #endif // SYMBIAN_REUSE_STALE_CACHELINES |
|
368 TBool reUsingCacheline = cacheLineNew ? (TBool)ETrue:(TBool)EFalse; |
|
369 |
|
370 // Request a free segment from cache manager |
|
371 if (reUsingCacheline) |
|
372 { |
|
373 iCacheLines->Remove(cacheLineCount-1); |
|
374 } |
|
375 else |
|
376 { |
|
377 TInt r = iManager.AllocateAndLockCacheLine(this, aPos, cacheLineNew, segmentCount); |
|
378 if (r != KErrNone) |
|
379 { |
|
380 __CACHE_PRINT1(_L("CACHECLIENT: AllocateSegment() failed r %d"), r); |
|
381 return NULL; |
|
382 } |
|
383 // check the address isn't already in the iCacheLines - |
|
384 // this can happen if it was stolen from us and we then steal it back |
|
385 TInt indexDup = iCacheLines->Find(cacheLineNew); |
|
386 if (indexDup != KErrNotFound) |
|
387 iCacheLines->Remove(indexDup); |
|
388 } |
|
389 |
|
390 |
|
391 segmentAddr = cacheLineNew->iAddr; |
|
392 |
|
393 // Add to top of LRU queue |
|
394 TInt r = iCacheLines->Insert(cacheLineNew, 0); |
|
395 __ASSERT_ALWAYS(r == KErrNone, Fault(ECacheClientInsertFailed)); |
|
396 |
|
397 __CACHE_PRINT4(_L("CACHECLIENT: AllocateAndLockSegments(), Client %08X pos %d addr %08X len %d"), |
|
398 this, I64LOW(cacheLineNew->iPos), cacheLineNew->iAddr, CacheLineLen(cacheLineNew)); |
|
399 |
|
400 // assume LRU in use ... |
|
401 iLastCacheLineIndex = 0; |
|
402 |
|
403 if (!reUsingCacheline || reUsedCacheLineGrown) |
|
404 RemoveLru(cacheLineNew); |
|
405 |
|
406 return segmentAddr; |
|
407 } |
|
408 |
|
409 |
|
410 |
|
411 TInt CCacheClient::StartSegment(const CCacheManager::TCacheLine* aCacheLine, TInt64 aPos) |
|
412 { |
|
413 TInt offset = (TInt) (aPos - aCacheLine->iPos); |
|
414 return offset >> iManager.SegmentSizeLog2(); |
|
415 } |
|
416 |
|
417 TInt CCacheClient::CacheLineLen(const CCacheManager::TCacheLine* aCacheLine) |
|
418 { |
|
419 return aCacheLine->iAllocatedSegments << iManager.SegmentSizeLog2(); |
|
420 } |
|
421 |
|
422 |
|
423 void CCacheClient::MarkSegmentsAsFilled(TInt64 aPos, TInt aSegmentCount) |
|
424 { |
|
425 __CACHE_PRINT2(_L("CACHECLIENT: Mark Filled Segments(%ld, %d)"), aPos, aSegmentCount); |
|
426 |
|
427 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
428 |
|
429 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound0)); |
|
430 |
|
431 iManager.SetFilled(this, *cacheLine, StartSegment(cacheLine, aPos) + aSegmentCount); |
|
432 } |
|
433 |
|
434 void CCacheClient::MarkSegmentsAsDirty(TInt64 aPos, TInt aSegmentCount) |
|
435 { |
|
436 __CACHE_PRINT2(_L("CACHECLIENT: Mark Dirty Segments(%ld, %d)"), aPos, aSegmentCount); |
|
437 |
|
438 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
439 |
|
440 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound1)); |
|
441 |
|
442 iManager.SetDirty(this, *cacheLine, StartSegment(cacheLine, aPos) + aSegmentCount); |
|
443 } |
|
444 |
|
445 TInt CCacheClient::FindFirstDirtySegment(TInt64& aPos, TUint8*& aAddr) |
|
446 { |
|
447 TInt cacheLineCount = iCacheLines->Count(); |
|
448 |
|
449 aPos = KMaxTInt64; |
|
450 TInt segmentCount = 0; |
|
451 for (TInt index = 0; index< cacheLineCount; index++) |
|
452 { |
|
453 CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[index]; |
|
454 |
|
455 TInt dirtyCount = iManager.DirtyCount(this, cacheLine); |
|
456 if ((dirtyCount > 0 && cacheLine.iPos < aPos)) |
|
457 { |
|
458 aPos = cacheLine.iPos; |
|
459 aAddr = cacheLine.iAddr; |
|
460 segmentCount = dirtyCount; |
|
461 } |
|
462 } |
|
463 |
|
464 return segmentCount; |
|
465 } |
|
466 |
|
467 |
|
468 void CCacheClient::MarkSegmentsAsClean(TInt64 aPos) |
|
469 { |
|
470 __CACHE_PRINT1(_L("CACHECLIENT: MarkSegmentsAsClean(%d)"), I64LOW(aPos)); |
|
471 |
|
472 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
473 |
|
474 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound2)); |
|
475 |
|
476 // this sets the dirty count to zero, unlocks the cacheline and |
|
477 // marks all the dirty pages as filled |
|
478 iManager.ClearDirty(this, *cacheLine); |
|
479 |
|
480 RemoveLru(cacheLine); |
|
481 } |
|
482 |
|
483 |
|
484 void CCacheClient::RemoveEmptySegments(const CCacheManager::TCacheLine* aCacheLine) |
|
485 { |
|
486 iManager.RemoveEmptySegments(this, *aCacheLine); |
|
487 } |
|
488 |
|
489 TInt CCacheClient::LockSegments(TInt64 aPos, TInt aSegmentCount, TBool aMakeDirty) |
|
490 { |
|
491 __CACHE_PRINT1(_L("CACHECLIENT: LockCacheLine(%d, %d)"), I64LOW(aPos)); |
|
492 |
|
493 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
494 |
|
495 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound3)); |
|
496 |
|
497 |
|
498 TInt startSegmentNumber = StartSegment(cacheLine, aPos); |
|
499 |
|
500 TInt lockError; |
|
501 if (aMakeDirty) |
|
502 lockError = iManager.LockCacheLine(this, *cacheLine, 0, cacheLine->iAllocatedSegments); |
|
503 else |
|
504 lockError = iManager.LockCacheLine(this, *cacheLine, startSegmentNumber, aSegmentCount); |
|
505 |
|
506 if (lockError == KErrInUse) |
|
507 { |
|
508 return lockError; |
|
509 } |
|
510 else if (lockError != KErrNone) |
|
511 { |
|
512 RemoveCacheLine(cacheLine); |
|
513 return lockError; |
|
514 } |
|
515 |
|
516 // Check for "holes".i.e. empty segments before this one. |
|
517 // If found, discard the empty segments and return KErrNotFound |
|
518 TInt filledCount = iManager.FillCount(this, *cacheLine); |
|
519 if (startSegmentNumber > filledCount) |
|
520 { |
|
521 __CACHE_PRINT(_L("CACHE: found hole")); |
|
522 #if defined(_DEBUG) || defined(_DEBUG_RELEASE) |
|
523 iManager.Stats().iHoleCount++; |
|
524 #endif |
|
525 |
|
526 |
|
527 RemoveEmptySegments(cacheLine); |
|
528 iManager.UnlockCacheLine(this, *cacheLine); |
|
529 if (CacheLineLen(cacheLine) == 0) |
|
530 FreeCacheLine(cacheLine); |
|
531 return KErrNotFound; |
|
532 } |
|
533 |
|
534 // Move this entry to top of LRU queue |
|
535 iCacheLines->Remove(iLastCacheLineIndex); |
|
536 #ifdef _DEBUG |
|
537 TInt r = |
|
538 #endif |
|
539 iCacheLines->Insert(cacheLine, 0); |
|
540 __ASSERT_DEBUG(r == KErrNone, Fault(ECacheClientLruInsertFailed)); |
|
541 iLastCacheLineIndex = 0; |
|
542 |
|
543 |
|
544 return KErrNone; |
|
545 } |
|
546 |
|
547 |
|
548 /** |
|
549 UnlockSegments() |
|
550 */ |
|
551 void CCacheClient::UnlockSegments(TInt64 aPos) |
|
552 { |
|
553 __CACHE_PRINT1(_L("CACHECLIENT: UnlockSegments(%d, %d)"), I64LOW(aPos)); |
|
554 |
|
555 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
556 |
|
557 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound4)); |
|
558 |
|
559 iManager.UnlockCacheLine(this, *cacheLine); |
|
560 } |
|
561 |
|
562 |
|
563 TInt CCacheClient::FindCacheLineIndex(TInt64 aPos) |
|
564 { |
|
565 TInt cacheLineCount = iCacheLines->Count(); |
|
566 |
|
567 if (iLastCacheLineIndex != -1 && iLastCacheLineIndex < cacheLineCount) |
|
568 { |
|
569 const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[iLastCacheLineIndex]; |
|
570 |
|
571 if (cacheLine->iClient == this && |
|
572 aPos >= cacheLine->iPos && |
|
573 aPos < cacheLine->iPos+CacheLineLen(cacheLine)) |
|
574 return iLastCacheLineIndex; |
|
575 } |
|
576 |
|
577 for (TInt index=0; index < cacheLineCount; index++) |
|
578 { |
|
579 const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[index]; |
|
580 |
|
581 if (cacheLine->iClient == this && |
|
582 aPos >= cacheLine->iPos && |
|
583 (aPos < cacheLine->iPos+CacheLineLen(cacheLine))) |
|
584 { |
|
585 iLastCacheLineIndex = index; |
|
586 return index; |
|
587 } |
|
588 } |
|
589 |
|
590 return KErrNotFound; |
|
591 } |
|
592 |
|
593 const CCacheManager::TCacheLine* CCacheClient::FindCacheLine(TInt64 aPos) |
|
594 { |
|
595 TInt index = FindCacheLineIndex(aPos); |
|
596 |
|
597 return (index == KErrNotFound) ? NULL : (*iCacheLines)[index]; |
|
598 } |
|
599 |
|
600 |
|
601 TBool CCacheClient::SegmentEmpty(TInt64 aPos) |
|
602 { |
|
603 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
604 |
|
605 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound5)); |
|
606 |
|
607 TInt startSegment = StartSegment(cacheLine, aPos); |
|
608 TInt fillCount = iManager.FillCount(this, *cacheLine); |
|
609 |
|
610 |
|
611 return startSegment >= fillCount ? (TBool)ETrue : (TBool)EFalse; |
|
612 } |
|
613 |
|
614 |
|
615 TBool CCacheClient::SegmentDirty(TInt64 aPos) |
|
616 { |
|
617 const CCacheManager::TCacheLine* cacheLine = FindCacheLine(aPos); |
|
618 |
|
619 __ASSERT_ALWAYS(cacheLine != NULL, Fault(ECacheClientSegmentNotFound6)); |
|
620 |
|
621 TInt startSegment = StartSegment(cacheLine, aPos); |
|
622 TInt dirtyCount = iManager.DirtyCount(this, *cacheLine); |
|
623 |
|
624 return startSegment >= dirtyCount ? (TBool)ETrue : (TBool)EFalse; |
|
625 } |
|
626 |
|
627 // return whether there are too many locked pages globally (for all files) |
|
628 // or too many for this file |
|
629 TBool CCacheClient::TooManyLockedSegments() |
|
630 { |
|
631 if (iManager.TooManyLockedSegments()) |
|
632 return ETrue; |
|
633 if (LockedBytes() > iMaxBytesCached) |
|
634 return ETrue; |
|
635 return EFalse; |
|
636 } |
|
637 |
|
638 |
|
639 TInt CCacheClient::CachedBytes() |
|
640 { |
|
641 TInt cacheLineCount = iCacheLines->Count(); |
|
642 |
|
643 TInt totalBytesCached = 0; |
|
644 for (TInt n=0; n < cacheLineCount; n++) |
|
645 totalBytesCached+= ((*iCacheLines)[n]->iFilledSegments << iManager.SegmentSizeLog2()); |
|
646 return totalBytesCached; |
|
647 } |
|
648 |
|
649 TInt CCacheClient::LockedBytes() |
|
650 { |
|
651 TInt cacheLineCount = iCacheLines->Count(); |
|
652 TInt segmentSizeLog2 = iManager.SegmentSizeLog2(); |
|
653 |
|
654 TInt totalBytesLocked = 0; |
|
655 for (TInt n=0; n < cacheLineCount; n++) |
|
656 { |
|
657 CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[n]; |
|
658 if (cacheLine.iLockCount > 0) |
|
659 totalBytesLocked+= cacheLine.iAllocatedSegments << segmentSizeLog2; |
|
660 } |
|
661 return totalBytesLocked; |
|
662 } |
|
663 |
|
664 void CCacheClient::RemoveLru(const CCacheManager::TCacheLine* aCacheLineToExclude) |
|
665 { |
|
666 if (iMaxBytesCached == 0) // LRU queue ? |
|
667 return; |
|
668 |
|
669 // If we've exceeded max, use LRU queue to remove stuff |
|
670 |
|
671 TInt cacheLineCount = iCacheLines->Count(); |
|
672 TInt segmentSizeLog2 = iManager.SegmentSizeLog2(); |
|
673 |
|
674 // calculate the total bytes cached in all cachelines we own |
|
675 // NB it's difficult to keep a track of this in this class because cachelines can be stolen (!) |
|
676 TInt totalBytesCached = CachedBytes(); |
|
677 for (TInt lastIndex = cacheLineCount - 1; |
|
678 lastIndex >= 0 && totalBytesCached > iMaxBytesCached; |
|
679 lastIndex--) |
|
680 { |
|
681 const CCacheManager::TCacheLine* cacheLine = (*iCacheLines)[lastIndex]; |
|
682 |
|
683 // __CACHE_PRINT3(_L("Test cacheline index %d pos %ld len %d"), lastIndex, cacheLine.iPos, cacheLine.iLen); |
|
684 |
|
685 if (cacheLine != aCacheLineToExclude) |
|
686 { |
|
687 // if removing a cacheline will bring the total to UNDER the "maximum" |
|
688 // then don't remove it and abort - to do otherwise could remove |
|
689 // data that has just been read-ahead .... |
|
690 TInt lenCacheLine = cacheLine->iFilledSegments << segmentSizeLog2; |
|
691 if (totalBytesCached - lenCacheLine <= iMaxBytesCached) |
|
692 { |
|
693 __CACHE_PRINT(_L("CACHECLIENT: cacheline too big to remove, aborting..")); |
|
694 break; |
|
695 } |
|
696 |
|
697 __CACHE_PRINT3(_L("CACHECLIENT: Removing LRU index %d pos %ld len %d"), lastIndex, cacheLine->iPos, CacheLineLen(cacheLine)); |
|
698 TInt bytesFreed = cacheLine->iFilledSegments << iManager.SegmentSizeLog2(); |
|
699 if (FreeCacheLine(cacheLine)) |
|
700 { |
|
701 totalBytesCached-= bytesFreed; |
|
702 __ASSERT_ALWAYS(totalBytesCached >= 0, Fault(ECacheClientTotalByteCountInvalid)); |
|
703 } |
|
704 } |
|
705 } |
|
706 } |
|
707 |
|
708 |
|
709 |
|
710 TBool CCacheClient::FreeCacheLine(const CCacheManager::TCacheLine* aCacheLine) |
|
711 { |
|
712 __CACHE_PRINT1(_L("CACHECLIENT: FreeCacheLine(%d) )"), I64LOW(aCacheLine->iPos)); |
|
713 |
|
714 |
|
715 TBool success = iManager.FreeCacheLine(this, *aCacheLine); |
|
716 |
|
717 if (success) |
|
718 RemoveCacheLine(aCacheLine); |
|
719 |
|
720 return success; |
|
721 } |
|
722 |
|
723 void CCacheClient::RemoveCacheLine(const CCacheManager::TCacheLine* aCacheLine) |
|
724 { |
|
725 __CACHE_PRINT1(_L("CACHECLIENT: RemoveCacheLine(%d) )"), I64LOW(aCacheLine->iPos)); |
|
726 |
|
727 TInt index = iCacheLines->Find(aCacheLine); |
|
728 |
|
729 __ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheClientSegmentNotFound7)); |
|
730 |
|
731 iCacheLines->Remove(index); |
|
732 } |
|
733 |
|
734 #if defined(_DEBUG) || defined(_DEBUG_RELEASE) |
|
735 void CCacheClient::DumpCache() |
|
736 { |
|
737 RDebug::Print(_L("**** CACHECLIENT: CacheLines ****\n")); |
|
738 TInt cacheLineCount = iCacheLines->Count(); |
|
739 |
|
740 for (TInt index = 0; index< cacheLineCount; index++) |
|
741 { |
|
742 CCacheManager::TCacheLine& cacheLine = *(*iCacheLines)[index]; |
|
743 iManager.DumpCacheLine(cacheLine); |
|
744 } |
|
745 } |
|
746 #endif |