--- a/userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp Wed Jun 23 11:59:44 2010 +0100
+++ b/userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp Wed Jun 23 12:52:28 2010 +0100
@@ -27,9 +27,9 @@
The static cache page creation function.
Cache page objects are not supposed to be created on the stack, so this factory function is required.
*/
-TDynamicDirCachePage* TDynamicDirCachePage::NewL(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr)
+TDynamicDirCachePage* TDynamicDirCachePage::CreateCachePage(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr)
{
- return new(ELeave) TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr);
+ return new TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr);
}
/**
@@ -113,6 +113,7 @@
iMinSizeInPages(aMinPageNum),
iMaxSizeInPages(aMaxPageNum),
iDrive(aDrive),
+ iLastVisitedPage(NULL),
iLockedQ(_FOFF(TDynamicDirCachePage, iLink)),
iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)),
iLockedQCount(0),
@@ -159,15 +160,18 @@
// allocate as many permanently locked pages as there are threads - plus one
// otherwise DoMakePageMRU() won't work properly with only one thread
//-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool)
- const TUint KThreadCount = 1;
- iPermanentlyAllocatedPageCount = KThreadCount + 1;
+ const TUint KThreadCount = 1;
+ iPermanentlyAllocatedPageCount = KThreadCount;
if (iPermanentlyAllocatedPageCount > iMinSizeInPages)
iMinSizeInPages = iPermanentlyAllocatedPageCount;
for (TUint n=0; n<iPermanentlyAllocatedPageCount; n++)
{
- TDynamicDirCachePage* pPage = AllocateAndLockNewPageL(0);
+ TDynamicDirCachePage* pPage = AllocateAndLockNewPage();
+ ASSERT(pPage);
+ if (!pPage)
+ User::Leave(KErrNoMemory);
AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked);
LookupTblAdd(pPage);
}
@@ -188,42 +192,6 @@
}
/**
-Insert an unlocked page into the last position of the locked queue, may squeeze the original last page into
-the unlocked queue.
-This function is used on last visited but 'unlocked' pages to avoid excessive lock/unlock calls to cache memory
-manager as contiguous entry reading/writing often happens on the same page.
-@param aPage the page to be inserted.
-@pre the page type of aPage should only be TDynamicDirCachePage::EUnknown
-*/
-void CDynamicDirCache::MakePageLastLocked(TDynamicDirCachePage* aPage)
- {
- // this function should not be called on active pages
- ASSERT(aPage->iType == TDynamicDirCachePage::EUnknown);
-
- if (iLockedQ.IsEmpty())
- {
- // if locked queue is empty, add it onto the locked queue directly
- AddFirstOntoQueue(aPage, TDynamicDirCachePage::ELocked);
- }
- else
- {
- // otherwise, we squeeze for the last position on locked queue
- while (iLockedQCount + 1 >= iMinSizeInPages)
- {
- TDynamicDirCachePage* last = iLockedQ.Last();
- DeQueue(last);
- UnlockPage(last);
- AddFirstOntoQueue(last, TDynamicDirCachePage::EUnlocked);
- }
-
- // iLockedQCount + 1 < iMinSizeInPages
- iLockedQ.AddLast(*aPage);
- aPage->SetPageType(TDynamicDirCachePage::ELocked);
- iLockedQCount++;
- }
- }
-
-/**
Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page first.
The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor.
@@ -245,13 +213,19 @@
aDes.Append(pPage->PtrInPage(aPos), aLength);
- // if page is from unlocked queue, insert it onto the last page of the locked
- // queue. this is to avoid excessive locking and unlocking operations that is
- // highly likely to happen for following reads.
- if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
- {
- DeQueue(pPage);
- MakePageLastLocked(pPage);
+ // if page is from unlocked queue, try to keep it locked until we move to a
+ // different page from the unlocked queue
+ // this is to avoid excessive locking and unlocking operations that is
+ // highly likely to happen when DoFindL() linearly scan through the directory
+ if (pPage->PageType() == TDynamicDirCachePage::EUnlocked
+ && iLastVisitedPage != pPage)
+ {
+ // Note: iLastVisitedPage may have been moved from unlocked queue to locked queue
+ if(iLastVisitedPage && iLastVisitedPage->PageType() == TDynamicDirCachePage::EUnlocked)
+ {
+ User::LeaveIfError(UnlockPage(iLastVisitedPage));
+ }
+ iLastVisitedPage = pPage;
}
}
else // page locking failed
@@ -621,23 +595,32 @@
case EDumpCache:
{
RFs fs;
- fs.Connect();
+ r = fs.Connect();
+ if(r != KErrNone)
+ break;
+
const TUint32 debugRegister = DebugRegister();
fs.SetDebugRegister(debugRegister|KFSYS);
Dump();
fs.SetDebugRegister(debugRegister);
fs.Close();
+ r = KErrNone;
break;
}
case ECacheInfo:
{
RFs fs;
- fs.Connect();
+ r = fs.Connect();
+ if(r != KErrNone)
+ break;
+
const TUint32 debugRegister = DebugRegister();
fs.SetDebugRegister(debugRegister|KFSYS);
- Info();
+ TDirCacheInfo* aInfo = static_cast<TDirCacheInfo*>(aParam2);
+ Info(aInfo);
fs.SetDebugRegister(debugRegister);
fs.Close();
+ r = KErrNone;
break;
}
@@ -680,60 +663,60 @@
{
// __PRINT1(_L("MakePageMRU (%lx)"), aPos);
// __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages);
- // check there are at least two locked pages
- ASSERT(iLockedQCount > 1);
+ // check there are at least one locked pages
+ ASSERT(iLockedQCount > 0);
// check the MRU page first, if it is already the MRU page, we can return immediately
TInt64 pageStartMedPos = CalcPageStartPos(aPos);
if (!iLockedQ.IsEmpty())
{
- if (iLockedQ.First()->StartPos() == pageStartMedPos)
+ if (iLockedQCount > 1 && iLockedQ.First()->StartPos() == pageStartMedPos)
{
return;
}
}
TDynamicDirCachePage* pPage = FindPageByPos(aPos);
- if (pPage)
- {
- ASSERT(pPage->IsValid());
+ if (pPage)
+ {
+ ASSERT(pPage->IsValid());
// lock page before make it MRU
- if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
- {
- ASSERT(!pPage->IsLocked());
- if (LockPage(pPage) == NULL)
- {
- DeQueue(pPage);
- LookupTblRemove(pPage->StartPos());
- DecommitPage(pPage);
- delete pPage;
- pPage = NULL;
- }
- }
- else
- {
- // error checking: page should either be locked or active
- ASSERT(LockPage(pPage) != NULL);
- }
- }
+ if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
+ {
+ ASSERT(!pPage->IsLocked() || (pPage->IsLocked() && pPage == iLastVisitedPage));
+ if (LockPage(pPage) == NULL)
+ {
+ DeQueue(pPage);
+ LookupTblRemove(pPage->StartPos());
+ DecommitPage(pPage);
+ delete pPage;
+ pPage = NULL;
+ }
+ }
+ else
+ {
+ // error checking: page should either be locked or active
+ ASSERT(LockPage(pPage) != NULL);
+ }
+ }
- // if page not found or page data not valid anymore, use active page to read data
- if (!pPage)
- {
- TRAPD(err, pPage = UpdateActivePageL(aPos));
- if (err != KErrNone)
- {
- // problem occurred reading active page, return immediately.
- return;
- }
- }
+ // if page not found or page data not valid anymore, use active page to read data
+ if (!pPage)
+ {
+ TRAPD(err, pPage = UpdateActivePageL(aPos));
+ if (err != KErrNone)
+ {
+ // problem occurred reading active page, return immediately.
+ return;
+ }
+ }
- // by now, the page is either locked or active page
+ // by now, the page is either locked or active page
ASSERT(pPage && pPage->IsValid() && pPage->IsLocked());
- TBool allocateNewPage = pPage == iLockedQ.Last() && !CacheIsFull();
+ TBool makeNewPageMRU = pPage == iLockedQ.Last();
switch (pPage->PageType())
@@ -762,24 +745,34 @@
ASSERT(0);
}
- if (allocateNewPage)
- {
- TDynamicDirCachePage* nPage = NULL;
- TRAPD(err, nPage = AllocateAndLockNewPageL(0));
- if (err == KErrNone)
- {
+ if (!makeNewPageMRU)
+ return;
+
+ // when cache is full and a new MRU page is about to be added, we will need to evict the LRU page
+ // accordingly
+ if (CacheIsFull())
+ {
+ TUint32& queueCnt = iMaxSizeInPages - iMinSizeInPages > 0 ? iUnlockedQCount : iLockedQCount;
+ queueCnt++;
+ CheckThresholds();
+ queueCnt--;
+ }
- // about to add a page to end of locked queue, so lie about iLockedQCount
- iLockedQCount++;
- CheckThresholds();
- iLockedQCount--;
+ // attempt to grow the cache by appending a clean, new page at the end of the locked page queue.
+ // This can fail when out of memory; the LRU mechanism then makes sure the oldest page will be re-used.
+ TDynamicDirCachePage* nPage = AllocateAndLockNewPage();
+ if (!nPage)
+ return;
- iLockedQ.AddLast(*nPage);
- nPage->SetPageType(TDynamicDirCachePage::ELocked);
- ++iLockedQCount;
- LookupTblAdd(nPage);
- }
- }
+ // about to add the new active page, force the locked queue to evict the existing last page to make room
+ // for the new active page
+ iLockedQCount++;
+ CheckThresholds();
+ iLockedQCount--;
+
+ iLockedQ.AddLast(*nPage);
+ nPage->SetPageType(TDynamicDirCachePage::ELocked);
+ ++iLockedQCount;
}
/**
@@ -855,7 +848,7 @@
*/
void CDynamicDirCache::CheckThresholds()
{
- while (iLockedQCount + 1 > iMinSizeInPages)
+ while (iLockedQCount > iMinSizeInPages)
{
TDynamicDirCachePage* movePage = iLockedQ.Last();
UnlockPage(movePage);
@@ -895,58 +888,67 @@
Try to create a new page and lock the page content when it is created. This function should only be called
when creating iActive page or making a page MRU (which might result in page evictions).
@return the pointer of the newly created page, or NULL if allocation failed.
-@param aStartMedPos the starting media address of the page to be created.
-@pre aStartMedPos should not already be existing in the cache.
*/
-TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPageL(TInt64 aStartMedPos)
- {
- __PRINT1(_L("CDynamicDirCache::AllocateAndLockNewPageL(aStartMedPos=%lx)"), aStartMedPos);
+TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPage(/*TInt64 aStartMedPos*/)
+ {
+ __PRINT(_L("CDynamicDirCache::AllocateAndLockNewPage()"));
+
+ TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs());
+
+ if (!startRamAddr)
+ return NULL;
+
+ TDynamicDirCachePage* pPage = TDynamicDirCachePage::CreateCachePage(this, 0, startRamAddr);
- TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs());
- if (startRamAddr)
- {
- // create new page and return
- TDynamicDirCachePage* pPage = TDynamicDirCachePage::NewL(this, aStartMedPos, startRamAddr);
- pPage->SetLocked(ETrue);
- pPage->SetValid(EFalse);
- return pPage;
- }
+ // Failure would mean the cache chunk was able to grow but we've run out of heap.
+ // This seems extremely unlikely, but decommit the now-unmanageable cache segment just in case.
+ if (!pPage)
+ {
+ iCacheMemoryClient->DecommitSegments(startRamAddr, PageSizeInSegs());
+ return NULL;
+ }
- return NULL;
+ pPage->SetLocked(ETrue);
+ pPage->SetValid(EFalse);
+ return pPage;
}
-#ifdef _DEBUG
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
/**
Dump cache information, only enabled in debug mode.
@see CDynamicDirCache::Control()
*/
-void CDynamicDirCache::Info() const
- {
- __PRINT(_L("======== CDynamicDirCache::Info ========="));
- const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2();
- // page size
- __PRINT1(_L("=== Pages size: [%d Bytes]"), iPageSizeInBytes);
- __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2);
+void CDynamicDirCache::Info(TDirCacheInfo* aInfo) const
+ {
+ __PRINT(_L("======== CDynamicDirCache::Info ========="));
+ const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2();
+ const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2;
+ // page size
+ __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2);
+ __PRINT1(_L("=== Page data size: [%d Bytes]"), iPageSizeInBytes);
+ __PRINT1(_L("=== Page mem size: [%d Bytes]"), 1 << pageMemSizeLog2);
- // data size:
- __PRINT1(_L("=== Min data size: [%d Bytes]"), iMinSizeInPages << iPageSizeLog2);
- __PRINT1(_L("=== Max data size: [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2);
+ // cache size in pages
+ __PRINT1(_L("=== Min cache size in pages: [%d]"), iMinSizeInPages);
+ __PRINT1(_L("=== Max cache size in pages: [%d]"), iMaxSizeInPages);
- // memory size:
- const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2;
- __PRINT1(_L("=== Min memory size: [%d Bytes]"), iMinSizeInPages << pageMemSizeLog2);
- __PRINT1(_L("=== Max memory size: [%d Bytes]"), iMaxSizeInPages << pageMemSizeLog2);
-
- // reserved pages
- __PRINT1(_L("=== Number of pages reserved: [%d]"), iMinSizeInPages);
- __PRINT1(_L("=== Reserved memory: [%d Bytes]"), (iMinSizeInPages * PageSizeInSegs()) << SegmentSizeInBytesLog2);
- // locked page num
- __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount);
- __PRINT1(_L("=== Locked memory: [%d Bytes]"), (iLockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2);
- // unlocked page num
- __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount);
- __PRINT1(_L("=== Unlocked memory: [%d Bytes]"), (iUnlockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2);
- }
+ // locked page num
+ __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount);
+ // unlocked page num
+ __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount);
+ __PRINT(_L("=========================================\n"));
+
+ ASSERT(aInfo);
+ aInfo->iMemorySegmentSize = 1 << SegmentSizeInBytesLog2;
+ aInfo->iPageSizeInMemory = PageSizeInSegs() << SegmentSizeInBytesLog2;
+ aInfo->iPageSizeInData = iPageSizeInBytes;
+ aInfo->iMinCacheSizeInPages = iMinSizeInPages;
+ aInfo->iMaxCacheSizeInPages = iMaxSizeInPages;
+ aInfo->iMinCacheSizeInMemory = iMinSizeInPages * aInfo->iPageSizeInMemory;
+ aInfo->iMaxCacheSizeInMemory = iMaxSizeInPages * aInfo->iPageSizeInMemory;
+ aInfo->iLockedPageNumber = iLockedQCount;
+ aInfo->iUnlockedPageNumber = iUnlockedQCount;
+ }
/**
Dump cache content, only enabled in debug mode.
@@ -996,7 +998,7 @@
}
__PRINT(_L("===========================================\n"));
}
-#endif //_DEBUG
+#endif //#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
/**
Lock an unlocked page, or do nothing if the page is already locked.
@@ -1028,14 +1030,17 @@
{
ASSERT(aPage != NULL);
__PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos());
- TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs());
- if (r == KErrNone)
- {
- aPage->SetLocked(EFalse);
+ if (aPage)
+ {
+ TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs());
+ if (r == KErrNone)
+ {
+ aPage->SetLocked(EFalse);
+ }
+ return r;
}
- return r;
- }
-
+ return KErrArgument;
+ }
/**
Decommit a locked or unlocked page.
@return TInt KErrNone if decommition was successful, otherwise system-wide error code.