1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CacheFileContextEvictor.h"
7 #include "CacheFileIOManager.h"
8 #include "CacheIndex.h"
9 #include "CacheIndexIterator.h"
10 #include "CacheFileUtils.h"
12 #include "LoadContextInfo.h"
13 #include "nsThreadUtils.h"
15 #include "nsISimpleEnumerator.h"
16 #include "nsIDirectoryEnumerator.h"
17 #include "mozilla/Base64.h"
23 const char kContextEvictionPrefix
[] = "ce_";
24 const uint32_t kContextEvictionPrefixLength
=
25 sizeof(kContextEvictionPrefix
) - 1;
27 bool CacheFileContextEvictor::sDiskAlreadySearched
= false;
29 CacheFileContextEvictor::CacheFileContextEvictor()
31 , mIndexIsUpToDate(false)
33 LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this));
36 CacheFileContextEvictor::~CacheFileContextEvictor()
38 LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this));
42 CacheFileContextEvictor::Init(nsIFile
*aCacheDirectory
)
44 LOG(("CacheFileContextEvictor::Init()"));
48 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
50 CacheIndex::IsUpToDate(&mIndexIsUpToDate
);
52 mCacheDirectory
= aCacheDirectory
;
54 rv
= aCacheDirectory
->Clone(getter_AddRefs(mEntriesDir
));
55 if (NS_WARN_IF(NS_FAILED(rv
))) {
59 rv
= mEntriesDir
->AppendNative(NS_LITERAL_CSTRING(kEntriesDir
));
60 if (NS_WARN_IF(NS_FAILED(rv
))) {
64 if (!sDiskAlreadySearched
) {
65 LoadEvictInfoFromDisk();
66 if ((mEntries
.Length() != 0) && mIndexIsUpToDate
) {
76 CacheFileContextEvictor::ContextsCount()
78 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
80 return mEntries
.Length();
84 CacheFileContextEvictor::AddContext(nsILoadContextInfo
*aLoadContextInfo
)
86 LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p]",
87 this, aLoadContextInfo
));
91 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
93 CacheFileContextEvictorEntry
*entry
= nullptr;
94 for (uint32_t i
= 0; i
< mEntries
.Length(); ++i
) {
95 if (mEntries
[i
]->mInfo
->Equals(aLoadContextInfo
)) {
102 entry
= new CacheFileContextEvictorEntry();
103 entry
->mInfo
= aLoadContextInfo
;
104 mEntries
.AppendElement(entry
);
107 entry
->mTimeStamp
= PR_Now() / PR_USEC_PER_MSEC
;
109 PersistEvictionInfoToDisk(aLoadContextInfo
);
111 if (mIndexIsUpToDate
) {
112 // Already existing context could be added again, in this case the iterator
113 // would be recreated. Close the old iterator explicitely.
114 if (entry
->mIterator
) {
115 entry
->mIterator
->Close();
116 entry
->mIterator
= nullptr;
119 rv
= CacheIndex::GetIterator(aLoadContextInfo
, false,
120 getter_AddRefs(entry
->mIterator
));
122 // This could probably happen during shutdown. Remove the entry from
123 // the array, but leave the info on the disk. No entry can be opened
124 // during shutdown and we'll load the eviction info on next start.
125 LOG(("CacheFileContextEvictor::AddContext() - Cannot get an iterator. "
127 mEntries
.RemoveElement(entry
);
138 CacheFileContextEvictor::CacheIndexStateChanged()
140 LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this));
142 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
144 bool isUpToDate
= false;
145 CacheIndex::IsUpToDate(&isUpToDate
);
146 if (mEntries
.Length() == 0) {
147 // Just save the state and exit, since there is nothing to do
148 mIndexIsUpToDate
= isUpToDate
;
152 if (!isUpToDate
&& !mIndexIsUpToDate
) {
153 // Index is outdated and status has not changed, nothing to do.
157 if (isUpToDate
&& mIndexIsUpToDate
) {
158 // Status has not changed, but make sure the eviction is running.
163 // We're not evicting, but we should be evicting?!
164 LOG(("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to "
165 "date, we have some context to evict but eviction is not running! "
169 mIndexIsUpToDate
= isUpToDate
;
171 if (mIndexIsUpToDate
) {
182 CacheFileContextEvictor::WasEvicted(const nsACString
&aKey
, nsIFile
*aFile
,
185 LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
186 PromiseFlatCString(aKey
).get()));
190 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
192 nsCOMPtr
<nsILoadContextInfo
> info
= CacheFileUtils::ParseKey(aKey
);
195 LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
200 CacheFileContextEvictorEntry
*entry
= nullptr;
201 for (uint32_t i
= 0; i
< mEntries
.Length(); ++i
) {
202 if (info
->Equals(mEntries
[i
]->mInfo
)) {
209 LOG(("CacheFileContextEvictor::WasEvicted() - Didn't find equal context, "
210 "returning false."));
215 PRTime lastModifiedTime
;
216 rv
= aFile
->GetLastModifiedTime(&lastModifiedTime
);
218 LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
219 ", returning false."));
224 *_retval
= !(lastModifiedTime
> entry
->mTimeStamp
);
225 LOG(("CacheFileContextEvictor::WasEvicted() - returning %s. [mTimeStamp=%lld,"
226 " lastModifiedTime=%lld]", *_retval
? "true" : "false",
227 mEntries
[0]->mTimeStamp
, lastModifiedTime
));
233 CacheFileContextEvictor::PersistEvictionInfoToDisk(
234 nsILoadContextInfo
*aLoadContextInfo
)
236 LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
237 "loadContextInfo=%p]", this, aLoadContextInfo
));
241 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
243 nsCOMPtr
<nsIFile
> file
;
244 rv
= GetContextFile(aLoadContextInfo
, getter_AddRefs(file
));
245 if (NS_WARN_IF(NS_FAILED(rv
))) {
251 file
->GetNativePath(path
);
255 rv
= file
->OpenNSPRFileDesc(PR_RDWR
| PR_CREATE_FILE
| PR_TRUNCATE
, 0600,
257 if (NS_WARN_IF(NS_FAILED(rv
))) {
258 LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file "
259 "failed! [path=%s, rv=0x%08x]", path
.get(), rv
));
265 LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully "
266 "created file. [path=%s]", path
.get()));
272 CacheFileContextEvictor::RemoveEvictInfoFromDisk(
273 nsILoadContextInfo
*aLoadContextInfo
)
275 LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
276 "loadContextInfo=%p]", this, aLoadContextInfo
));
280 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
282 nsCOMPtr
<nsIFile
> file
;
283 rv
= GetContextFile(aLoadContextInfo
, getter_AddRefs(file
));
284 if (NS_WARN_IF(NS_FAILED(rv
))) {
290 file
->GetNativePath(path
);
293 rv
= file
->Remove(false);
294 if (NS_WARN_IF(NS_FAILED(rv
))) {
295 LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file"
296 " failed! [path=%s, rv=0x%08x]", path
.get(), rv
));
300 LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully "
301 "removed file. [path=%s]", path
.get()));
307 CacheFileContextEvictor::LoadEvictInfoFromDisk()
309 LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this));
313 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
315 sDiskAlreadySearched
= true;
317 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
318 rv
= mCacheDirectory
->GetDirectoryEntries(getter_AddRefs(enumerator
));
319 if (NS_WARN_IF(NS_FAILED(rv
))) {
323 nsCOMPtr
<nsIDirectoryEnumerator
> dirEnum
= do_QueryInterface(enumerator
, &rv
);
324 if (NS_WARN_IF(NS_FAILED(rv
))) {
329 nsCOMPtr
<nsIFile
> file
;
330 rv
= dirEnum
->GetNextFile(getter_AddRefs(file
));
336 file
->IsDirectory(&isDir
);
342 rv
= file
->GetNativeLeafName(leaf
);
344 LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - "
345 "GetNativeLeafName() failed! Skipping file."));
349 if (leaf
.Length() < kContextEvictionPrefixLength
) {
353 if (!StringBeginsWith(leaf
, NS_LITERAL_CSTRING(kContextEvictionPrefix
))) {
357 nsAutoCString encoded
;
358 encoded
= Substring(leaf
, kContextEvictionPrefixLength
);
359 encoded
.ReplaceChar('-', '/');
361 nsAutoCString decoded
;
362 rv
= Base64Decode(encoded
, decoded
);
364 LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding "
365 "failed. Removing the file. [file=%s]", leaf
.get()));
370 nsCOMPtr
<nsILoadContextInfo
> info
= CacheFileUtils::ParseKey(decoded
);
373 LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
374 "context key, removing file. [contextKey=%s, file=%s]",
375 decoded
.get(), leaf
.get()));
380 PRTime lastModifiedTime
;
381 rv
= file
->GetLastModifiedTime(&lastModifiedTime
);
386 CacheFileContextEvictorEntry
*entry
= new CacheFileContextEvictorEntry();
388 entry
->mTimeStamp
= lastModifiedTime
;
389 mEntries
.AppendElement(entry
);
396 CacheFileContextEvictor::GetContextFile(nsILoadContextInfo
*aLoadContextInfo
,
401 nsAutoCString leafName
;
402 leafName
.Assign(NS_LITERAL_CSTRING(kContextEvictionPrefix
));
404 nsAutoCString keyPrefix
;
405 CacheFileUtils::AppendKeyPrefix(aLoadContextInfo
, keyPrefix
);
407 nsAutoCString data64
;
408 rv
= Base64Encode(keyPrefix
, data64
);
409 if (NS_WARN_IF(NS_FAILED(rv
))) {
413 // Replace '/' with '-' since '/' cannot be part of the filename.
414 data64
.ReplaceChar('/', '-');
416 leafName
.Append(data64
);
418 nsCOMPtr
<nsIFile
> file
;
419 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
420 if (NS_WARN_IF(NS_FAILED(rv
))) {
424 rv
= file
->AppendNative(leafName
);
425 if (NS_WARN_IF(NS_FAILED(rv
))) {
434 CacheFileContextEvictor::CreateIterators()
436 LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this));
442 for (uint32_t i
= 0; i
< mEntries
.Length(); ) {
443 rv
= CacheIndex::GetIterator(mEntries
[i
]->mInfo
, false,
444 getter_AddRefs(mEntries
[i
]->mIterator
));
446 LOG(("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator"
447 ". [rv=0x%08x]", rv
));
448 mEntries
.RemoveElementAt(i
);
457 CacheFileContextEvictor::CloseIterators()
459 LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this));
461 for (uint32_t i
= 0; i
< mEntries
.Length(); ++i
) {
462 if (mEntries
[i
]->mIterator
) {
463 mEntries
[i
]->mIterator
->Close();
464 mEntries
[i
]->mIterator
= nullptr;
470 CacheFileContextEvictor::StartEvicting()
472 LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this));
474 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
477 LOG(("CacheFileContextEvictor::StartEvicting() - already evicintg."));
481 if (mEntries
.Length() == 0) {
482 LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict."));
486 nsCOMPtr
<nsIRunnable
> ev
;
487 ev
= NS_NewRunnableMethod(this, &CacheFileContextEvictor::EvictEntries
);
489 nsRefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
491 nsresult rv
= ioThread
->Dispatch(ev
, CacheIOThread::EVICT
);
493 LOG(("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to "
494 "IO thread. [rv=0x%08x]", rv
));
501 CacheFileContextEvictor::EvictEntries()
503 LOG(("CacheFileContextEvictor::EvictEntries()"));
507 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
511 if (!mIndexIsUpToDate
) {
512 LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
518 if (CacheIOThread::YieldAndRerun()) {
519 LOG(("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher "
525 if (mEntries
.Length() == 0) {
526 LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there "
527 "is no context to evict."));
532 rv
= mEntries
[0]->mIterator
->GetNextHash(&hash
);
533 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
534 LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
535 "iterator. [iterator=%p, info=%p]", mEntries
[0]->mIterator
.get(),
536 mEntries
[0]->mInfo
.get()));
537 RemoveEvictInfoFromDisk(mEntries
[0]->mInfo
);
538 mEntries
.RemoveElementAt(0);
540 } else if (NS_FAILED(rv
)) {
541 LOG(("CacheFileContextEvictor::EvictEntries() - Iterator failed to "
542 "provide next hash (shutdown?), keeping eviction info on disk."
543 " [iterator=%p, info=%p]", mEntries
[0]->mIterator
.get(),
544 mEntries
[0]->mInfo
.get()));
545 mEntries
.RemoveElementAt(0);
549 LOG(("CacheFileContextEvictor::EvictEntries() - Processing hash. "
550 "[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]", LOGSHA1(&hash
),
551 mEntries
[0]->mIterator
.get(), mEntries
[0]->mInfo
.get()));
553 nsRefPtr
<CacheFileHandle
> handle
;
554 CacheFileIOManager::gInstance
->mHandles
.GetHandle(&hash
, false,
555 getter_AddRefs(handle
));
557 // We doom any active handle in CacheFileIOManager::EvictByContext(), so
558 // this must be a new one. Skip it.
559 LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
560 "found an active handle. [handle=%p]", handle
.get()));
564 nsAutoCString leafName
;
565 CacheFileIOManager::HashToStr(&hash
, leafName
);
567 PRTime lastModifiedTime
;
568 nsCOMPtr
<nsIFile
> file
;
569 rv
= mEntriesDir
->Clone(getter_AddRefs(file
));
570 if (NS_SUCCEEDED(rv
)) {
571 rv
= file
->AppendNative(leafName
);
573 if (NS_SUCCEEDED(rv
)) {
574 rv
= file
->GetLastModifiedTime(&lastModifiedTime
);
577 LOG(("CacheFileContextEvictor::EvictEntries() - Cannot get last modified "
578 "time, skipping entry."));
582 if (lastModifiedTime
> mEntries
[0]->mTimeStamp
) {
583 LOG(("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. "
584 "[mTimeStamp=%lld, lastModifiedTime=%lld]", mEntries
[0]->mTimeStamp
,
589 LOG(("CacheFileContextEvictor::EvictEntries - Removing entry."));
591 CacheIndex::RemoveEntry(&hash
);
594 NS_NOTREACHED("We should never get here");