111 :iPageSizeLog2(aPageSizeInBytesLog2), |
111 :iPageSizeLog2(aPageSizeInBytesLog2), |
112 iWrGranularityLog2(aWrGranularityLog2), |
112 iWrGranularityLog2(aWrGranularityLog2), |
113 iMinSizeInPages(aMinPageNum), |
113 iMinSizeInPages(aMinPageNum), |
114 iMaxSizeInPages(aMaxPageNum), |
114 iMaxSizeInPages(aMaxPageNum), |
115 iDrive(aDrive), |
115 iDrive(aDrive), |
|
116 iLastVisitedPage(NULL), |
116 iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
117 iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
117 iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
118 iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
118 iLockedQCount(0), |
119 iLockedQCount(0), |
119 iUnlockedQCount(0), |
120 iUnlockedQCount(0), |
120 iHashFunction(HashFunction), |
121 iHashFunction(HashFunction), |
157 |
158 |
158 |
159 |
159 // allocate as many permanently locked pages as there are threads - plus one |
160 // allocate as many permanently locked pages as there are threads - plus one |
160 // otherwise DoMakePageMRU() won't work properly with only one thread |
161 // otherwise DoMakePageMRU() won't work properly with only one thread |
161 //-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool) |
162 //-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool) |
162 const TUint KThreadCount = 1; |
163 const TUint KThreadCount = 1; |
163 iPermanentlyAllocatedPageCount = KThreadCount + 1; |
164 iPermanentlyAllocatedPageCount = KThreadCount; |
164 |
165 |
165 if (iPermanentlyAllocatedPageCount > iMinSizeInPages) |
166 if (iPermanentlyAllocatedPageCount > iMinSizeInPages) |
166 iMinSizeInPages = iPermanentlyAllocatedPageCount; |
167 iMinSizeInPages = iPermanentlyAllocatedPageCount; |
167 |
168 |
168 for (TUint n=0; n<iPermanentlyAllocatedPageCount; n++) |
169 for (TUint n=0; n<iPermanentlyAllocatedPageCount; n++) |
169 { |
170 { |
170 TDynamicDirCachePage* pPage = AllocateAndLockNewPageL(0); |
171 TDynamicDirCachePage* pPage = AllocateAndLockNewPage(); |
|
172 ASSERT(pPage); |
|
173 if (!pPage) |
|
174 User::Leave(KErrNoMemory); |
171 AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); |
175 AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); |
172 LookupTblAdd(pPage); |
176 LookupTblAdd(pPage); |
173 } |
177 } |
174 |
178 |
175 } |
179 } |
186 CleanupStack::Pop(); |
190 CleanupStack::Pop(); |
187 return pSelf; |
191 return pSelf; |
188 } |
192 } |
189 |
193 |
190 /** |
194 /** |
191 Insert an unlocked page into the last position of the locked queue, may squeeze the original last page into |
|
192 the unlocked queue. |
|
193 This function is used on last visited but 'unlocked' pages to avoid excessive lock/unlock calls to cache memory |
|
194 manager as contiguous entry reading/writing often happens on the same page. |
|
195 @param aPage the page to be inserted. |
|
196 @pre the page type of aPage should only be TDynamicDirCachePage::EUnknown |
|
197 */ |
|
198 void CDynamicDirCache::MakePageLastLocked(TDynamicDirCachePage* aPage) |
|
199 { |
|
200 // this function should not be called on active pages |
|
201 ASSERT(aPage->iType == TDynamicDirCachePage::EUnknown); |
|
202 |
|
203 if (iLockedQ.IsEmpty()) |
|
204 { |
|
205 // if locked queue is empty, add it onto the locked queue directly |
|
206 AddFirstOntoQueue(aPage, TDynamicDirCachePage::ELocked); |
|
207 } |
|
208 else |
|
209 { |
|
210 // otherwise, we squeeze for the last position on locked queue |
|
211 while (iLockedQCount + 1 >= iMinSizeInPages) |
|
212 { |
|
213 TDynamicDirCachePage* last = iLockedQ.Last(); |
|
214 DeQueue(last); |
|
215 UnlockPage(last); |
|
216 AddFirstOntoQueue(last, TDynamicDirCachePage::EUnlocked); |
|
217 } |
|
218 |
|
219 // iLockedQCount + 1 < iMinSizeInPages |
|
220 iLockedQ.AddLast(*aPage); |
|
221 aPage->SetPageType(TDynamicDirCachePage::ELocked); |
|
222 iLockedQCount++; |
|
223 } |
|
224 } |
|
225 |
|
226 /** |
|
227 Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page first. |
195 Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page first. |
228 The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor. |
196 The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor. |
229 |
197 |
230 @param aPos the starting position of the media address to be read. |
198 @param aPos the starting position of the media address to be read. |
231 @param aLength the length of the content to be read. |
199 @param aLength the length of the content to be read. |
243 { |
211 { |
244 // read data and append them to the descriptor |
212 // read data and append them to the descriptor |
245 aDes.Append(pPage->PtrInPage(aPos), aLength); |
213 aDes.Append(pPage->PtrInPage(aPos), aLength); |
246 |
214 |
247 |
215 |
248 // if page is from unlocked queue, insert it onto the last page of the locked |
216 // if page is from unlocked queue, try to keep it locked until we move to a |
249 // queue. this is to avoid excessive locking and unlocking operations that is |
217 // different page from the unlocked queue |
250 // highly likely to happen for following reads. |
218 // this is to avoid excessive locking and unlocking operations that is |
251 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) |
219 // highly likely to happen when DoFindL() linearly scan through the directory |
252 { |
220 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked |
253 DeQueue(pPage); |
221 && iLastVisitedPage != pPage) |
254 MakePageLastLocked(pPage); |
222 { |
|
223 // Note: iLastVisitedPage may have been moved from unlocked queue to locked queue |
|
224 if(iLastVisitedPage && iLastVisitedPage->PageType() == TDynamicDirCachePage::EUnlocked) |
|
225 { |
|
226 User::LeaveIfError(UnlockPage(iLastVisitedPage)); |
|
227 } |
|
228 iLastVisitedPage = pPage; |
255 } |
229 } |
256 } |
230 } |
257 else // page locking failed |
231 else // page locking failed |
258 { |
232 { |
259 ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); |
233 ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); |
678 |
661 |
679 void CDynamicDirCache::DoMakePageMRU(TInt64 aPos) |
662 void CDynamicDirCache::DoMakePageMRU(TInt64 aPos) |
680 { |
663 { |
681 // __PRINT1(_L("MakePageMRU (%lx)"), aPos); |
664 // __PRINT1(_L("MakePageMRU (%lx)"), aPos); |
682 // __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); |
665 // __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); |
683 // check there are at least two locked pages |
666 // check there are at least one locked pages |
684 ASSERT(iLockedQCount > 1); |
667 ASSERT(iLockedQCount > 0); |
685 |
668 |
686 // check the MRU page first, if it is already the MRU page, we can return immediately |
669 // check the MRU page first, if it is already the MRU page, we can return immediately |
687 TInt64 pageStartMedPos = CalcPageStartPos(aPos); |
670 TInt64 pageStartMedPos = CalcPageStartPos(aPos); |
688 if (!iLockedQ.IsEmpty()) |
671 if (!iLockedQ.IsEmpty()) |
689 { |
672 { |
690 if (iLockedQ.First()->StartPos() == pageStartMedPos) |
673 if (iLockedQCount > 1 && iLockedQ.First()->StartPos() == pageStartMedPos) |
691 { |
674 { |
692 return; |
675 return; |
693 } |
676 } |
694 } |
677 } |
695 |
678 |
696 TDynamicDirCachePage* pPage = FindPageByPos(aPos); |
679 TDynamicDirCachePage* pPage = FindPageByPos(aPos); |
697 if (pPage) |
680 if (pPage) |
698 { |
681 { |
699 ASSERT(pPage->IsValid()); |
682 ASSERT(pPage->IsValid()); |
700 // lock page before make it MRU |
683 // lock page before make it MRU |
701 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) |
684 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) |
702 { |
685 { |
703 ASSERT(!pPage->IsLocked()); |
686 ASSERT(!pPage->IsLocked() || (pPage->IsLocked() && pPage == iLastVisitedPage)); |
704 if (LockPage(pPage) == NULL) |
687 if (LockPage(pPage) == NULL) |
705 { |
688 { |
706 DeQueue(pPage); |
689 DeQueue(pPage); |
707 LookupTblRemove(pPage->StartPos()); |
690 LookupTblRemove(pPage->StartPos()); |
708 DecommitPage(pPage); |
691 DecommitPage(pPage); |
709 delete pPage; |
692 delete pPage; |
710 pPage = NULL; |
693 pPage = NULL; |
711 } |
694 } |
712 } |
695 } |
713 else |
696 else |
714 { |
697 { |
715 // error checking: page should either be locked or active |
698 // error checking: page should either be locked or active |
716 ASSERT(LockPage(pPage) != NULL); |
699 ASSERT(LockPage(pPage) != NULL); |
717 } |
700 } |
718 } |
701 } |
719 |
702 |
720 // if page not found or page data not valid anymore, use active page to read data |
703 // if page not found or page data not valid anymore, use active page to read data |
721 if (!pPage) |
704 if (!pPage) |
722 { |
705 { |
723 TRAPD(err, pPage = UpdateActivePageL(aPos)); |
706 TRAPD(err, pPage = UpdateActivePageL(aPos)); |
724 if (err != KErrNone) |
707 if (err != KErrNone) |
725 { |
708 { |
726 // problem occurred reading active page, return immediately. |
709 // problem occurred reading active page, return immediately. |
727 return; |
710 return; |
728 } |
711 } |
729 } |
712 } |
730 |
713 |
731 // by now, the page is either locked or active page |
714 // by now, the page is either locked or active page |
732 ASSERT(pPage && pPage->IsValid() && pPage->IsLocked()); |
715 ASSERT(pPage && pPage->IsValid() && pPage->IsLocked()); |
733 |
716 |
734 |
717 |
735 |
718 |
736 TBool allocateNewPage = pPage == iLockedQ.Last() && !CacheIsFull(); |
719 TBool makeNewPageMRU = pPage == iLockedQ.Last(); |
737 |
720 |
738 |
721 |
739 switch (pPage->PageType()) |
722 switch (pPage->PageType()) |
740 { |
723 { |
741 case TDynamicDirCachePage::EUnlocked: |
724 case TDynamicDirCachePage::EUnlocked: |
760 } |
743 } |
761 default: |
744 default: |
762 ASSERT(0); |
745 ASSERT(0); |
763 } |
746 } |
764 |
747 |
765 if (allocateNewPage) |
748 if (!makeNewPageMRU) |
766 { |
749 return; |
767 TDynamicDirCachePage* nPage = NULL; |
750 |
768 TRAPD(err, nPage = AllocateAndLockNewPageL(0)); |
751 // when cache is full and a new MRU page is about to be added, we will need to evict the LRU page |
769 if (err == KErrNone) |
752 // accordingly |
770 { |
753 if (CacheIsFull()) |
771 |
754 { |
772 // about to add a page to end of locked queue, so lie about iLockedQCount |
755 TUint32& queueCnt = iMaxSizeInPages - iMinSizeInPages > 0 ? iUnlockedQCount : iLockedQCount; |
773 iLockedQCount++; |
756 queueCnt++; |
774 CheckThresholds(); |
757 CheckThresholds(); |
775 iLockedQCount--; |
758 queueCnt--; |
776 |
759 } |
777 iLockedQ.AddLast(*nPage); |
760 |
778 nPage->SetPageType(TDynamicDirCachePage::ELocked); |
761 // attempt to grow the cache by appending a clean, new page at the end of the locked page queue. |
779 ++iLockedQCount; |
762 // This can fail when out of memory; the LRU mechanism then makes sure the oldest page will be re-used. |
780 LookupTblAdd(nPage); |
763 TDynamicDirCachePage* nPage = AllocateAndLockNewPage(); |
781 } |
764 if (!nPage) |
782 } |
765 return; |
|
766 |
|
767 // about to add the new active page, force the locked queue to evict the existing last page to make room |
|
768 // for the new active page |
|
769 iLockedQCount++; |
|
770 CheckThresholds(); |
|
771 iLockedQCount--; |
|
772 |
|
773 iLockedQ.AddLast(*nPage); |
|
774 nPage->SetPageType(TDynamicDirCachePage::ELocked); |
|
775 ++iLockedQCount; |
783 } |
776 } |
784 |
777 |
785 /** |
778 /** |
786 Implementation of pure virtual function. |
779 Implementation of pure virtual function. |
787 @see MDiskSpecialAccessor::MakePageMRU() |
780 @see MDiskSpecialAccessor::MakePageMRU() |
893 |
886 |
894 /** |
887 /** |
895 Try to create a new page and lock the page content when it is created. This function should only be called |
888 Try to create a new page and lock the page content when it is created. This function should only be called |
896 when creating iActive page or making a page MRU (which might result in page evictions). |
889 when creating iActive page or making a page MRU (which might result in page evictions). |
897 @return the pointer of the newly created page, or NULL if allocation failed. |
890 @return the pointer of the newly created page, or NULL if allocation failed. |
898 @param aStartMedPos the starting media address of the page to be created. |
891 */ |
899 @pre aStartMedPos should not already be existing in the cache. |
892 TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPage(/*TInt64 aStartMedPos*/) |
900 */ |
893 { |
901 TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPageL(TInt64 aStartMedPos) |
894 __PRINT(_L("CDynamicDirCache::AllocateAndLockNewPage()")); |
902 { |
895 |
903 __PRINT1(_L("CDynamicDirCache::AllocateAndLockNewPageL(aStartMedPos=%lx)"), aStartMedPos); |
896 TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); |
904 |
897 |
905 TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); |
898 if (!startRamAddr) |
906 if (startRamAddr) |
899 return NULL; |
907 { |
900 |
908 // create new page and return |
901 TDynamicDirCachePage* pPage = TDynamicDirCachePage::CreateCachePage(this, 0, startRamAddr); |
909 TDynamicDirCachePage* pPage = TDynamicDirCachePage::NewL(this, aStartMedPos, startRamAddr); |
902 |
910 pPage->SetLocked(ETrue); |
903 // Failure would mean the cache chunk was able to grow but we've run out of heap. |
911 pPage->SetValid(EFalse); |
904 // This seems extremely unlikely, but decommit the now-unmanageable cache segment just in case. |
912 return pPage; |
905 if (!pPage) |
913 } |
906 { |
914 |
907 iCacheMemoryClient->DecommitSegments(startRamAddr, PageSizeInSegs()); |
915 return NULL; |
908 return NULL; |
916 } |
909 } |
917 |
910 |
918 #ifdef _DEBUG |
911 pPage->SetLocked(ETrue); |
|
912 pPage->SetValid(EFalse); |
|
913 return pPage; |
|
914 } |
|
915 |
|
916 #if defined(_DEBUG) || defined(_DEBUG_RELEASE) |
919 /** |
917 /** |
920 Dump cache information, only enabled in debug mode. |
918 Dump cache information, only enabled in debug mode. |
921 @see CDynamicDirCache::Control() |
919 @see CDynamicDirCache::Control() |
922 */ |
920 */ |
923 void CDynamicDirCache::Info() const |
921 void CDynamicDirCache::Info(TDirCacheInfo* aInfo) const |
924 { |
922 { |
925 __PRINT(_L("======== CDynamicDirCache::Info =========")); |
923 __PRINT(_L("======== CDynamicDirCache::Info =========")); |
926 const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); |
924 const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); |
927 // page size |
925 const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; |
928 __PRINT1(_L("=== Pages size: [%d Bytes]"), iPageSizeInBytes); |
926 // page size |
929 __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); |
927 __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); |
930 |
928 __PRINT1(_L("=== Page data size: [%d Bytes]"), iPageSizeInBytes); |
931 // data size: |
929 __PRINT1(_L("=== Page mem size: [%d Bytes]"), 1 << pageMemSizeLog2); |
932 __PRINT1(_L("=== Min data size: [%d Bytes]"), iMinSizeInPages << iPageSizeLog2); |
930 |
933 __PRINT1(_L("=== Max data size: [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2); |
931 // cache size in pages |
934 |
932 __PRINT1(_L("=== Min cache size in pages: [%d]"), iMinSizeInPages); |
935 // memory size: |
933 __PRINT1(_L("=== Max cache size in pages: [%d]"), iMaxSizeInPages); |
936 const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; |
934 |
937 __PRINT1(_L("=== Min memory size: [%d Bytes]"), iMinSizeInPages << pageMemSizeLog2); |
935 // locked page num |
938 __PRINT1(_L("=== Max memory size: [%d Bytes]"), iMaxSizeInPages << pageMemSizeLog2); |
936 __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); |
939 |
937 // unlocked page num |
940 // reserved pages |
938 __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); |
941 __PRINT1(_L("=== Number of pages reserved: [%d]"), iMinSizeInPages); |
939 __PRINT(_L("=========================================\n")); |
942 __PRINT1(_L("=== Reserved memory: [%d Bytes]"), (iMinSizeInPages * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
940 |
943 // locked page num |
941 ASSERT(aInfo); |
944 __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); |
942 aInfo->iMemorySegmentSize = 1 << SegmentSizeInBytesLog2; |
945 __PRINT1(_L("=== Locked memory: [%d Bytes]"), (iLockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
943 aInfo->iPageSizeInMemory = PageSizeInSegs() << SegmentSizeInBytesLog2; |
946 // unlocked page num |
944 aInfo->iPageSizeInData = iPageSizeInBytes; |
947 __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); |
945 aInfo->iMinCacheSizeInPages = iMinSizeInPages; |
948 __PRINT1(_L("=== Unlocked memory: [%d Bytes]"), (iUnlockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
946 aInfo->iMaxCacheSizeInPages = iMaxSizeInPages; |
949 } |
947 aInfo->iMinCacheSizeInMemory = iMinSizeInPages * aInfo->iPageSizeInMemory; |
|
948 aInfo->iMaxCacheSizeInMemory = iMaxSizeInPages * aInfo->iPageSizeInMemory; |
|
949 aInfo->iLockedPageNumber = iLockedQCount; |
|
950 aInfo->iUnlockedPageNumber = iUnlockedQCount; |
|
951 } |
950 |
952 |
951 /** |
953 /** |
952 Dump cache content, only enabled in debug mode. |
954 Dump cache content, only enabled in debug mode. |
953 @see CDynamicDirCache::Control() |
955 @see CDynamicDirCache::Control() |
954 */ |
956 */ |
1026 */ |
1028 */ |
1027 TInt CDynamicDirCache::UnlockPage(TDynamicDirCachePage* aPage) |
1029 TInt CDynamicDirCache::UnlockPage(TDynamicDirCachePage* aPage) |
1028 { |
1030 { |
1029 ASSERT(aPage != NULL); |
1031 ASSERT(aPage != NULL); |
1030 __PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos()); |
1032 __PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos()); |
1031 TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs()); |
1033 if (aPage) |
1032 if (r == KErrNone) |
1034 { |
1033 { |
1035 TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs()); |
1034 aPage->SetLocked(EFalse); |
1036 if (r == KErrNone) |
1035 } |
1037 { |
1036 return r; |
1038 aPage->SetLocked(EFalse); |
1037 } |
1039 } |
1038 |
1040 return r; |
|
1041 } |
|
1042 return KErrArgument; |
|
1043 } |
1039 /** |
1044 /** |
1040 Decommit a locked or unlocked page. |
1045 Decommit a locked or unlocked page. |
1041 @return TInt KErrNone if decommition was successful, otherwise system-wide error code. |
1046 @return TInt KErrNone if decommition was successful, otherwise system-wide error code. |
1042 @param aPage the pointer of the page to be decommitted. |
1047 @param aPage the pointer of the page to be decommitted. |
1043 */ |
1048 */ |