First public contribution.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // f32\sfile\sf_file_cache.cpp
24 #include <e32std_private.h>
30 #include "sf_file_cache.h"
31 #include "sf_cache_man.h"
32 #include "sf_cache_client.h"
33 #include "sf_file_cache_defs.h"
36 // disables flushing of stale cachelines before each write
41 ECacheSettingsInitFailed,
42 ECacheSettingsNotFound,
43 ECacheSettingGetFailed,
45 ECacheBadOperationIndex,
49 ECompletingWriteWithDataRemaining,
59 EFileNameAlreadyOwned,
61 EReOpeningUnNamedFile,
62 EStartingDirtyAWrongStage,
63 EUnexpectedReNewLFailure,
65 EFlushingWithSessionNull,
69 LOCAL_C void Fault(TFileCacheFault aFault)
71 // Report a fault in the file cache
74 User::Panic(_L("FSFILECACHE"), aFault);
77 const TInt KMinSequentialReadsBeforeReadAhead = 3;
79 //************************************
81 //************************************
83 inline TBool ReadCachingEnabled(CFileShare& aShare)
85 TUint mode = aShare.iMode;
87 TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
89 if (((mode & EFileReadBuffered) && (fileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))) ||
90 (((mode & EFileReadDirectIO) == 0) && (fileCacheFlags & EFileCacheReadOn)))
100 inline TBool WriteCachingEnabled(CFileShare& aShare)
102 TUint mode = aShare.iMode;
104 TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
106 if ((mode & EFileWrite) == 0)
110 else if (((mode & EFileWriteBuffered) && (fileCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))) ||
111 (((mode & EFileWriteDirectIO) == 0) && (fileCacheFlags & EFileCacheWriteOn)))
121 void CFileCache::SetFileCacheFlags(CFileShare& aShare)
123 TInt fileCacheFlags = TFileCacheSettings::Flags(iDriveNum);
125 TUint& mode = aShare.iMode;
127 // enable/disable read ahead
128 if (((mode & EFileReadAheadOn) && (fileCacheFlags & (EFileCacheReadAheadEnabled | EFileCacheReadAheadOn))) ||
129 (((mode & EFileReadAheadOff) == 0) && (fileCacheFlags & EFileCacheReadAheadOn)))
131 __CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead ENABLED"));
132 mode|= EFileReadAheadOn;
136 __CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead disabled"));
137 mode&= ~EFileReadAheadOn;
140 // enable/disable read caching
141 if (ReadCachingEnabled(aShare))
143 __CACHE_PRINT(_L("CACHEFILE: EFileCacheRead ENABLED"));
144 mode|= EFileReadBuffered;
148 __CACHE_PRINT(_L("CACHEFILE: EFileCacheRead disabled"));
149 // if read caching is off, turn off read-ahead too
150 mode&= ~(EFileReadBuffered | EFileReadAheadOn);
153 // enable/disable write caching
154 if (WriteCachingEnabled(aShare))
156 __CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite ENABLED"));
157 mode|= EFileWriteBuffered;
161 __CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite disabled"));
162 mode&= ~EFileWriteBuffered;
167 void CFileCache::ConstructL(CFileShare& aShare)
169 iDrive = &aShare.File().Drive();
170 iDriveNum = iDrive->DriveNumber();
171 iMount=&iDrive->CurrentMount();
176 CCacheManager* manager = CCacheManagerFactory::CacheManager();
178 iCacheClient = manager->CreateClientL();
179 manager->RegisterClient(*iCacheClient);
182 TInt segmentSize = SegmentSize();
183 TInt segmentSizeMask = I64LOW(SegmentSizeMask());
184 // Get file cache size
185 iCacheSize = TFileCacheSettings::CacheSize(iDriveNum);
186 // must be at least one segment
187 iCacheSize = Max(iCacheSize, segmentSize);
188 // round up to nearest whole segment
189 iCacheSize = (iCacheSize + segmentSize - 1) & segmentSizeMask;
191 // Get max read-ahead length
192 iMaxReadAheadLen = TFileCacheSettings::MaxReadAheadLen(iDriveNum);
193 // must be at least one segment
194 iMaxReadAheadLen = Max(iMaxReadAheadLen, segmentSize);
195 // round up to nearest whole segment
196 iMaxReadAheadLen = (iMaxReadAheadLen + segmentSize - 1) & segmentSizeMask;
197 // read-ahead should not be greater than the cache size minus one segment
198 iMaxReadAheadLen = Min(iMaxReadAheadLen, iCacheSize - segmentSize);
199 // ... or greater than one cacheline (128K should be enough !)
200 iMaxReadAheadLen = Min(iMaxReadAheadLen, iCacheClient->CacheLineSize());
202 iFileCacheReadAsync = TFileCacheSettings::FileCacheReadAsync(iDriveNum);
204 iClosedFileKeepAliveTime = TFileCacheSettings::ClosedFileKeepAliveTime(iDriveNum);
205 iDirtyDataFlushTime = TFileCacheSettings::DirtyDataFlushTime(iDriveNum);
207 // Calculate max number of segments to cache
208 TInt maxSegmentsToCache = iCacheSize >> SegmentSizeLog2();
210 __CACHE_PRINT1(_L("CACHEFILE: maxSegmentsToCache %d"), maxSegmentsToCache);
212 iCacheClient->SetMaxSegments(maxSegmentsToCache);
214 CFileCache* fileCache = ReNewL(aShare);
215 __ASSERT_ALWAYS(fileCache != NULL, Fault(EUnexpectedReNewLFailure));
218 CFileCache* CFileCache::NewL(CFileShare& aShare)
220 __CACHE_PRINT(_L("CACHEFILE: CFileCache::NewL()"));
222 CFileCB* file = &aShare.File();
223 if ((CCacheManagerFactory::CacheManager() == NULL) ||
224 (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare)) ||
225 (FsThreadManager::IsDriveSync(file->Drive().DriveNumber(),EFalse)))
228 CFileCache* fileCache = new(ELeave) CFileCache();
229 CleanupClosePushL(*fileCache);
230 fileCache->ConstructL(aShare);
231 CleanupStack::Pop(1, fileCache);
235 CFileCache* CFileCache::ReNewL(CFileShare& aShare)
237 __CACHE_PRINT(_L("CACHEFILE: CFileCache::ReNewL()"));
239 // check not already open i.e. attached to a CFileCB
240 __ASSERT_DEBUG(iFileCB == NULL, Fault(EReNewingOpenCache));
242 // make sure the drive thread exists (the mount may have been mounted
243 // synchronously since the drive was last open)
244 const TInt r = FsThreadManager::GetDriveThread(iDriveNum, &iDriveThread);
245 if ((r!= KErrNone) || !iDriveThread)
248 // if re-opening in DirectIo mode, destroy the file cache
249 if (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare))
255 SetFileCacheFlags(aShare);
257 // assign ownership of this object to aFileCB before any leaves occur
258 iFileCB = &aShare.File();
259 iFileCB->iBody->iFileCache = this;
261 __ASSERT_DEBUG(iLock.Handle() == KNullHandle, Fault(ELockAlreadyOpen));
262 User::LeaveIfError(iLock.CreateLocal());
264 // delete the file name to save heap space (it's only needed
265 // while the file is on the closed queue so that it can be matched)
272 void CFileCache::Init(CFileShare& aShare)
274 SetFileCacheFlags(aShare);
277 CFileCache::CFileCache() :
278 iClosedTimer(ClosedTimerEvent, this),
279 iDirtyTimer(DirtyTimerEvent, this)
283 CFileCache::~CFileCache()
287 // stop the owning CFileCB from pointing to an about-to-be deleted object
288 if (iFileCB && iFileCB->iBody)
289 iFileCB->iBody->iFileCache = NULL;
291 CCacheManagerFactory::CacheManager()->DeregisterClient(*iCacheClient);
299 TInt CFileCache::ClosedTimerEvent(TAny* aFileCache)
301 TClosedFileUtils::Remove((CFileCache*) aFileCache);
305 TInt CFileCache::DirtyTimerEvent(TAny* aFileCache)
307 // Cannot report errors here
308 // coverity [unchecked_value]
309 (void)((CFileCache*) aFileCache)->FlushDirty();
316 void CFileCache::Close()
318 __CACHE_PRINT1(_L("CFileCache::Close() 0x%x"),this);
323 if (iCacheClient) // NB Object may not have been fully constructed
327 __ASSERT_DEBUG(((iCacheClient->FindFirstDirtySegment(pos, addr)) == 0), Fault(EClosingDirtyFile));
328 __ASSERT_DEBUG(!iDirtyDataOwner, Fault(EClosingDirtyFile));
333 __CACHE_PRINT2(_L("CFileCache::Close() iFileCB %08X IsClosed %d"),
334 iFileCB, TClosedFileUtils::IsClosed(this));
335 // if not already closed move to closed file queue
336 if (iFileCB != NULL &&
337 !iFileCB->DeleteOnClose() &&
338 !TClosedFileUtils::IsClosed(this) &&
341 // add to ClosedFiles container
342 __CACHE_PRINT1(_L("CLOSEDFILES: Adding %S\n"), &iFileCB->FileNameF() );
343 TRAP(r, TClosedFileUtils::AddL(this, ETrue));
345 // Acquire ownership of the CFileCB's file name
346 __ASSERT_DEBUG(iFileCB->iFileNameF, Fault(EClosingUnNamedFile));
347 __ASSERT_DEBUG(iFileNameF == NULL, Fault(EFileNameAlreadyOwned));
348 iFileNameF = iFileCB->iFileNameF;
349 iNameHash = iFileCB->iNameHash;
350 iFileCB->iFileNameF = NULL;
352 // remove pointer to owning CFileCB as this is called from CFileCB's destructor
355 // Successfully moved file !
358 // close the RFastLock object here to prevent OOM kernel tests from failing
366 // if already closed, close for good. N.B. CFsObject::DoClose() will
367 // cause it to be removed from the ClosedFiles container
368 CFsDispatchObject::Close();
372 CMountCB& CFileCache::Mount() const
377 CFileCB* CFileCache::FileCB()
384 TInt CFileCache::SegmentSize() const
386 return iCacheClient->SegmentSize();
389 TInt CFileCache::SegmentSizeLog2() const
391 return iCacheClient->SegmentSizeLog2();
394 TInt64 CFileCache::SegmentSizeMask() const
396 return iCacheClient->SegmentSizeMask();
400 Sets the cached file size
402 @param aSize The size of the file.
404 void CFileCache::SetSize64(TInt64 aSize)
406 __e32_atomic_store_ord64(&iSize64, aSize);
409 TDrive& CFileCache::Drive() const
414 TUint32 CFileCache::NameHash() const
419 HBufC& CFileCache::FileNameF() const
421 __ASSERT_DEBUG(iFileNameF, Fault(EClosedFileHasNoName));
428 TBool CFileCache::IsDriveThread()
430 return FsThreadManager::IsDriveThread(iDriveNum, EFalse);
434 void CFileCache::ResetReadAhead()
436 // iSequentialReads = 0;
443 dispatches a new message to fill the read cache if
444 (ReadAheadPos - ShareReadPos) <= (ReadAheadLen)
446 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
448 |----- ReadAheadLen ----------------->
450 ShareReadPos ReadAheadPos
452 Every successful read-ahead doubles ReadAheadLen until it reaches the maximum
453 (KDefaultFileCacheMaxReadAheadLen).
455 void CFileCache::ReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
457 if (!(aMode & EFileReadAheadOn) ||
458 (iSequentialReads < KMinSequentialReadsBeforeReadAhead) ||
459 iMaxReadAheadLen == 0)
463 TInt segmentSize = SegmentSize();
464 TInt64 segmentSizeMask = SegmentSizeMask();
466 // if the read-ahead pos has been reset to zero, then the read-ahead
467 // position and length must be re-calculated
469 TBool resetting = (iReadAheadPos == 0)?(TBool)ETrue:(TBool)EFalse;
472 iReadAheadPos = (iLastReadPos + segmentSize - 1) & segmentSizeMask;
474 // ensure read ahead len at least as big as last read
475 iReadAheadLen = Max(iReadAheadLen, iLastReadLen);
477 // round up to a segment size
478 iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
480 // ensure read ahead len at least as big as 1 segments
481 iReadAheadLen = Max(iReadAheadLen, segmentSize);
483 // ensure read ahead len not greater than the maximum read ahead len
484 iReadAheadLen = Min(iReadAheadLen, iMaxReadAheadLen);
486 iReadAheadRequest = NULL;
489 TInt bytesBuffered = (TInt) (iReadAheadPos - iLastReadPos);
490 TInt bytesNotBuffered = iMaxReadAheadLen - bytesBuffered;
493 // if read-ahead buffer len > current read-ahead len OR
494 // read-ahead buffer is more than half full, do nothing
495 if ((iReadAheadRequest) ||
496 (bytesBuffered > iReadAheadLen) ||
497 (bytesBuffered > (iMaxReadAheadLen>>1)) ||
498 (iReadAheadPos >= iSize64))
503 // double the read-ahead length - unless this is the first
507 // ensure read ahead len not greater than the free space available in buffer
508 iReadAheadLen = Min(iReadAheadLen, bytesNotBuffered);
510 // round up to a segment size
511 iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
513 #if defined (_DEBUG_READ_AHEAD)
514 TInt64 oldReadAheadPos = iReadAheadPos;
517 DoReadAhead(aMsgRequest, aMode);
520 // RDebug::Print(_L("Buffered: old %d new %d"), bytesBuffered, (iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos);
522 #if defined (_DEBUG_READ_AHEAD)
523 RDebug::Print(_L("Buffered: old %d new %d iLastReadPos %d ReadAheadPos old %d new %d iReadAheadLen %d"),
525 (TInt) ((iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos),
526 I64LOW(iLastReadPos),
527 I64LOW(oldReadAheadPos),
528 I64LOW(iReadAheadPos),
530 #endif // (_DEBUG_READ_AHEAD)
537 void CFileCache::DoReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
541 if (iCacheClient->FindSegment(iReadAheadPos) != NULL)
543 // if read ahead pos is already cached, then synchronous reads have caught up,
544 // so reset read ahead pos.
545 #if defined (_DEBUG_READ_AHEAD)
546 RDebug::Print(_L("ReadAhead: pos %d already cached"), I64LOW(iReadAheadPos));
553 CFsClientMessageRequest* newRequest = NULL;
554 TInt r = AllocateRequest(newRequest, EFalse, aMsgRequest.Session());
558 r = newRequest->PushOperation(
571 __CACHE_PRINT2(_L("TFsFileRead: ReadAhead pos %ld len %d"), newRequest->CurrentOperation().iReadWriteArgs.iPos, newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
573 // RDebug::Print(_L("ReadH:\tpos %d\tlen %d"), I64LOW(newRequest->CurrentOperation().iReadWriteArgs.iPos), newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
575 r = ReadBuffered(*newRequest, aMode);
576 if (r != CFsRequest::EReqActionContinue)
578 // if read ahead pos is already cached, then synchronous reads have caught up,
579 // so reset read ahead pos.
580 if (r == CFsRequest::EReqActionComplete)
582 #if defined (_DEBUG_READ_AHEAD)
583 RDebug::Print(_L("ReadAhead pos %d ALREADY DONE !!!"), I64LOW(iReadAheadPos));
588 newRequest->PopOperation();
594 iReadAheadPos = iReadAheadPos + iReadAheadLen;
595 iReadAheadRequest = newRequest;
597 #if defined (_DEBUG_READ_AHEAD)
598 RDebug::Print(_L("Dispatching ReadAhead with %s priority"), iFileCacheReadAsync?_S16("HIGH"):_S16("LOW"));
600 // If if media driver reads are synchronous (i.e. not interrupt driven) dispatch read-ahead
601 // with low priority so that it doesn't prevent client thread from running
602 newRequest->Dispatch(
603 EFalse, // don't init
604 iFileCacheReadAsync?(TBool)EFalse:(TBool)ETrue,
605 EFalse); // dispatch to back
608 TInt CFileCache::CompleteRead(CFsRequest* aRequest)
610 CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
612 __ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
616 GetFileFromScratch(aRequest, share, file);
617 CFileCache* fileCache = file->FileCache();
619 __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
621 TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
624 TUint mode = share?share->iMode : EFileReadBuffered;
625 TInt r = fileCache->ReadBuffered(msgRequest, mode);
627 // if this request has been cancelled we mustn't dispatch it again -
628 // we still need to call state machine however so that any locked segments can be unlocked
631 TInt lastError = msgRequest.LastError();
634 __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
637 if (lastError == KErrCancel)
638 r = CFsRequest::EReqActionComplete;
644 ReadBuffered - attempts to read from cache.
645 Called from TFsFileRead::Initialise() and CFileCache::CompleteRead()
647 @return CFsRequest::EReqActionComplete if request entirely satisfied
648 CFsRequest::EReqActionContinue if request not yet complete.
649 Results in transition from :
650 TFsFileRead::PostInitialise() to TFsFileRead::DoRequestL() or
651 CFileCache::CompleteRead() to TFsFileRead::DoRequestL()
652 CFsRequest::EReqActionBusy if filecache is "busy"
654 TInt CFileCache::ReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
658 CFsClientMessageRequest* newRequest = NULL;
660 __CACHE_PRINT2(_L("CFileCache::ReadBuffered() pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
661 TInt r = DoReadBuffered(aMsgRequest, aMode, newRequest);
666 newRequest->Dispatch();
672 void CFileCache::UpdateSharePosition(CFsMessageRequest& aMsgRequest, TMsgOperation& aCurrentOperation)
674 // update the file share's position if this request came from client
675 if (aCurrentOperation.iClientRequest)
677 CFileShare* share = (CFileShare*)aMsgRequest.ScratchValue();
678 if (aMsgRequest.LastError() == KErrNone)
680 __e32_atomic_store_ord64(&share->iPos, aCurrentOperation.iReadWriteArgs.iPos);
685 TInt CFileCache::DoReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode, CFsClientMessageRequest*& aNewRequest)
691 EStReadFromDiskComplete,
698 TInt retCode = CFsRequest::EReqActionComplete;
699 TInt& lastError = aMsgRequest.LastError();
700 TBool driveThread = IsDriveThread();
702 // temp storage for transition between EStReadFromCache / EStReadFromDiskComplete and EStCopyToClient
705 TInt64 segmentStartPos = 0;
707 TInt segmentSize = SegmentSize();
708 TInt segmentSizeLog2 = SegmentSizeLog2();
709 TInt64 segmentSizeMask = SegmentSizeMask();
713 TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
714 TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
715 TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
716 TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
717 TBool readAhead = (currentOperation->iReadWriteArgs.iData == NULL)?(TBool)ETrue:(TBool)EFalse;
719 switch(currentOperation->iState)
724 // if EFileReadDirectIO, flush write cache and read direct from disk
725 if (!(aMode & EFileReadBuffered))
728 TInt r = FlushDirty(&aMsgRequest);
730 if (r == CFsRequest::EReqActionBusy)
731 return CFsRequest::EReqActionBusy;
732 return CFsRequest::EReqActionContinue; // read uncached
735 if (currentPos > iSize64)
736 currentPos = iSize64;
738 currentOperation->iState = EStReadFromCache;
740 // count the number of sequential reads for read-ahead
741 if (currentOperation->iClientRequest)
743 if (currentPos == iLastReadPos)
749 iSequentialReads = 0;
752 iLastReadPos = currentPos + totalLen;
753 iLastReadLen = totalLen;
759 case EStReadFromCache:
761 // reading past end of file ?
762 if (currentPos + totalLen > iSize64)
763 totalLen = (TInt) (iSize64 - currentPos);
766 if (totalLen == 0 || lastError != KErrNone)
768 currentOperation->iState = EStEnd;
772 segmentStartPos = currentPos & segmentSizeMask;
775 TInt64 endPos = currentPos + totalLen;
776 TUint maxLenToRead = (TUint)Min((TInt64)(iCacheClient->CacheLineSize()), (endPos - segmentStartPos));
777 TInt maxSegments = (maxLenToRead + segmentSize - 1) >> segmentSizeLog2;
780 TInt segmentCount = maxSegments;
781 TInt filledSegmentCount;
783 addr = iCacheClient->FindAndLockSegments(segmentStartPos, segmentCount, filledSegmentCount, lockError, EFalse);
786 #if defined (_DEBUG_READ_AHEAD)
787 if (addr && readAhead)
788 RDebug::Print(_L("READAHEAD CACHELINE ALREADY EXISTS POS %d"), I64LOW(segmentStartPos));
791 // if cacheline contains filled and empty segments, deal with these seperately
792 // to simplify the code.....
793 if (filledSegmentCount > 0 && segmentCount > filledSegmentCount)
795 segmentCount = Min(segmentCount, filledSegmentCount);
798 if (lockError == KErrInUse)
800 // cacheline in use (by other thread):
801 // if this is a read-ahead, abandon it
802 // otherwise re-post the request
806 retCode = CFsRequest::EReqActionComplete;
807 currentOperation->iState = EStEnd;
811 #if defined (_DEBUG_READ_AHEAD)
812 RDebug::Print(_L("READC CACHELINE BUSY POS %d"), I64LOW(segmentStartPos));
815 return CFsRequest::EReqActionBusy;
820 // if not found, try to allocate as many contiguous segments
821 // as possible so that the number of reads issued to the media
822 // driver is kept to a minumum
824 if (addr == NULL) // not found ?
826 // if read len > size of the cache, don't read excess
827 // through the cache as this is wasteful
828 if (totalLen > iCacheSize)
830 TInt len = totalLen - iCacheSize;
831 TInt r = aMsgRequest.PushOperation(
833 (TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
838 currentOperation->iState = EStReStart;
845 return CFsRequest::EReqActionContinue;
848 // if position not cached postpone Initialise() to drive thread
849 // as there may be a read ahead already in the queue
851 if (!driveThread && !readAhead)
853 #if defined (_DEBUG_READ_AHEAD)
854 RDebug::Print(_L("*** POSTING READ TO DRIVE THREAD POS %d ***"), I64LOW(segmentStartPos));
856 return CFsRequest::EReqActionBusy;
859 segmentCount = maxSegments;
860 addr = iCacheClient->AllocateAndLockSegments(segmentStartPos, segmentCount, EFalse, !readAhead);
863 __CACHE_PRINT(_L("AllocateSegment failed"));
864 currentOperation->iState = EStReStart;
870 readLen = segmentCount << segmentSizeLog2;
871 __ASSERT_DEBUG(iSize64 > segmentStartPos, Fault(EPosBeyondSize));
872 readLen = (TInt)Min((TInt64)readLen, (iSize64 - segmentStartPos));
874 if (iCacheClient->SegmentEmpty(segmentStartPos))
876 // store readLen & addr in scratch area
877 currentOperation->iScratchValue0 = addr;
878 currentOperation->iScratchValue1 = (TAny*) readLen;
880 // read into cache segment(s)
881 __CACHE_PRINT2(_L("CACHEFILE: read pos %ld, readLen %d"), segmentStartPos, readLen);
882 TInt r = aMsgRequest.PushOperation(
883 segmentStartPos, readLen, addr, 0,
885 EStReadFromDiskComplete);
888 iCacheClient->UnlockSegments(segmentStartPos);
889 currentOperation->iState = EStReStart;
893 return CFsRequest::EReqActionContinue;
897 currentOperation->iState = EStCopyToClient;
902 case EStCopyToClient:
904 TInt segmentsLocked = (readLen + segmentSize - 1) >> segmentSizeLog2;
906 if (lastError == KErrNone)
908 // copy to user buffer
909 TInt offset = iCacheClient->CacheOffset(currentPos); // offset into segment
910 TInt len = Min(readLen - offset, totalLen);
912 // if addr is NULL then this is a read ahead request
913 if (currentOperation->iReadWriteArgs.iData != NULL)
915 if (currentOperation->iClientRequest)
917 __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
918 TPtrC8 ptr(addr+offset, len);
919 lastError = aMsgRequest.Write(0, ptr, currentOffset);
923 memcpy(((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, addr+offset, len);
928 iReadAheadRequest = NULL;
936 if (lastError == KErrNone)
937 iCacheClient->MarkSegmentsAsFilled(segmentStartPos, segmentsLocked);
938 iCacheClient->UnlockSegments(segmentStartPos);
940 if (lastError != KErrNone)
942 retCode = CFsRequest::EReqActionComplete;
943 currentOperation->iState = EStEnd;
947 if (totalLen > 0 && lastError == KErrNone)
949 currentOperation->iState = EStReadFromCache;
952 currentOperation->iState = EStEnd;
958 // update the file share's position if this request came from client
959 UpdateSharePosition(aMsgRequest, *currentOperation);
962 case EStReadFromDiskComplete:
964 // restore readLen etc from scratch area
965 segmentStartPos = currentPos & segmentSizeMask;
966 addr = (TUint8*) currentOperation->iScratchValue0;
967 readLen = (TInt) currentOperation->iScratchValue1;
969 aMsgRequest.CurrentOperation().iState = EStCopyToClient;
975 // ignore the failure if this is a read-ahead
979 retCode = CFsRequest::EReqActionComplete;
980 currentOperation->iState = EStEnd;
984 // We need to flush all dirty data to disk in case this read overlaps
985 // with any dirty data already in the file cache
986 if (aMode & EFileWriteBuffered)
988 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
989 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
993 retCode = CFsRequest::EReqActionContinue; // read uncached
994 currentOperation->iState = EStEnd;
997 We're now going to by-pass the file cache.
998 If we've already written something to the client's buffer then, in the flexible
999 memory model, the KDesWrittenShift bit will be set and so the descriptor length will
1000 updated in RMessageK::CallbackFunc() when the client thread runs. This (shorter)
1001 length will overwrite the descriptor length written by the local media subsystem.
1002 To get round this problem, we set the descriptor length artificially by writing a
1003 zero-length descriptor at the end of the client's buffer.
1005 if (currentOffset > 0 && currentOperation->iClientRequest)
1007 __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1008 TPtrC8 ptr(NULL, 0);
1009 TInt r = aMsgRequest.Write(0, ptr, currentOffset+totalLen);
1010 __CACHE_PRINT3(_L("CFileCache::DoReadBuffered() failed at pos %lx offset %x totalLen %x\n"), currentPos, currentOffset, totalLen);
1011 __CACHE_PRINT2(_L("CFileCache::DoReadBuffered() writing zero bytes at offset %x r %d\n"), currentOffset + totalLen, r);
1016 aMsgRequest.ReStart();
1017 UpdateSharePosition(aMsgRequest, *currentOperation);
1029 WriteBuffered - attempts to write to cache.
1030 Called from TFsFileRead::Initialise and TFsFileRead::DoRequestL
1032 @return CFsRequest::EReqActionComplete if request entirely satisfied
1033 CFsRequest::EReqActionContinue if request not yet complete
1034 CFsRequest::EReqActionBusy if filecache is busy
1036 TInt CFileCache::WriteBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
1041 CFsClientMessageRequest* newRequest = NULL;
1043 __CACHE_PRINT2(_L("CFileCache::WriteBuffered() pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
1044 TInt r = DoWriteBuffered(aMsgRequest, newRequest, aMode);
1049 newRequest->Dispatch();
1052 if (r == CFsRequest::EReqActionComplete)
1054 TMsgOperation& currentOperation = aMsgRequest.CurrentOperation();
1056 __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1058 TFsFileWrite::CommonEnd(&aMsgRequest, r, iInitialSize, iSize64, currentOperation.iReadWriteArgs.iPos, EFalse);
1067 TInt CFileCache::CompleteWrite(CFsRequest* aRequest)
1069 CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
1071 CFileShare* share = (CFileShare*)aRequest->ScratchValue();
1072 CFileCB& file = share->File();
1073 CFileCache* fileCache = file.FileCache(); //&file;
1074 __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
1077 TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
1081 TInt r = fileCache->WriteBuffered(msgRequest, share->iMode);
1084 __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
1087 // if this request has been cancelled we mustn't dispatch it again -
1088 // we still need to call state machine however so that any locked segments can be unlocked
1089 TInt lastError = msgRequest.LastError();
1090 if (lastError == KErrCancel)
1091 r = CFsRequest::EReqActionComplete;
1098 TInt CFileCache::DoWriteBuffered(CFsMessageRequest& aMsgRequest, CFsClientMessageRequest*& aNewRequest, TUint aMode)
1106 EStReadFromDiskComplete,
1107 EStWriteToCacheComplete,
1116 EReadFirstSegment = 0x01,
1117 EReadLastSegment = 0x02
1120 TBool cachingWrites = (aMode & EFileWriteBuffered)?(TBool)ETrue:(TBool)EFalse;
1121 TInt& lastError = aMsgRequest.LastError();
1122 TBool driveThread = IsDriveThread();
1123 TInt segmentSize = SegmentSize();
1124 TInt segmentSizeLog2 = SegmentSizeLog2();
1125 TInt64 segmentSizeMask = SegmentSizeMask();
1127 // temp storage for transition between EStWriteToCache / EStWriteToCacheComplete and EStCopyFromClient
1128 TInt segmentCount = 0;
1129 TUint8* addr = NULL;
1130 TInt64 firstSegmentStartPos = 0;
1132 TUint8* readAddr = NULL;
1133 TInt readSegmentCount = 0;
1139 TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
1140 TInt retCode = CFsRequest::EReqActionComplete;
1142 TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
1143 TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
1144 TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
1145 switch(currentOperation->iState)
1149 // Report background flush errors back to client
1150 CFileShare* share = (CFileShare*) aMsgRequest.ScratchValue();
1151 if (share->iFlushError != KErrNone)
1153 TInt r = share->iFlushError;
1154 share->iFlushError = KErrNone;
1158 if (currentPos > iSize64)
1159 currentPos = iSize64;
1160 iInitialSize = iSize64;
1162 // if EFileWriteDirectIO OR
1163 // (caching writes and requested write len > size of the cache),
1164 // flush the write cache
1165 if ((aMode & EFileWriteBuffered) == 0 ||
1166 (cachingWrites && totalLen > iCacheSize))
1168 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
1169 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
1172 // if EFileWriteDirectIO then overwrite any non-dirty data and
1173 // write direct to disk....
1175 // if caching writes and requested write len > size of the cache,
1176 // don't write excess through the cache as this is wasteful
1177 if (cachingWrites && totalLen > iCacheSize)
1179 // Destroy ALL cached data (dirty and non-dirty) to ensure
1180 // cache is consistent with data written to disk
1181 iCacheClient->Purge(ETrue);
1183 TInt len = totalLen - iCacheSize;
1184 TInt r = aMsgRequest.PushOperation(
1186 (TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
1191 currentOperation->iState = EStReStart;
1195 currentOffset+= len;
1198 return CFsRequest::EReqActionContinue;
1201 currentOperation->iState = EStWriteToCache;
1205 case EStWriteToCache:
1206 __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1208 // NB we must carry on if we get an error to ensure cache is consistent with disk
1209 if (totalLen == 0 || lastError == KErrCancel)
1211 currentOperation->iState = EStWriteToCacheComplete;
1215 firstSegmentStartPos = currentPos & segmentSizeMask;
1216 TInt64 endPos = currentPos + totalLen;
1218 // Find the maximum number of contiguous segments we need to lock
1219 // in this cacheline - when we unlock these will be marked as filled
1220 const TInt64 dataRange = Min((TInt64)iCacheSize, (endPos + segmentSize - 1 - firstSegmentStartPos));
1221 TInt maxSegmentCount = (TInt)(dataRange >> segmentSizeLog2);
1223 segmentCount = maxSegmentCount;
1226 TInt filledSegmentCount;
1227 addr = iCacheClient->FindAndLockSegments(firstSegmentStartPos, segmentCount, filledSegmentCount, lockError, cachingWrites);
1229 if (lockError == KErrInUse)
1230 return CFsRequest::EReqActionBusy;
1233 if (cachingWrites && addr == NULL && lastError == KErrNone && iCacheClient->TooManyLockedSegments())
1235 // Flush a single dirty cacheline (if there is one for this file)
1236 // or all dirty data on this drive (if there is any).
1237 // If there's nothing to flush then the dirty (locked) data
1238 // must belong to another drive, so there's not much we can do
1239 // but write through
1240 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
1241 if (r == CFsRequest::EReqActionBusy)
1242 return CFsRequest::EReqActionBusy;
1245 // Need to switch to drive thread to flush all data on this drive
1247 return CFsRequest::EReqActionBusy;
1249 TInt r = iDrive->FlushCachedFileInfo();
1251 if (r == CFsRequest::EReqActionBusy)
1252 return CFsRequest::EReqActionBusy;
1254 lastError = KErrNoMemory; // write through
1258 // flush cache before allocating a new cacheline
1259 if (cachingWrites && addr == NULL && lastError == KErrNone)
1261 // if no segment available, flush a single dirty cacheline
1262 // or wait if already flushing
1264 return CFsRequest::EReqActionBusy;
1265 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
1266 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
1270 // if no cacheline found & write caching is enabled, allocate a new cacheline
1271 if (cachingWrites && addr == NULL && lastError == KErrNone)
1273 // try to allocate up to write cache size - this may not
1274 // be possible if a segment in the range is already cached
1275 segmentCount = maxSegmentCount;
1276 addr = iCacheClient->AllocateAndLockSegments(currentPos, segmentCount, cachingWrites, ETrue);
1278 // continue if alloc failed
1280 lastError = KErrNoMemory;
1286 if (cachingWrites && lastError == KErrNone)
1287 lastError = KErrNoMemory;
1289 currentOperation->iState = EStCopyFromClient;
1293 // if the first or last segment are empty then we'll have to read-before-write
1294 TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
1296 TInt startPosSegOffset = iCacheClient->CacheOffset(currentPos);
1297 TInt endPosSegOffset = iCacheClient->CacheOffset(endPos);
1299 // partial first segment ?
1300 if (startPosSegOffset != 0 &&
1301 firstSegmentStartPos < iFileCB->Size64() &&
1302 iCacheClient->SegmentEmpty(firstSegmentStartPos))
1304 readFlags|= EReadFirstSegment;
1307 // partial last segment ?
1308 // NB this may be the first segment too !
1309 if (endPosSegOffset != 0 &&
1310 endPos < lastSegmentStartPos + segmentSize &&
1311 endPos < iFileCB->Size64() &&
1312 iCacheClient->SegmentEmpty(lastSegmentStartPos))
1314 readFlags|= EReadLastSegment;
1317 // read-before-write required ?
1318 if (readFlags & EReadFirstSegment)
1320 readFlags&= ~EReadFirstSegment;
1321 readPos = firstSegmentStartPos;
1323 readSegmentCount = 1;
1324 // if the last segment is empty and it's the same as the first or contiguous,
1325 // then read that too
1326 if ((readFlags & EReadLastSegment) && (segmentCount <= 2))
1328 readSegmentCount = segmentCount;
1329 readFlags&= ~EReadLastSegment;
1331 currentOperation->iState = EStReadFromDisk;
1333 else if (readFlags & EReadLastSegment)
1335 readFlags&= ~EReadLastSegment;
1336 readPos = lastSegmentStartPos;
1337 readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
1338 readSegmentCount = 1;
1339 currentOperation->iState = EStReadFromDisk;
1343 currentOperation->iState = EStCopyFromClient;
1348 case EStReadFromDisk:
1350 // Save address & segmentCount in scratch area
1351 currentOperation->iScratchValue0 = addr;
1352 __ASSERT_DEBUG(segmentCount < 0xFFFF, Fault(EBadSegmentCount));
1353 currentOperation->iScratchValue1 = (TAny*) ((readFlags << 16) | segmentCount);
1355 TInt maxReadLen = readSegmentCount << segmentSizeLog2;
1357 #pragma warning( disable : 4244 )
1358 //Disable Compiler Warning (levels 3 and 4) C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
1359 // maxReadLen is of 31 bits. Hence Min result is reduced to 31 bits
1361 TInt readLen = (TInt)Min((TInt64)maxReadLen, (iSize64 - readPos));
1363 #pragma warning( default : 4244 )
1365 __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1366 TInt r = aMsgRequest.PushOperation(
1367 readPos, readLen, readAddr, 0,
1369 EStReadFromDiskComplete,
1373 lastError = KErrNoMemory;
1374 currentOperation->iState = EStEnd;
1375 iCacheClient->UnlockSegments(firstSegmentStartPos);
1379 // requeue this request
1380 return CFsRequest::EReqActionContinue;
1383 case EStReadFromDiskComplete:
1385 __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1386 // restore addr & segmentCount etc from scratch area
1387 firstSegmentStartPos = currentPos & segmentSizeMask;
1388 addr = (TUint8*) currentOperation->iScratchValue0;
1389 segmentCount = ((TInt) currentOperation->iScratchValue1) & 0xFFFF;
1390 readFlags = ((TInt) currentOperation->iScratchValue1) >> 16;
1392 if (readFlags & EReadLastSegment)
1394 TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
1395 readFlags&= ~EReadLastSegment;
1396 readPos = lastSegmentStartPos;
1397 readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
1398 readSegmentCount = 1;
1399 currentOperation->iState = EStReadFromDisk;
1403 aMsgRequest.CurrentOperation().iState = EStCopyFromClient;
1407 case EStCopyFromClient:
1409 TInt writeLen = segmentCount << segmentSizeLog2;
1410 TInt offset = iCacheClient->CacheOffset(currentPos); // offset into segment
1411 writeLen = Min(writeLen - offset, totalLen);
1415 __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1416 // copy from user buffer to cache buffer
1417 TInt writeError = KErrNone;
1418 if (currentOperation->iClientRequest)
1420 TPtr8 ptr(addr+offset, writeLen, writeLen);
1421 writeError = aMsgRequest.Read(0, ptr, currentOffset);
1425 memcpy(addr+offset, ((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, writeLen);
1428 // "writing" past end of file ?
1429 if (currentPos + writeLen > iSize64 && cachingWrites && lastError == KErrNone && writeError == KErrNone)
1431 __CACHE_PRINT2(_L("CACHEFILE: extending size old %ld new %ld"), iSize64, currentPos + totalLen);
1432 iSize64 = currentPos + writeLen;
1435 TInt anyError = (writeError != KErrNone)?writeError:lastError;
1437 if (anyError == KErrNone)
1438 iCacheClient->MarkSegmentsAsFilled(firstSegmentStartPos, segmentCount);
1440 if (cachingWrites && anyError == KErrNone)
1442 iCacheClient->MarkSegmentsAsDirty(firstSegmentStartPos, segmentCount);
1443 // start dirty data timer
1444 FileDirty(aMsgRequest);
1447 // unlock if we're not buffering writes (segments won't be unlocked if dirty)
1448 iCacheClient->UnlockSegments(firstSegmentStartPos);
1451 currentOffset+= writeLen;
1452 currentPos+= writeLen;
1453 totalLen-= writeLen;
1455 currentOperation->iState = EStWriteToCache;
1459 case EStWriteToCacheComplete:
1461 __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1463 if (lastError == KErrCancel)
1465 currentOperation->iState = EStEnd;
1467 else if ((!cachingWrites) || (lastError != KErrNone))
1469 // allow TFsFileWrite::DoRequestL() to proceed normally using original pos & len
1470 __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1472 currentOperation->iState = EStWriteThrough;
1476 __CACHE_PRINT2(_L("CACHEFILE: write buffered pos %ld, len %d"),
1477 aMsgRequest.CurrentOperation().iReadWriteArgs.iPos, aMsgRequest.CurrentOperation().iReadWriteArgs.iOffset);
1478 __ASSERT_DEBUG(totalLen == 0, Fault(ECompletingWriteWithDataRemaining));
1480 currentOperation->iState = EStEnd;
1485 case EStWriteThrough:
1487 if (lastError == KErrCancel)
1489 currentOperation->iState = EStEnd;
1493 // we're going to issue an uncached write so clear any error
1494 lastError = KErrNone;
1498 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
1500 if (r == CFsRequest::EReqActionBusy)
1501 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
1503 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure
1504 // TFsFileWrite::PostInitialise() doesn't think there's no data left to process
1505 aMsgRequest.ReStart();
1506 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
1510 retCode = CFsRequest::EReqActionContinue;
1512 // fall into...EStRestart
1513 currentOperation->iState = EStReStart;
1517 __ASSERT_DEBUG(retCode == CFsRequest::EReqActionBusy || retCode == CFsRequest::EReqActionContinue, Fault(EBadRetCode));
1519 aMsgRequest.ReStart();
1520 UpdateSharePosition(aMsgRequest, *currentOperation);
1521 if (currentOperation->iClientRequest)
1522 currentOperation->iReadWriteArgs.iPos = currentOperation->iClientPosition; // NB maybe KCurrentPosition64
1533 TInt CFileCache::AllocateRequest(CFsClientMessageRequest*& aNewRequest, TBool aWrite, CSessionFs* aSession)
1536 RLocalMessage msgNew;
1537 const TOperation& oP = OperationArray[aWrite?EFsFileWriteDirty:EFsFileRead];
1538 TInt r = RequestAllocator::GetMessageRequest(oP, msgNew, aNewRequest);
1542 aNewRequest->Set(msgNew, oP, aSession);
1543 aNewRequest->SetDrive(iDrive);
1545 // read-aheads and write-dirty requests should not be posted to plugins
1546 // If there are data-modifying plugins, then these should sit above the file cache
1547 aNewRequest->iCurrentPlugin = NULL;
1548 aNewRequest->EnablePostIntercept(EFalse);
1550 // Scratch value is a CFileCB pointer NOT a CFileShare for requests
1551 // allocated by the file cache
1552 aNewRequest->SetScratchValue64( MAKE_TINT64(EFalse, (TUint) iFileCB) );
1554 // don't call Initialise(), don't call PostInitialise()
1555 aNewRequest->SetState(CFsRequest::EReqStateDoRequest);
1557 // don't call Message().Complete()
1558 aNewRequest->SetCompleted(EFalse);
1560 __ASSERT_DEBUG(aNewRequest->CurrentOperationPtr() == NULL, Fault(EBadOperationIndex));
1565 TInt CFileCache::FlushDirty(CFsRequest* aOldRequest)
1569 CFsClientMessageRequest* newRequest = NULL;
1571 TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
1577 //To be used in notification framework.
1578 //newRequest->iUID = aOldRequest->Message().Identity();
1579 newRequest->Dispatch();
1586 void CFileCache::Purge(TBool aPurgeDirty)
1590 iCacheClient->Purge(aPurgeDirty);
1595 void CFileCache::PropagateFlushErrorToAllFileShares()
1598 TInt count = FileShares->Count();
1601 CFileShare* share = (CFileShare*)(*FileShares)[count];
1602 if (&share->File() == iFileCB)
1604 share->iFlushError = iFlushError;
1607 FileShares->Unlock();
1611 CFileCache::DoFlushDirty()
1613 @param aFlushAll. If EFalse then only a single cacheline will be flushed
1615 returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
1616 CFsRequest::EReqActionComplete if nothing to flush / flush completed
1617 KErrNoMemory if failed to allocate a request
1618 or one of the system wide error codes
1621 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
1627 return CFsRequest::EReqActionBusy;
1629 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
1631 if (segmentCount == 0)
1633 TInt flushError = iFlushError;
1634 iFlushError = KErrNone;
1636 // If this flush didn't originate from a client request return last error
1637 if (!aOldRequest || !aOldRequest->Session())
1638 return (flushError == KErrNone)? CFsRequest::EReqActionComplete : flushError;
1640 // Return the last error from CFileShare::iFlushError
1641 // and then clear CFileShare::iFlushError
1643 CFileShare* share = (CFileShare*) SessionObjectFromHandle(
1644 aOldRequest->Message().Int3(),
1645 FileShares->UniqueID(),
1646 aOldRequest->Session());
1651 r = share->iFlushError;
1652 share->iFlushError = KErrNone;
1655 return CFsRequest::EReqActionComplete;
1661 // NB aOldRequest->Session may be NULL - e.g for FileShareCloseOp
1662 CSessionFs* session = aOldRequest && aOldRequest->Session() ? aOldRequest->Session() : iDirtyDataOwner;
1664 __ASSERT_ALWAYS(session, Fault(EFlushingWithSessionNull));
1666 TInt r = AllocateRequest(aNewRequest, ETrue, session);
1670 r = aNewRequest->PushOperation(0, 0, (TUint8*) NULL, 0);
1674 aNewRequest->Free();
1679 // set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
1680 aNewRequest->CurrentOperation().iScratchValue0 =
1681 (TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
1684 // issue flush request
1685 r = FlushDirtySm(*aNewRequest);
1687 // We should only get three possible return codes from FlushDirtySm() :
1688 // CFsRequest::EReqActionContinue - a write request (aNewRequest) needs to be dispatched
1689 // CFsRequest::EReqActionComplete - completed already - this can happen if dirty data was beyond file end
1690 // CFsRequest::EReqActionBusy - first dirty cacheline is already in use.
1691 __ASSERT_DEBUG( r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionComplete || r == CFsRequest::EReqActionBusy, Fault(EBadRetCode));
1692 if (r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionBusy)
1694 // requeue the caller's request (aOldRequest) if there is one
1695 return CFsRequest::EReqActionBusy;
1697 else // CFsRequest::EReqActionComplete
1699 aNewRequest->PopOperation();
1700 aNewRequest->Free();
1708 TInt TFsFileWriteDirty::PostInitialise(CFsRequest* aRequest)
1710 return CFileCache::CompleteFlushDirty(aRequest);
1713 TInt CFileCache::CompleteFlushDirty(CFsRequest* aRequest)
1715 CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
1719 GetFileFromScratch(aRequest, share, file);
1720 CFileCache* fileCache = file->FileCache();
1722 __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
1725 TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
1728 fileCache->iLock.Wait();
1729 TInt r = fileCache->FlushDirtySm(msgRequest);
1730 fileCache->iLock.Signal();
1733 __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
1736 // if this request has been cancelled we mustn't dispatch it again -
1737 // we still need to call state machine however so that any locked segments can be unlocked
1738 if (msgRequest.LastError() == KErrCancel)
1739 r = CFsRequest::EReqActionComplete;
1744 TInt CFileCache::FlushDirtySm(CFsMessageRequest& aMsgRequest)
1750 EStWriteToDiskComplete,
1755 TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
1756 TInt& lastError = aMsgRequest.LastError();
1762 switch(currentOperation->iState)
1769 case EStWriteToDisk:
1771 currentOperation->iState = EStWriteToDisk;
1774 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
1776 // keep track of how many segments we've written
1777 currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
1779 // if no more dirty segments of if a genuine error then finish
1780 // NB if the request has been cancelled then we still need to proceed and mark all the
1781 // segments as clean, otherwise they will never get freed !
1782 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel))
1784 currentOperation->iState = EStEnd;
1788 TInt len = segmentCount << SegmentSizeLog2();
1791 // Result of Min shall be of size TInt
1792 // Hence to suppress warning
1793 len = (TInt)(Min(iSize64 - pos, (TInt64)len));
1798 // if writing past end of file or this request has been cancelled, just mark as clean
1799 if (len == 0 || lastError == KErrCancel)
1801 currentOperation->iState = EStMarkAsClean;
1805 __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
1807 TInt filledSegmentCount;
1809 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue);
1811 // If cacheline is busy, we need to post request to back of drive queue
1812 // To dispatch to drive thread and intercept before DoRequestL() we must call iPostInitialise(),
1813 // so set the state back to CFsRequest::EReqStatePostInitialise
1814 if (lockError == KErrInUse)
1816 __CACHE_PRINT(_L("FlushDirtySm() - cacheline BUSY !"));
1817 aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
1818 return CFsRequest::EReqActionBusy;
1820 else if (lockError != KErrNone)
1822 iFlushBusy = EFalse;
1823 lastError = lockError;
1824 return CFsRequest::EReqActionComplete;
1827 currentOperation->Set(pos, len, addr, 0, EStWriteToDiskComplete);
1830 TInt r = aMsgRequest.PushOperation(
1833 EStWriteToDiskComplete);
1836 iCacheClient->UnlockSegments(pos);
1837 iFlushBusy = EFalse;
1839 return CFsRequest::EReqActionComplete;
1842 return CFsRequest::EReqActionContinue; // continue on to TFsFileWrite::DoRequestL()()
1846 case EStWriteToDiskComplete:
1849 // simulate a media eject to test critical error server
1850 if (CCacheManagerFactory::CacheManager()->SimulateWriteFailureEnabled())
1852 lastError = KErrNotReady;
1856 pos = currentOperation->iReadWriteArgs.iPos;
1857 iCacheClient->UnlockSegments(pos);
1861 case EStMarkAsClean:
1863 // NB pos must be set by EStWriteToDiskComplete or EStWriteToDisk
1865 if (lastError != KErrNone)
1867 __CACHE_PRINT1(_L("CACHEFILE: WriteThrough FAILED %d"), lastError);
1869 lastError = HandleWriteDirtyError(lastError);
1872 if (lastError == KErrNone)
1874 // clear error and try again
1875 currentOperation->iState = EStWriteToDisk;
1879 iFlushError = lastError;
1880 PropagateFlushErrorToAllFileShares();
1882 __CACHE_PRINT2(_L("CACHEFILE: Resetting size from %ld to %ld"), iSize64, iFileCB->Size64());
1883 SetSize64(iFileCB->Size64());
1886 if (lastError != KErrNone)
1888 // Destroy ALL cached data (dirty and non-dirty) !
1889 iCacheClient->Purge(ETrue);
1893 // Mark segment as clean
1894 iCacheClient->MarkSegmentsAsClean(pos);
1898 if (TInt(currentOperation->iScratchValue0) > 0)
1899 currentOperation->iState = EStWriteToDisk;
1901 currentOperation->iState = EStEnd;
1909 iFlushBusy = EFalse;
1913 // Re-start dirty data timer if there is still some dirty data
1914 if (iCacheClient->FindFirstDirtySegment(pos, addr) != 0)
1915 FileDirty(aMsgRequest);
1917 return CFsRequest::EReqActionComplete;
1924 Handle a dirty data write error
1926 Returns aError if dirty data should be thrown away or
1927 KErrNone if write should be retried
1929 TInt CFileCache::HandleWriteDirtyError(TInt aError)
1931 __THRD_PRINT3(_L(">TRACE: CFileCache::HandleWriteDirtyError() aError %d mounted %d changed %d"), aError, iDrive->IsMounted(), iDrive->IsChanged());
1933 // normally the disk change will have been detected by TDrive::CheckMount() but occasionally,
1934 // the change will occur while writing - in which case we need to mimick what TDrive::CheckMount does,
1935 // to make sure we are in a consisten state i.e. dismount the drive and set iCurrentMount to NULL
1936 if (iDrive->IsChanged())
1938 iDrive->SetChanged(EFalse);
1939 if (iDrive->IsMounted()) // Dismount the mount if it is still marked as mounted
1943 // if error didn't occur because of a media eject, do nothing
1944 if (aError == KErrNotReady && !iDrive->IsMounted())
1947 TLocaleMessage line1;
1948 TLocaleMessage line2;
1949 line1=EFileServer_PutTheCardBackLine1;
1950 line2=EFileServer_PutTheCardBackLine2;
1952 //-- create Notifier
1953 CAsyncNotifier* notifier = CAsyncNotifier::New();
1959 notifier->SetMount(iMount);
1962 // While this (drive) thread is calling the notifier server (& effectively suspended),
1963 // the main thread may call TFsFileRead::PostInitialise() or TFsFileWrite::PostInitialise()
1964 // which would cause dead-lock unless we release the lock here. We also need to release
1965 // the lock before calling CDriveThread::CompleteReadWriteRequests().
1972 TInt ret = notifier->Notify(
1973 TLocaleMessageText(line1),
1974 TLocaleMessageText(line2),
1975 TLocaleMessageText(EFileServer_Button1),
1976 TLocaleMessageText(EFileServer_Button2),
1984 // Wait up to 3 seconds for a disk change - this is because there is often a substantial
1985 // (one/two second) delay between inserting a card & getting a notification that the card is ready;
1986 // if we give up too soon and fire of the notifier again, it's not very user-friendly !
1987 const TTimeIntervalMicroSeconds32 KHalfSecond(500000);
1988 const TInt KRetries = 6;;
1989 for (TInt n=0; !iDrive->IsChanged() && n<KRetries; n++)
1990 User::After(KHalfSecond);
1994 // Without this code, retry will indiscriminately write over whatever disk happens to be present.
1995 // However if the write error is to the bootsector remounting will always fail because the boot
1996 // sector will have changed and hence the disk is useless.
1998 TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
2000 TInt remountSuccess = iDrive->ReMount(*iMount);
2002 TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
2003 if (!remountSuccess)
2007 iMount->Drive().SetChanged(EFalse);
2009 aError = KErrNone; // Retry
2016 // Cancel hung state
2017 FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
2020 // media had been removed and NOT replaced: so destroy ALL cached data
2021 // (dirty and non-dirty) for all filecaches on this drive
2022 if (aError != KErrNone)
2024 CDriveThread* pT=NULL;
2025 TInt r=FsThreadManager::GetDriveThread(DriveNumber(), &pT);
2028 pT->CompleteReadWriteRequests();
2029 iDrive->PurgeDirty(*iMount);
2044 void CFileCache::FileDirty(CFsMessageRequest& aMsgRequest)
2046 CSessionFs* session = aMsgRequest.Session();
2047 __ASSERT_ALWAYS(session, Fault(EDirtyDataOwnerNull));
2049 // Remember the last session which caused the file to become dirty
2050 // Always record whether any session has reserved access so the CheckDiskSpace() behaves correctly
2051 if (iDirtyDataOwner == NULL || session->ReservedAccess(iDriveNum))
2052 iDirtyDataOwner = session;
2054 // start a timer after which file will be flushed
2055 CDriveThread* driveThread=NULL;
2056 TInt r = FsThreadManager::GetDriveThread(iDriveNum, &driveThread);
2057 if(r == KErrNone && driveThread != NULL)
2058 iDirtyTimer.Start(driveThread, iDirtyDataFlushTime);
2061 //----------------------------------------------------------------------------
2063 Mark the file as clean and stop dirty data timer
2065 void CFileCache::MarkFileClean()
2067 iDirtyDataOwner = NULL;
2077 //************************************
2078 // TFileCacheSettings
2079 //************************************
2081 RArray<TFileCacheSettings::TFileCacheConfig>* TFileCacheSettings::iFileCacheSettings = NULL;
2085 const TInt KDriveCacheSettingsArrayGranularity = 4;
2088 TFileCacheSettings::TFileCacheConfig::TFileCacheConfig(TInt aDrive) :
2090 iFileCacheReadAsync(KDefaultFileCacheReadAsync),
2091 iFairSchedulingLen(KDefaultFairSchedulingLen << KByteToByteShift),
2092 iCacheSize(KDefaultFileCacheSize << KByteToByteShift),
2093 iMaxReadAheadLen(KDefaultFileCacheMaxReadAheadLen << KByteToByteShift),
2094 iClosedFileKeepAliveTime(KDefaultClosedFileKeepAliveTime << KByteToByteShift), // convert milliSecs -> microSecs (approximately)
2095 iDirtyDataFlushTime(KDefaultDirtyDataFlushTime << KByteToByteShift) // convert milliSecs -> microSecs (approximately)
2097 iFlags = ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
2101 TFileCacheFlags TFileCacheSettings::TFileCacheConfig::ConvertEnumToFlags(const TInt aFileCacheRead, const TInt aFileCacheReadAhead, const TInt aFileCacheWrite)
2106 if (aFileCacheRead == EFileCacheFlagEnabled)
2107 flags|= EFileCacheReadEnabled;
2108 else if (aFileCacheRead == EFileCacheFlagOn)
2109 flags|= EFileCacheReadEnabled | EFileCacheReadOn;
2112 if (aFileCacheReadAhead == EFileCacheFlagEnabled)
2113 flags|= EFileCacheReadAheadEnabled;
2114 else if (aFileCacheReadAhead == EFileCacheFlagOn)
2115 flags|= EFileCacheReadAheadEnabled | EFileCacheReadAheadOn;
2118 if (aFileCacheWrite == EFileCacheFlagEnabled)
2119 flags|= EFileCacheWriteEnabled;
2120 else if (aFileCacheWrite == EFileCacheFlagOn)
2121 flags|= EFileCacheWriteEnabled | EFileCacheWriteOn;
2123 return TFileCacheFlags(flags);
2128 _LIT8(KLitSectionNameDrive,"Drive%C");
2130 static const TPtrC8 KCacheFlagEnumStrings[]=
2135 _S8(""), // terminator
2137 const TInt KMaxEnumLen = 7;
2139 void TFileCacheSettings::ReadEnum(const TDesC8& aSection, const TDesC8& aProperty, TInt32& aEnumVal, const TPtrC8* aEnumStrings)
2141 TBuf8<KMaxEnumLen> buf;
2142 if (!F32Properties::GetString(aSection, aProperty, buf))
2145 const TPtrC8* enumString;
2146 for (enumString=aEnumStrings, n=0; enumString->Length()!= 0; enumString++, n++)
2148 if (buf.LeftTPtr(enumString->Length()).MatchF(*enumString) == 0)
2156 TInt TFileCacheSettings::ReadPropertiesFile(TInt aDriveNumber)
2161 if (!TGlobalFileCacheSettings::Enabled())
2164 TFileCacheConfig* driveCacheSettings;
2165 TInt r = GetFileCacheConfig(aDriveNumber, driveCacheSettings);
2169 // restore default settings in case they've been changed by SetFlags()
2170 driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
2173 // Get file cache configuration settings for this drive
2174 // N.B. Size/length values are specified in Kilobytes, timer values in Milliseconds
2175 TBuf8<8> sectionName;
2176 sectionName.Format(KLitSectionNameDrive, 'A' + aDriveNumber);
2179 // Read FileCacheSize
2180 if (F32Properties::GetInt(sectionName, _L8("FileCacheSize"), val))
2181 driveCacheSettings->iCacheSize = val << KByteToByteShift;
2183 // ensure read-ahead len is not greater than the cache size
2184 driveCacheSettings->iMaxReadAheadLen =
2185 Min(driveCacheSettings->iMaxReadAheadLen, driveCacheSettings->iCacheSize);
2187 // Read FileCacheReadAsync
2189 if (F32Properties::GetBool(sectionName, _L8("FileCacheReadAsync"), bVal))
2190 driveCacheSettings->iFileCacheReadAsync = bVal;
2192 // Read FairSchedulingLen
2193 if (F32Properties::GetInt(sectionName, _L8("FairSchedulingLen"), val))
2194 driveCacheSettings->iFairSchedulingLen = val << KByteToByteShift;
2196 // Read ClosedFileKeepAliveTime - convert miliSecs to microSecs (approximately)
2197 if (F32Properties::GetInt(sectionName, _L8("ClosedFileKeepAliveTime"), val))
2198 driveCacheSettings->iClosedFileKeepAliveTime = val << KByteToByteShift;
2200 // Read DirtyDataFlushTime - convert miliSecs to microSecs (approximately)
2201 if (F32Properties::GetInt(sectionName, _L8("DirtyDataFlushTime"), val))
2202 driveCacheSettings->iDirtyDataFlushTime = val << KByteToByteShift;
2204 // get read, read-ahead and write states
2205 TInt32 readVal = KDefaultFileCacheRead;
2206 TInt32 readAheadVal = KDefaultFileCacheReadAhead;
2207 TInt32 writeVal = KDefaultFileCacheWrite;
2209 ReadEnum(sectionName, _L8("FileCacheRead"), readVal, &KCacheFlagEnumStrings[0]);
2210 ReadEnum(sectionName, _L8("FileCacheReadAhead"), readAheadVal, &KCacheFlagEnumStrings[0]);
2211 ReadEnum(sectionName, _L8("FileCacheWrite"), writeVal, &KCacheFlagEnumStrings[0]);
2212 driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(readVal, readAheadVal, writeVal);
2214 __CACHE_PRINT7(_L("ReadPropertiesFile() drive %C flags %08X CacheSize %d FileCacheReadAsync %d iFairSchedulingLen %d iClosedFileKeepAliveTime %d iDirtyDataFlushTime %d\n"),
2216 driveCacheSettings->iFlags,
2217 driveCacheSettings->iCacheSize,
2218 driveCacheSettings->iFileCacheReadAsync,
2219 driveCacheSettings->iFairSchedulingLen,
2220 driveCacheSettings->iClosedFileKeepAliveTime,
2221 driveCacheSettings->iDirtyDataFlushTime);
2227 TInt TFileCacheSettings::GetFileCacheConfig(TInt aDrive, TFileCacheConfig*& aConfig)
2233 TFileCacheConfig driveCacheSettingsToMatch(aDrive);
2235 TInt index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
2236 if (index == KErrNotFound)
2238 // create a new entry.
2239 TInt r = iFileCacheSettings->InsertInUnsignedKeyOrder(driveCacheSettingsToMatch);
2242 index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
2243 __ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheSettingsNotFound));
2246 aConfig = &(*iFileCacheSettings)[index];
2250 void TFileCacheSettings::SetFlags(TInt aDrive, TFileCacheFlags aFlags)
2252 TFileCacheConfig* driveCacheSettings;
2253 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2254 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2256 driveCacheSettings->iFlags = aFlags;
2259 TFileCacheFlags TFileCacheSettings::Flags(TInt aDrive)
2261 TFileCacheConfig* driveCacheSettings;
2262 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2263 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2265 return driveCacheSettings->iFlags;
2268 void TFileCacheSettings::Init()
2270 if (iFileCacheSettings == NULL)
2272 iFileCacheSettings = new RArray<TFileCacheConfig>(KDriveCacheSettingsArrayGranularity, _FOFF(TFileCacheConfig, iDrive));
2273 __ASSERT_ALWAYS(iFileCacheSettings != NULL, Fault(ECacheSettingsInitFailed));
2278 TBool TFileCacheSettings::FileCacheReadAsync(TInt aDrive)
2280 TFileCacheConfig* driveCacheSettings;
2281 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2282 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2284 return driveCacheSettings->iFileCacheReadAsync;
2287 TInt TFileCacheSettings::FairSchedulingLen(TInt aDrive)
2289 TFileCacheConfig* driveCacheSettings;
2290 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2291 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2293 return driveCacheSettings->iFairSchedulingLen;
2296 TInt TFileCacheSettings::MaxReadAheadLen(TInt aDrive)
2298 TFileCacheConfig* driveCacheSettings;
2299 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2300 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2302 return driveCacheSettings->iMaxReadAheadLen;
2305 TInt TFileCacheSettings::CacheSize(TInt aDrive)
2307 TFileCacheConfig* driveCacheSettings;
2308 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2309 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2311 return driveCacheSettings->iCacheSize;
2314 TInt TFileCacheSettings::ClosedFileKeepAliveTime(TInt aDrive)
2316 TFileCacheConfig* driveCacheSettings;
2317 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2318 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2320 return driveCacheSettings->iClosedFileKeepAliveTime;
2324 TInt TFileCacheSettings::DirtyDataFlushTime(TInt aDrive)
2326 TFileCacheConfig* driveCacheSettings;
2327 TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
2328 __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
2330 return driveCacheSettings->iDirtyDataFlushTime;
2333 //************************************
2335 //************************************
2337 CFsObjectCon* TClosedFileUtils::iClosedFiles = NULL;
2339 void TClosedFileUtils::InitL()
2341 iClosedFiles = TheContainer->CreateL();
2342 if (iClosedFiles == NULL)
2343 User::LeaveIfError(KErrNoMemory);
2348 TBool TClosedFileUtils::IsClosed(CFileCache* aFileCache)
2350 return (aFileCache->Container() == iClosedFiles);
2353 TInt TClosedFileUtils::Count()
2355 return iClosedFiles->Count();
2358 CFileCache* TClosedFileUtils::At(TInt aIndex)
2360 return (CFileCache*) (*iClosedFiles)[aIndex];
2364 // Add a closed file to closed file container
2365 void TClosedFileUtils::AddL(CFileCache* aFileCache, TBool aLock)
2367 if (aFileCache->iDriveThread)
2369 iClosedFiles->AddL(aFileCache, aLock);
2371 // start a timer after which file will be removed from closed queue
2372 CDriveThread* driveThread=NULL;
2373 TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &driveThread);
2374 if(r == KErrNone && driveThread != NULL)
2375 aFileCache->iClosedTimer.Start(driveThread, aFileCache->iClosedFileKeepAliveTime);
2381 // Remove a closed file from closed file container so that it can be re-opened
2382 void TClosedFileUtils::ReOpen(CFileCache* aFileCache, TBool aLock)
2384 // get the drive thread in case it has changed since the file was last open
2385 const TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &aFileCache->iDriveThread);
2386 if ((r == KErrNone) && aFileCache->iDriveThread)
2387 aFileCache->iClosedTimer.Stop();
2389 iClosedFiles->Remove(aFileCache, aLock);
2392 // Remove all closed files from closed file container and close them for good
2393 void TClosedFileUtils::Remove()
2395 RemoveFiles(NULL, NULL);
2398 // Remove all closed files belonging to a particular TDrive from closed file container and close them for good
2399 void TClosedFileUtils::Remove(TInt aDrvNumber)
2401 RemoveFiles(&TClosedFileUtils::TestDrive, (TAny*) aDrvNumber);
2404 // Remove a closed file from closed file container and close it for good
2405 void TClosedFileUtils::Remove(CFileCache* aFileCache)
2407 RemoveFiles(&TClosedFileUtils::TestFile, (TAny*) aFileCache);
2410 void TClosedFileUtils::RemoveFiles(TTestFunc aTestFunc, TAny* aTestVal)
2414 TInt count = TClosedFileUtils::Count();
2417 CFileCache& file = *(CFileCache*)(*iClosedFiles)[count];
2418 if ((aTestFunc == NULL) || ((*aTestFunc)(file, aTestVal)))
2420 iClosedFiles->Remove(&file, EFalse);
2429 TBool TClosedFileUtils::TestDrive(CFileCache& aFileCache, TAny* aVal)
2431 TBool r = (aFileCache.iDriveNum == (TInt) aVal)?(TBool) ETrue: (TBool) EFalse;
2434 __CACHE_PRINT1(_L("CLOSEDFILES: TestDrive() closing %S\n"), &aFileCache.FileNameF() );
2438 TBool TClosedFileUtils::TestFile(CFileCache& aFileCache, TAny* aVal)
2440 TBool r = (&aFileCache == ((CFileCache*) aVal))?(TBool)ETrue:(TBool)EFalse;
2443 __CACHE_PRINT1(_L("CLOSEDFILES: TestFile() closing %S\n"), &aFileCache.FileNameF() );
2450 void TClosedFileUtils::Lock()
2452 iClosedFiles->Lock();
2455 void TClosedFileUtils::Unlock()
2457 iClosedFiles->Unlock();