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 "CacheFileIOManager.h"
8 #include "../cache/nsCacheUtils.h"
9 #include "CacheHashUtils.h"
10 #include "CacheStorageService.h"
11 #include "CacheIndex.h"
12 #include "CacheFileUtils.h"
13 #include "nsThreadUtils.h"
14 #include "CacheFile.h"
15 #include "CacheObserver.h"
17 #include "CacheFileContextEvictor.h"
19 #include "nsISimpleEnumerator.h"
20 #include "nsIDirectoryEnumerator.h"
21 #include "nsIObserverService.h"
22 #include "nsICacheStorageVisitor.h"
23 #include "nsISizeOf.h"
24 #include "mozilla/Telemetry.h"
25 #include "mozilla/DebugOnly.h"
26 #include "mozilla/Services.h"
27 #include "nsDirectoryServiceUtils.h"
28 #include "nsAppDirectoryServiceDefs.h"
29 #include "private/pprio.h"
30 #include "mozilla/VisualEventTracer.h"
31 #include "mozilla/Preferences.h"
32 #include "nsNetUtil.h"
34 // include files for ftruncate (or equivalent)
42 // XXX add necessary include file for ftruncate (or equivalent)
49 #define kOpenHandlesLimit 64
50 #define kMetadataWriteDelay 5000
51 #define kRemoveTrashStartDelay 60000 // in milliseconds
52 #define kSmartSizeUpdateInterval 60000 // in milliseconds
55 const uint32_t kMaxCacheSizeKB
= 200*1024; // 200 MB
57 const uint32_t kMaxCacheSizeKB
= 350*1024; // 350 MB
61 CacheFileHandle::DispatchRelease()
63 if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
67 nsCOMPtr
<nsIEventTarget
> ioTarget
= CacheFileIOManager::IOTarget();
72 nsRefPtr
<nsRunnableMethod
<CacheFileHandle
, MozExternalRefCountType
, false> > event
=
73 NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release
);
74 nsresult rv
= ioTarget
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
82 NS_IMPL_ADDREF(CacheFileHandle
)
83 NS_IMETHODIMP_(MozExternalRefCountType
)
84 CacheFileHandle::Release()
86 nsrefcnt count
= mRefCnt
- 1;
87 if (DispatchRelease()) {
88 // Redispatched to the IO thread.
92 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
94 LOG(("CacheFileHandle::Release() [this=%p, refcnt=%d]", this, mRefCnt
.get()));
95 NS_PRECONDITION(0 != mRefCnt
, "dup release");
97 NS_LOG_RELEASE(this, count
, "CacheFileHandle");
108 NS_INTERFACE_MAP_BEGIN(CacheFileHandle
)
109 NS_INTERFACE_MAP_ENTRY(nsISupports
)
110 NS_INTERFACE_MAP_END_THREADSAFE
112 CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash
*aHash
, bool aPriority
)
115 , mPriority(aPriority
)
117 , mSpecialFile(false)
123 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
124 , this, LOGSHA1(aHash
)));
127 CacheFileHandle::CacheFileHandle(const nsACString
&aKey
, bool aPriority
)
130 , mPriority(aPriority
)
139 LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
140 PromiseFlatCString(aKey
).get()));
143 CacheFileHandle::~CacheFileHandle()
145 LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
147 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
149 nsRefPtr
<CacheFileIOManager
> ioMan
= CacheFileIOManager::gInstance
;
150 if (!IsClosed() && ioMan
) {
151 ioMan
->CloseHandleInternal(this);
156 CacheFileHandle::Log()
158 nsAutoCString leafName
;
160 mFile
->GetNativeLeafName(leafName
);
164 LOG(("CacheFileHandle::Log() - special file [this=%p, isDoomed=%d, "
165 "priority=%d, closed=%d, invalid=%d, fileExists=%d, fileSize=%lld, "
166 "leafName=%s, key=%s]", this, mIsDoomed
, mPriority
, mClosed
, mInvalid
,
167 mFileExists
, mFileSize
, leafName
.get(), mKey
.get()));
169 LOG(("CacheFileHandle::Log() - entry file [this=%p, hash=%08x%08x%08x%08x"
170 "%08x, isDoomed=%d, priority=%d, closed=%d, invalid=%d, fileExists=%d,"
171 " fileSize=%lld, leafName=%s, key=%s]", this, LOGSHA1(mHash
),
172 mIsDoomed
, mPriority
, mClosed
, mInvalid
, mFileExists
, mFileSize
,
173 leafName
.get(), mKey
.get()));
178 CacheFileHandle::FileSizeInK() const
180 MOZ_ASSERT(mFileSize
!= -1);
181 uint64_t size64
= mFileSize
;
188 NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
189 "truncating to PR_UINT32_MAX");
190 size
= PR_UINT32_MAX
;
192 size
= static_cast<uint32_t>(size64
);
201 CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
204 nsCOMPtr
<nsISizeOf
> sizeOf
;
206 sizeOf
= do_QueryInterface(mFile
);
208 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
211 n
+= mallocSizeOf(mFD
);
212 n
+= mKey
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
217 CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
219 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);
222 /******************************************************************************
223 * CacheFileHandles::HandleHashKey
224 *****************************************************************************/
227 CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle
* aHandle
)
229 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
231 mHandles
.InsertElementAt(0, aHandle
);
235 CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle
* aHandle
)
237 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
239 DebugOnly
<bool> found
;
240 found
= mHandles
.RemoveElement(aHandle
);
244 already_AddRefed
<CacheFileHandle
>
245 CacheFileHandles::HandleHashKey::GetNewestHandle()
247 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
249 nsRefPtr
<CacheFileHandle
> handle
;
250 if (mHandles
.Length()) {
251 handle
= mHandles
[0];
254 return handle
.forget();
258 CacheFileHandles::HandleHashKey::GetHandles(nsTArray
<nsRefPtr
<CacheFileHandle
> > &aResult
)
260 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
262 for (uint32_t i
= 0; i
< mHandles
.Length(); ++i
) {
263 CacheFileHandle
* handle
= mHandles
[i
];
264 aResult
.AppendElement(handle
);
271 CacheFileHandles::HandleHashKey::AssertHandlesState()
273 for (uint32_t i
= 0; i
< mHandles
.Length(); ++i
) {
274 CacheFileHandle
* handle
= mHandles
[i
];
275 MOZ_ASSERT(handle
->IsDoomed());
282 CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
284 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
287 n
+= mallocSizeOf(mHash
);
288 for (uint32_t i
= 0; i
< mHandles
.Length(); ++i
) {
289 n
+= mHandles
[i
]->SizeOfIncludingThis(mallocSizeOf
);
295 /******************************************************************************
297 *****************************************************************************/
299 CacheFileHandles::CacheFileHandles()
301 LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
302 MOZ_COUNT_CTOR(CacheFileHandles
);
305 CacheFileHandles::~CacheFileHandles()
307 LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
308 MOZ_COUNT_DTOR(CacheFileHandles
);
312 CacheFileHandles::GetHandle(const SHA1Sum::Hash
*aHash
,
314 CacheFileHandle
**_retval
)
316 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
320 LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
324 // find hash entry for key
325 HandleHashKey
*entry
= mTable
.GetEntry(*aHash
);
327 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
328 "no handle entries found", LOGSHA1(aHash
)));
329 return NS_ERROR_NOT_AVAILABLE
;
336 // Check if the entry is doomed
337 nsRefPtr
<CacheFileHandle
> handle
= entry
->GetNewestHandle();
339 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
340 "no handle found %p, entry %p", LOGSHA1(aHash
), handle
.get(), entry
));
341 return NS_ERROR_NOT_AVAILABLE
;
344 if (handle
->IsDoomed()) {
345 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
346 "found doomed handle %p, entry %p", LOGSHA1(aHash
), handle
.get(), entry
));
348 // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
349 if (!aReturnDoomed
) {
350 return NS_ERROR_NOT_AVAILABLE
;
353 LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
354 "found handle %p, entry %p", LOGSHA1(aHash
), handle
.get(), entry
));
357 handle
.forget(_retval
);
363 CacheFileHandles::NewHandle(const SHA1Sum::Hash
*aHash
,
365 CacheFileHandle
**_retval
)
367 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
371 LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash
)));
374 // find hash entry for key
375 HandleHashKey
*entry
= mTable
.PutEntry(*aHash
);
382 entry
->AssertHandlesState();
385 nsRefPtr
<CacheFileHandle
> handle
= new CacheFileHandle(entry
->Hash(), aPriority
);
386 entry
->AddHandle(handle
);
388 LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
389 "created new handle %p, entry=%p", LOGSHA1(aHash
), handle
.get(), entry
));
391 handle
.forget(_retval
);
396 CacheFileHandles::RemoveHandle(CacheFileHandle
*aHandle
)
398 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
406 LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
407 , aHandle
, LOGSHA1(aHandle
->Hash())));
410 // find hash entry for key
411 HandleHashKey
*entry
= mTable
.GetEntry(*aHandle
->Hash());
413 MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
414 "Should find entry when removing a handle before shutdown");
416 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
417 "no entries found", LOGSHA1(aHandle
->Hash())));
425 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
426 "removing handle %p", LOGSHA1(entry
->Hash()), aHandle
));
427 entry
->RemoveHandle(aHandle
);
429 if (entry
->IsEmpty()) {
430 LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
431 "list is empty, removing entry %p", LOGSHA1(entry
->Hash()), entry
));
432 mTable
.RemoveEntry(*entry
->Hash());
436 static PLDHashOperator
437 GetAllHandlesEnum(CacheFileHandles::HandleHashKey
* aEntry
, void *aClosure
)
439 nsTArray
<nsRefPtr
<CacheFileHandle
> > *array
=
440 static_cast<nsTArray
<nsRefPtr
<CacheFileHandle
> > *>(aClosure
);
442 aEntry
->GetHandles(*array
);
443 return PL_DHASH_NEXT
;
447 CacheFileHandles::GetAllHandles(nsTArray
<nsRefPtr
<CacheFileHandle
> > *_retval
)
449 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
450 mTable
.EnumerateEntries(&GetAllHandlesEnum
, _retval
);
453 static PLDHashOperator
454 GetActiveHandlesEnum(CacheFileHandles::HandleHashKey
* aEntry
, void *aClosure
)
456 nsTArray
<nsRefPtr
<CacheFileHandle
> > *array
=
457 static_cast<nsTArray
<nsRefPtr
<CacheFileHandle
> > *>(aClosure
);
459 nsRefPtr
<CacheFileHandle
> handle
= aEntry
->GetNewestHandle();
462 if (!handle
->IsDoomed()) {
463 array
->AppendElement(handle
);
466 return PL_DHASH_NEXT
;
470 CacheFileHandles::GetActiveHandles(
471 nsTArray
<nsRefPtr
<CacheFileHandle
> > *_retval
)
473 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
474 mTable
.EnumerateEntries(&GetActiveHandlesEnum
, _retval
);
478 CacheFileHandles::ClearAll()
480 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
485 CacheFileHandles::HandleCount()
487 return mTable
.Count();
492 CacheFileHandles::Log(CacheFileHandlesEntry
*entry
)
494 LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry
));
496 nsTArray
<nsRefPtr
<CacheFileHandle
> > array
;
497 aEntry
->GetHandles(array
);
499 for (uint32_t i
= 0; i
< array
.Length(); ++i
) {
500 CacheFileHandle
*handle
= array
[i
];
504 LOG(("CacheFileHandles::Log() END [entry=%p]", entry
));
511 CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
513 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
515 return mTable
.SizeOfExcludingThis(mallocSizeOf
);
520 class ShutdownEvent
: public nsRunnable
{
522 ShutdownEvent(mozilla::Mutex
*aLock
, mozilla::CondVar
*aCondVar
)
526 MOZ_COUNT_CTOR(ShutdownEvent
);
532 MOZ_COUNT_DTOR(ShutdownEvent
);
538 MutexAutoLock
lock(*mLock
);
540 CacheFileIOManager::gInstance
->ShutdownInternal();
547 mozilla::Mutex
*mLock
;
548 mozilla::CondVar
*mCondVar
;
551 class OpenFileEvent
: public nsRunnable
{
553 OpenFileEvent(const nsACString
&aKey
, uint32_t aFlags
,
554 CacheFileIOListener
*aCallback
)
556 , mCallback(aCallback
)
559 MOZ_COUNT_CTOR(OpenFileEvent
);
560 mIOMan
= CacheFileIOManager::gInstance
;
562 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable
*>(this), aKey
.BeginReading());
563 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable
*>(this), "net::cache::open-background");
569 MOZ_COUNT_DTOR(OpenFileEvent
);
577 if (!(mFlags
& CacheFileIOManager::SPECIAL_FILE
)) {
579 sum
.update(mKey
.BeginReading(), mKey
.Length());
583 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::open-background");
585 rv
= NS_ERROR_NOT_INITIALIZED
;
587 if (mFlags
& CacheFileIOManager::SPECIAL_FILE
) {
588 rv
= mIOMan
->OpenSpecialFileInternal(mKey
, mFlags
,
589 getter_AddRefs(mHandle
));
591 rv
= mIOMan
->OpenFileInternal(&mHash
, mKey
, mFlags
,
592 getter_AddRefs(mHandle
));
596 MOZ_EVENT_TRACER_NAME_OBJECT(mHandle
.get(), mKey
.get());
597 if (mHandle
->Key().IsEmpty()) {
598 mHandle
->Key() = mKey
;
602 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::open-background");
604 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::open-result");
605 mCallback
->OnFileOpened(mHandle
, rv
);
606 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::open-result");
614 nsCOMPtr
<CacheFileIOListener
> mCallback
;
615 nsRefPtr
<CacheFileIOManager
> mIOMan
;
616 nsRefPtr
<CacheFileHandle
> mHandle
;
620 class ReadEvent
: public nsRunnable
{
622 ReadEvent(CacheFileHandle
*aHandle
, int64_t aOffset
, char *aBuf
,
623 int32_t aCount
, CacheFileIOListener
*aCallback
)
628 , mCallback(aCallback
)
630 MOZ_COUNT_CTOR(ReadEvent
);
632 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable
*>(this), aHandle
->Key().get());
633 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable
*>(this), "net::cache::read-background");
639 MOZ_COUNT_DTOR(ReadEvent
);
647 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::read-background");
648 if (mHandle
->IsClosed()) {
649 rv
= NS_ERROR_NOT_INITIALIZED
;
651 rv
= CacheFileIOManager::gInstance
->ReadInternal(
652 mHandle
, mOffset
, mBuf
, mCount
);
654 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::read-background");
656 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::read-result");
657 mCallback
->OnDataRead(mHandle
, mBuf
, rv
);
658 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::read-result");
664 nsRefPtr
<CacheFileHandle
> mHandle
;
668 nsCOMPtr
<CacheFileIOListener
> mCallback
;
671 class WriteEvent
: public nsRunnable
{
673 WriteEvent(CacheFileHandle
*aHandle
, int64_t aOffset
, const char *aBuf
,
674 int32_t aCount
, bool aValidate
, CacheFileIOListener
*aCallback
)
679 , mValidate(aValidate
)
680 , mCallback(aCallback
)
682 MOZ_COUNT_CTOR(WriteEvent
);
684 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable
*>(this), aHandle
->Key().get());
685 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable
*>(this), "net::cache::write-background");
691 MOZ_COUNT_DTOR(WriteEvent
);
693 if (!mCallback
&& mBuf
) {
694 free(const_cast<char *>(mBuf
));
703 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::write-background");
704 if (mHandle
->IsClosed()) {
705 rv
= NS_ERROR_NOT_INITIALIZED
;
707 rv
= CacheFileIOManager::gInstance
->WriteInternal(
708 mHandle
, mOffset
, mBuf
, mCount
, mValidate
);
710 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::write-background");
712 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::write-result");
714 mCallback
->OnDataWritten(mHandle
, mBuf
, rv
);
716 free(const_cast<char *>(mBuf
));
719 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::write-result");
725 nsRefPtr
<CacheFileHandle
> mHandle
;
730 nsCOMPtr
<CacheFileIOListener
> mCallback
;
733 class DoomFileEvent
: public nsRunnable
{
735 DoomFileEvent(CacheFileHandle
*aHandle
,
736 CacheFileIOListener
*aCallback
)
737 : mCallback(aCallback
)
740 MOZ_COUNT_CTOR(DoomFileEvent
);
742 MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable
*>(this), aHandle
->Key().get());
743 MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable
*>(this), "net::cache::doom-background");
749 MOZ_COUNT_DTOR(DoomFileEvent
);
757 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::doom-background");
758 if (mHandle
->IsClosed()) {
759 rv
= NS_ERROR_NOT_INITIALIZED
;
761 rv
= CacheFileIOManager::gInstance
->DoomFileInternal(mHandle
);
763 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::doom-background");
765 MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable
*>(this), "net::cache::doom-result");
767 mCallback
->OnFileDoomed(mHandle
, rv
);
769 MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable
*>(this), "net::cache::doom-result");
775 nsCOMPtr
<CacheFileIOListener
> mCallback
;
776 nsCOMPtr
<nsIEventTarget
> mTarget
;
777 nsRefPtr
<CacheFileHandle
> mHandle
;
780 class DoomFileByKeyEvent
: public nsRunnable
{
782 DoomFileByKeyEvent(const nsACString
&aKey
,
783 CacheFileIOListener
*aCallback
)
784 : mCallback(aCallback
)
786 MOZ_COUNT_CTOR(DoomFileByKeyEvent
);
789 sum
.update(aKey
.BeginReading(), aKey
.Length());
792 mIOMan
= CacheFileIOManager::gInstance
;
796 ~DoomFileByKeyEvent()
798 MOZ_COUNT_DTOR(DoomFileByKeyEvent
);
807 rv
= NS_ERROR_NOT_INITIALIZED
;
809 rv
= mIOMan
->DoomFileByKeyInternal(&mHash
, false);
814 mCallback
->OnFileDoomed(nullptr, rv
);
822 nsCOMPtr
<CacheFileIOListener
> mCallback
;
823 nsRefPtr
<CacheFileIOManager
> mIOMan
;
826 class ReleaseNSPRHandleEvent
: public nsRunnable
{
828 explicit ReleaseNSPRHandleEvent(CacheFileHandle
*aHandle
)
831 MOZ_COUNT_CTOR(ReleaseNSPRHandleEvent
);
835 ~ReleaseNSPRHandleEvent()
837 MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent
);
843 if (mHandle
->mFD
&& !mHandle
->IsClosed()) {
844 CacheFileIOManager::gInstance
->ReleaseNSPRHandleInternal(mHandle
);
851 nsRefPtr
<CacheFileHandle
> mHandle
;
854 class TruncateSeekSetEOFEvent
: public nsRunnable
{
856 TruncateSeekSetEOFEvent(CacheFileHandle
*aHandle
, int64_t aTruncatePos
,
857 int64_t aEOFPos
, CacheFileIOListener
*aCallback
)
859 , mTruncatePos(aTruncatePos
)
861 , mCallback(aCallback
)
863 MOZ_COUNT_CTOR(TruncateSeekSetEOFEvent
);
867 ~TruncateSeekSetEOFEvent()
869 MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent
);
877 if (mHandle
->IsClosed()) {
878 rv
= NS_ERROR_NOT_INITIALIZED
;
880 rv
= CacheFileIOManager::gInstance
->TruncateSeekSetEOFInternal(
881 mHandle
, mTruncatePos
, mEOFPos
);
885 mCallback
->OnEOFSet(mHandle
, rv
);
892 nsRefPtr
<CacheFileHandle
> mHandle
;
893 int64_t mTruncatePos
;
895 nsCOMPtr
<CacheFileIOListener
> mCallback
;
898 class RenameFileEvent
: public nsRunnable
{
900 RenameFileEvent(CacheFileHandle
*aHandle
, const nsACString
&aNewName
,
901 CacheFileIOListener
*aCallback
)
904 , mCallback(aCallback
)
906 MOZ_COUNT_CTOR(RenameFileEvent
);
912 MOZ_COUNT_DTOR(RenameFileEvent
);
920 if (mHandle
->IsClosed()) {
921 rv
= NS_ERROR_NOT_INITIALIZED
;
923 rv
= CacheFileIOManager::gInstance
->RenameFileInternal(mHandle
,
928 mCallback
->OnFileRenamed(mHandle
, rv
);
935 nsRefPtr
<CacheFileHandle
> mHandle
;
937 nsCOMPtr
<CacheFileIOListener
> mCallback
;
940 class InitIndexEntryEvent
: public nsRunnable
{
942 InitIndexEntryEvent(CacheFileHandle
*aHandle
, uint32_t aAppId
,
943 bool aAnonymous
, bool aInBrowser
)
946 , mAnonymous(aAnonymous
)
947 , mInBrowser(aInBrowser
)
949 MOZ_COUNT_CTOR(InitIndexEntryEvent
);
953 ~InitIndexEntryEvent()
955 MOZ_COUNT_DTOR(InitIndexEntryEvent
);
961 if (mHandle
->IsClosed() || mHandle
->IsDoomed()) {
965 CacheIndex::InitEntry(mHandle
->Hash(), mAppId
, mAnonymous
, mInBrowser
);
967 // We cannot set the filesize before we init the entry. If we're opening
968 // an existing entry file, frecency and expiration time will be set after
969 // parsing the entry file, but we must set the filesize here since nobody is
970 // going to set it if there is no write to the file.
971 uint32_t sizeInK
= mHandle
->FileSizeInK();
972 CacheIndex::UpdateEntry(mHandle
->Hash(), nullptr, nullptr, &sizeInK
);
978 nsRefPtr
<CacheFileHandle
> mHandle
;
984 class UpdateIndexEntryEvent
: public nsRunnable
{
986 UpdateIndexEntryEvent(CacheFileHandle
*aHandle
, const uint32_t *aFrecency
,
987 const uint32_t *aExpirationTime
)
989 , mHasFrecency(false)
990 , mHasExpirationTime(false)
992 MOZ_COUNT_CTOR(UpdateIndexEntryEvent
);
995 mFrecency
= *aFrecency
;
997 if (aExpirationTime
) {
998 mHasExpirationTime
= true;
999 mExpirationTime
= *aExpirationTime
;
1004 ~UpdateIndexEntryEvent()
1006 MOZ_COUNT_DTOR(UpdateIndexEntryEvent
);
1012 if (mHandle
->IsClosed() || mHandle
->IsDoomed()) {
1016 CacheIndex::UpdateEntry(mHandle
->Hash(),
1017 mHasFrecency
? &mFrecency
: nullptr,
1018 mHasExpirationTime
? &mExpirationTime
: nullptr,
1024 nsRefPtr
<CacheFileHandle
> mHandle
;
1026 bool mHasExpirationTime
;
1028 uint32_t mExpirationTime
;
1031 class MetadataWriteScheduleEvent
: public nsRunnable
1040 nsRefPtr
<CacheFile
> mFile
;
1041 nsRefPtr
<CacheFileIOManager
> mIOMan
;
1043 MetadataWriteScheduleEvent(CacheFileIOManager
* aManager
,
1051 virtual ~MetadataWriteScheduleEvent() { }
1055 nsRefPtr
<CacheFileIOManager
> ioMan
= CacheFileIOManager::gInstance
;
1057 NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
1064 ioMan
->ScheduleMetadataWriteInternal(mFile
);
1067 ioMan
->UnscheduleMetadataWriteInternal(mFile
);
1070 ioMan
->ShutdownMetadataWriteSchedulingInternal();
1077 CacheFileIOManager
* CacheFileIOManager::gInstance
= nullptr;
1079 NS_IMPL_ISUPPORTS(CacheFileIOManager
, nsITimerCallback
)
1081 CacheFileIOManager::CacheFileIOManager()
1082 : mShuttingDown(false)
1083 , mTreeCreated(false)
1084 , mOverLimitEvicting(false)
1085 , mRemovingTrashDirs(false)
1087 LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
1088 MOZ_COUNT_CTOR(CacheFileIOManager
);
1089 MOZ_ASSERT(!gInstance
, "multiple CacheFileIOManager instances!");
1092 CacheFileIOManager::~CacheFileIOManager()
1094 LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
1095 MOZ_COUNT_DTOR(CacheFileIOManager
);
1100 CacheFileIOManager::Init()
1102 LOG(("CacheFileIOManager::Init()"));
1104 MOZ_ASSERT(NS_IsMainThread());
1107 return NS_ERROR_ALREADY_INITIALIZED
;
1110 nsRefPtr
<CacheFileIOManager
> ioMan
= new CacheFileIOManager();
1112 nsresult rv
= ioMan
->InitInternal();
1113 NS_ENSURE_SUCCESS(rv
, rv
);
1115 ioMan
.swap(gInstance
);
1120 CacheFileIOManager::InitInternal()
1124 mIOThread
= new CacheIOThread();
1126 rv
= mIOThread
->Init();
1127 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Can't create background thread");
1128 NS_ENSURE_SUCCESS(rv
, rv
);
1130 mStartTime
= TimeStamp::NowLoRes();
1137 CacheFileIOManager::Shutdown()
1139 LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance
));
1141 MOZ_ASSERT(NS_IsMainThread());
1144 return NS_ERROR_NOT_INITIALIZED
;
1147 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2
> shutdownTimer
;
1149 CacheIndex::PreShutdown();
1151 ShutdownMetadataWriteScheduling();
1154 mozilla::Mutex
lock("CacheFileIOManager::Shutdown() lock");
1155 mozilla::CondVar
condVar(lock
, "CacheFileIOManager::Shutdown() condVar");
1157 MutexAutoLock
autoLock(lock
);
1158 nsRefPtr
<ShutdownEvent
> ev
= new ShutdownEvent(&lock
, &condVar
);
1159 DebugOnly
<nsresult
> rv
;
1160 rv
= gInstance
->mIOThread
->Dispatch(ev
, CacheIOThread::CLOSE
);
1161 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1165 MOZ_ASSERT(gInstance
->mHandles
.HandleCount() == 0);
1166 MOZ_ASSERT(gInstance
->mHandlesByLastUsed
.Length() == 0);
1168 if (gInstance
->mIOThread
) {
1169 gInstance
->mIOThread
->Shutdown();
1172 CacheIndex::Shutdown();
1174 if (CacheObserver::ClearCacheOnShutdown()) {
1175 Telemetry::AutoTimer
<Telemetry::NETWORK_DISK_CACHE2_SHUTDOWN_CLEAR_PRIVATE
> totalTimer
;
1176 gInstance
->SyncRemoveAllCacheFiles();
1179 nsRefPtr
<CacheFileIOManager
> ioMan
;
1180 ioMan
.swap(gInstance
);
1186 CacheFileIOManager::ShutdownInternal()
1188 LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
1190 MOZ_ASSERT(mIOThread
->IsCurrentThread());
1192 // No new handles can be created after this flag is set
1193 mShuttingDown
= true;
1195 // close all handles and delete all associated files
1196 nsTArray
<nsRefPtr
<CacheFileHandle
> > handles
;
1197 mHandles
.GetAllHandles(&handles
);
1198 handles
.AppendElements(mSpecialHandles
);
1200 for (uint32_t i
=0 ; i
<handles
.Length() ; i
++) {
1201 CacheFileHandle
*h
= handles
[i
];
1206 // Close file handle
1208 ReleaseNSPRHandleInternal(h
);
1211 // Remove file if entry is doomed or invalid
1212 if (h
->mFileExists
&& (h
->mIsDoomed
|| h
->mInvalid
)) {
1213 LOG(("CacheFileIOManager::ShutdownInternal() - Removing file from disk"));
1214 h
->mFile
->Remove(false);
1217 if (!h
->IsSpecialFile() && !h
->mIsDoomed
&&
1218 (h
->mInvalid
|| !h
->mFileExists
)) {
1219 CacheIndex::RemoveEntry(h
->Hash());
1222 // Remove the handle from mHandles/mSpecialHandles
1223 if (h
->IsSpecialFile()) {
1224 mSpecialHandles
.RemoveElement(h
);
1226 mHandles
.RemoveHandle(h
);
1229 // Pointer to the hash is no longer valid once the last handle with the
1230 // given hash is released. Null out the pointer so that we crash if there
1231 // is a bug in this code and we dereference the pointer after this point.
1232 if (!h
->IsSpecialFile()) {
1237 // Assert the table is empty. When we are here, no new handles can be added
1238 // and handles will no longer remove them self from this table and we don't
1239 // want to keep invalid handles here. Also, there is no lookup after this
1241 MOZ_ASSERT(mHandles
.HandleCount() == 0);
1243 // Release trash directory enumerator
1244 if (mTrashDirEnumerator
) {
1245 mTrashDirEnumerator
->Close();
1246 mTrashDirEnumerator
= nullptr;
1254 CacheFileIOManager::OnProfile()
1256 LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance
));
1258 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1260 // CacheFileIOManager::Init() failed, probably could not create the IO
1261 // thread, just go with it...
1262 return NS_ERROR_NOT_INITIALIZED
;
1267 nsCOMPtr
<nsIFile
> directory
;
1269 CacheObserver::ParentDirOverride(getter_AddRefs(directory
));
1271 #if defined(MOZ_WIDGET_ANDROID)
1272 nsCOMPtr
<nsIFile
> profilelessDirectory
;
1273 char* cachePath
= getenv("CACHE_DIRECTORY");
1274 if (!directory
&& cachePath
&& *cachePath
) {
1275 rv
= NS_NewNativeLocalFile(nsDependentCString(cachePath
),
1276 true, getter_AddRefs(directory
));
1277 if (NS_SUCCEEDED(rv
)) {
1278 // Save this directory as the profileless path.
1279 rv
= directory
->Clone(getter_AddRefs(profilelessDirectory
));
1280 NS_ENSURE_SUCCESS(rv
, rv
);
1282 // Add profile leaf name to the directory name to distinguish
1283 // multiple profiles Fennec supports.
1284 nsCOMPtr
<nsIFile
> profD
;
1285 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
1286 getter_AddRefs(profD
));
1288 nsAutoCString leafName
;
1289 if (NS_SUCCEEDED(rv
)) {
1290 rv
= profD
->GetNativeLeafName(leafName
);
1292 if (NS_SUCCEEDED(rv
)) {
1293 rv
= directory
->AppendNative(leafName
);
1295 if (NS_FAILED(rv
)) {
1296 directory
= nullptr;
1303 rv
= NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR
,
1304 getter_AddRefs(directory
));
1308 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
1309 getter_AddRefs(directory
));
1313 rv
= directory
->Append(NS_LITERAL_STRING("cache2"));
1314 NS_ENSURE_SUCCESS(rv
, rv
);
1317 // All functions return a clone.
1318 ioMan
->mCacheDirectory
.swap(directory
);
1320 #if defined(MOZ_WIDGET_ANDROID)
1321 if (profilelessDirectory
) {
1322 rv
= profilelessDirectory
->Append(NS_LITERAL_STRING("cache2"));
1323 NS_ENSURE_SUCCESS(rv
, rv
);
1326 ioMan
->mCacheProfilelessDirectory
.swap(profilelessDirectory
);
1329 if (ioMan
->mCacheDirectory
) {
1330 CacheIndex::Init(ioMan
->mCacheDirectory
);
1337 already_AddRefed
<nsIEventTarget
>
1338 CacheFileIOManager::IOTarget()
1340 nsCOMPtr
<nsIEventTarget
> target
;
1341 if (gInstance
&& gInstance
->mIOThread
) {
1342 target
= gInstance
->mIOThread
->Target();
1345 return target
.forget();
1349 already_AddRefed
<CacheIOThread
>
1350 CacheFileIOManager::IOThread()
1352 nsRefPtr
<CacheIOThread
> thread
;
1354 thread
= gInstance
->mIOThread
;
1357 return thread
.forget();
1362 CacheFileIOManager::IsOnIOThread()
1364 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1365 if (ioMan
&& ioMan
->mIOThread
) {
1366 return ioMan
->mIOThread
->IsCurrentThread();
1374 CacheFileIOManager::IsOnIOThreadOrCeased()
1376 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1377 if (ioMan
&& ioMan
->mIOThread
) {
1378 return ioMan
->mIOThread
->IsCurrentThread();
1387 CacheFileIOManager::IsShutdown()
1392 return gInstance
->mShuttingDown
;
1397 CacheFileIOManager::ScheduleMetadataWrite(CacheFile
* aFile
)
1399 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1400 NS_ENSURE_TRUE(ioMan
, NS_ERROR_NOT_INITIALIZED
);
1402 NS_ENSURE_TRUE(!ioMan
->mShuttingDown
, NS_ERROR_NOT_INITIALIZED
);
1404 nsRefPtr
<MetadataWriteScheduleEvent
> event
= new MetadataWriteScheduleEvent(
1405 ioMan
, aFile
, MetadataWriteScheduleEvent::SCHEDULE
);
1406 nsCOMPtr
<nsIEventTarget
> target
= ioMan
->IOTarget();
1407 NS_ENSURE_TRUE(target
, NS_ERROR_UNEXPECTED
);
1408 return target
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
1412 CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile
* aFile
)
1414 MOZ_ASSERT(IsOnIOThreadOrCeased());
1418 if (!mMetadataWritesTimer
) {
1419 mMetadataWritesTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
1420 NS_ENSURE_SUCCESS(rv
, rv
);
1422 rv
= mMetadataWritesTimer
->InitWithCallback(
1423 this, kMetadataWriteDelay
, nsITimer::TYPE_ONE_SHOT
);
1424 NS_ENSURE_SUCCESS(rv
, rv
);
1427 if (mScheduledMetadataWrites
.IndexOf(aFile
) !=
1428 mScheduledMetadataWrites
.NoIndex
) {
1432 mScheduledMetadataWrites
.AppendElement(aFile
);
1439 CacheFileIOManager::UnscheduleMetadataWrite(CacheFile
* aFile
)
1441 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1442 NS_ENSURE_TRUE(ioMan
, NS_ERROR_NOT_INITIALIZED
);
1444 NS_ENSURE_TRUE(!ioMan
->mShuttingDown
, NS_ERROR_NOT_INITIALIZED
);
1446 nsRefPtr
<MetadataWriteScheduleEvent
> event
= new MetadataWriteScheduleEvent(
1447 ioMan
, aFile
, MetadataWriteScheduleEvent::UNSCHEDULE
);
1448 nsCOMPtr
<nsIEventTarget
> target
= ioMan
->IOTarget();
1449 NS_ENSURE_TRUE(target
, NS_ERROR_UNEXPECTED
);
1450 return target
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
1454 CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile
* aFile
)
1456 MOZ_ASSERT(IsOnIOThreadOrCeased());
1458 mScheduledMetadataWrites
.RemoveElement(aFile
);
1460 if (mScheduledMetadataWrites
.Length() == 0 &&
1461 mMetadataWritesTimer
) {
1462 mMetadataWritesTimer
->Cancel();
1463 mMetadataWritesTimer
= nullptr;
1471 CacheFileIOManager::ShutdownMetadataWriteScheduling()
1473 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1474 NS_ENSURE_TRUE(ioMan
, NS_ERROR_NOT_INITIALIZED
);
1476 nsRefPtr
<MetadataWriteScheduleEvent
> event
= new MetadataWriteScheduleEvent(
1477 ioMan
, nullptr, MetadataWriteScheduleEvent::SHUTDOWN
);
1478 nsCOMPtr
<nsIEventTarget
> target
= ioMan
->IOTarget();
1479 NS_ENSURE_TRUE(target
, NS_ERROR_UNEXPECTED
);
1480 return target
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
1484 CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
1486 MOZ_ASSERT(IsOnIOThreadOrCeased());
1488 nsTArray
<nsRefPtr
<CacheFile
> > files
;
1489 files
.SwapElements(mScheduledMetadataWrites
);
1490 for (uint32_t i
= 0; i
< files
.Length(); ++i
) {
1491 CacheFile
* file
= files
[i
];
1492 file
->WriteMetadataIfNeeded();
1495 if (mMetadataWritesTimer
) {
1496 mMetadataWritesTimer
->Cancel();
1497 mMetadataWritesTimer
= nullptr;
1504 CacheFileIOManager::Notify(nsITimer
* aTimer
)
1506 MOZ_ASSERT(IsOnIOThreadOrCeased());
1507 MOZ_ASSERT(mMetadataWritesTimer
== aTimer
);
1509 mMetadataWritesTimer
= nullptr;
1511 nsTArray
<nsRefPtr
<CacheFile
> > files
;
1512 files
.SwapElements(mScheduledMetadataWrites
);
1513 for (uint32_t i
= 0; i
< files
.Length(); ++i
) {
1514 CacheFile
* file
= files
[i
];
1515 file
->WriteMetadataIfNeeded();
1523 CacheFileIOManager::OpenFile(const nsACString
&aKey
,
1524 uint32_t aFlags
, CacheFileIOListener
*aCallback
)
1526 LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
1527 PromiseFlatCString(aKey
).get(), aFlags
, aCallback
));
1530 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1533 return NS_ERROR_NOT_INITIALIZED
;
1536 bool priority
= aFlags
& CacheFileIOManager::PRIORITY
;
1537 nsRefPtr
<OpenFileEvent
> ev
= new OpenFileEvent(aKey
, aFlags
, aCallback
);
1538 rv
= ioMan
->mIOThread
->Dispatch(ev
, priority
1539 ? CacheIOThread::OPEN_PRIORITY
1540 : CacheIOThread::OPEN
);
1541 NS_ENSURE_SUCCESS(rv
, rv
);
1547 CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash
*aHash
,
1548 const nsACString
&aKey
,
1550 CacheFileHandle
**_retval
)
1552 LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
1553 "key=%s, flags=%d]", LOGSHA1(aHash
), PromiseFlatCString(aKey
).get(),
1556 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1560 if (mShuttingDown
) {
1561 return NS_ERROR_NOT_INITIALIZED
;
1564 if (!mTreeCreated
) {
1565 rv
= CreateCacheTree();
1566 if (NS_FAILED(rv
)) return rv
;
1569 nsCOMPtr
<nsIFile
> file
;
1570 rv
= GetFile(aHash
, getter_AddRefs(file
));
1571 NS_ENSURE_SUCCESS(rv
, rv
);
1573 nsRefPtr
<CacheFileHandle
> handle
;
1574 mHandles
.GetHandle(aHash
, false, getter_AddRefs(handle
));
1576 if ((aFlags
& (OPEN
| CREATE
| CREATE_NEW
)) == CREATE_NEW
) {
1578 rv
= DoomFileInternal(handle
);
1579 NS_ENSURE_SUCCESS(rv
, rv
);
1583 rv
= mHandles
.NewHandle(aHash
, aFlags
& PRIORITY
, getter_AddRefs(handle
));
1584 NS_ENSURE_SUCCESS(rv
, rv
);
1587 rv
= file
->Exists(&exists
);
1588 NS_ENSURE_SUCCESS(rv
, rv
);
1591 CacheIndex::RemoveEntry(aHash
);
1593 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
1595 rv
= file
->Remove(false);
1596 if (NS_FAILED(rv
)) {
1597 NS_WARNING("Cannot remove old entry from the disk");
1598 LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
1599 ". [rv=0x%08x]", rv
));
1603 CacheIndex::AddEntry(aHash
);
1604 handle
->mFile
.swap(file
);
1605 handle
->mFileSize
= 0;
1609 handle
.swap(*_retval
);
1614 rv
= file
->Exists(&exists
);
1615 NS_ENSURE_SUCCESS(rv
, rv
);
1617 if (exists
&& mContextEvictor
) {
1618 if (mContextEvictor
->ContextsCount() == 0) {
1619 mContextEvictor
= nullptr;
1621 bool wasEvicted
= false;
1622 mContextEvictor
->WasEvicted(aKey
, file
, &wasEvicted
);
1624 LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
1625 "entry was evicted by EvictByContext()"));
1627 file
->Remove(false);
1628 CacheIndex::RemoveEntry(aHash
);
1633 if (!exists
&& (aFlags
& (OPEN
| CREATE
| CREATE_NEW
)) == OPEN
) {
1634 return NS_ERROR_NOT_AVAILABLE
;
1637 rv
= mHandles
.NewHandle(aHash
, aFlags
& PRIORITY
, getter_AddRefs(handle
));
1638 NS_ENSURE_SUCCESS(rv
, rv
);
1641 rv
= file
->GetFileSize(&handle
->mFileSize
);
1642 NS_ENSURE_SUCCESS(rv
, rv
);
1644 handle
->mFileExists
= true;
1646 CacheIndex::EnsureEntryExists(aHash
);
1648 handle
->mFileSize
= 0;
1650 CacheIndex::AddEntry(aHash
);
1653 handle
->mFile
.swap(file
);
1654 handle
.swap(*_retval
);
1659 CacheFileIOManager::OpenSpecialFileInternal(const nsACString
&aKey
,
1661 CacheFileHandle
**_retval
)
1663 LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
1664 PromiseFlatCString(aKey
).get(), aFlags
));
1666 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1670 if (mShuttingDown
) {
1671 return NS_ERROR_NOT_INITIALIZED
;
1674 if (!mTreeCreated
) {
1675 rv
= CreateCacheTree();
1676 if (NS_FAILED(rv
)) return rv
;
1679 nsCOMPtr
<nsIFile
> file
;
1680 rv
= GetSpecialFile(aKey
, getter_AddRefs(file
));
1681 NS_ENSURE_SUCCESS(rv
, rv
);
1683 nsRefPtr
<CacheFileHandle
> handle
;
1684 for (uint32_t i
= 0 ; i
< mSpecialHandles
.Length() ; i
++) {
1685 if (!mSpecialHandles
[i
]->IsDoomed() && mSpecialHandles
[i
]->Key() == aKey
) {
1686 handle
= mSpecialHandles
[i
];
1691 if ((aFlags
& (OPEN
| CREATE
| CREATE_NEW
)) == CREATE_NEW
) {
1693 rv
= DoomFileInternal(handle
);
1694 NS_ENSURE_SUCCESS(rv
, rv
);
1698 handle
= new CacheFileHandle(aKey
, aFlags
& PRIORITY
);
1699 mSpecialHandles
.AppendElement(handle
);
1702 rv
= file
->Exists(&exists
);
1703 NS_ENSURE_SUCCESS(rv
, rv
);
1706 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
1708 rv
= file
->Remove(false);
1709 if (NS_FAILED(rv
)) {
1710 NS_WARNING("Cannot remove old entry from the disk");
1711 LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
1712 "failed. [rv=0x%08x]", rv
));
1716 handle
->mFile
.swap(file
);
1717 handle
->mFileSize
= 0;
1721 handle
.swap(*_retval
);
1726 rv
= file
->Exists(&exists
);
1727 NS_ENSURE_SUCCESS(rv
, rv
);
1729 if (!exists
&& (aFlags
& (OPEN
| CREATE
| CREATE_NEW
)) == OPEN
) {
1730 return NS_ERROR_NOT_AVAILABLE
;
1733 handle
= new CacheFileHandle(aKey
, aFlags
& PRIORITY
);
1734 mSpecialHandles
.AppendElement(handle
);
1737 rv
= file
->GetFileSize(&handle
->mFileSize
);
1738 NS_ENSURE_SUCCESS(rv
, rv
);
1740 handle
->mFileExists
= true;
1742 handle
->mFileSize
= 0;
1745 handle
->mFile
.swap(file
);
1746 handle
.swap(*_retval
);
1751 CacheFileIOManager::CloseHandleInternal(CacheFileHandle
*aHandle
)
1753 LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle
));
1755 MOZ_ASSERT(!aHandle
->IsClosed());
1759 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
1761 // Close file handle
1763 ReleaseNSPRHandleInternal(aHandle
);
1766 // Delete the file if the entry was doomed or invalid
1767 if (aHandle
->mIsDoomed
|| aHandle
->mInvalid
) {
1768 LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
1770 aHandle
->mFile
->Remove(false);
1773 if (!aHandle
->IsSpecialFile() && !aHandle
->mIsDoomed
&&
1774 (aHandle
->mInvalid
|| !aHandle
->mFileExists
)) {
1775 CacheIndex::RemoveEntry(aHandle
->Hash());
1778 // Don't remove handles after shutdown
1779 if (!mShuttingDown
) {
1780 if (aHandle
->IsSpecialFile()) {
1781 mSpecialHandles
.RemoveElement(aHandle
);
1783 mHandles
.RemoveHandle(aHandle
);
1792 CacheFileIOManager::Read(CacheFileHandle
*aHandle
, int64_t aOffset
,
1793 char *aBuf
, int32_t aCount
,
1794 CacheFileIOListener
*aCallback
)
1796 LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
1797 "listener=%p]", aHandle
, aOffset
, aCount
, aCallback
));
1800 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1802 if (aHandle
->IsClosed() || !ioMan
) {
1803 return NS_ERROR_NOT_INITIALIZED
;
1806 nsRefPtr
<ReadEvent
> ev
= new ReadEvent(aHandle
, aOffset
, aBuf
, aCount
,
1808 rv
= ioMan
->mIOThread
->Dispatch(ev
, aHandle
->IsPriority()
1809 ? CacheIOThread::READ_PRIORITY
1810 : CacheIOThread::READ
);
1811 NS_ENSURE_SUCCESS(rv
, rv
);
1817 CacheFileIOManager::ReadInternal(CacheFileHandle
*aHandle
, int64_t aOffset
,
1818 char *aBuf
, int32_t aCount
)
1820 LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%lld, count=%d]",
1821 aHandle
, aOffset
, aCount
));
1825 if (!aHandle
->mFileExists
) {
1826 NS_WARNING("Trying to read from non-existent file");
1827 return NS_ERROR_NOT_AVAILABLE
;
1830 if (!aHandle
->mFD
) {
1831 rv
= OpenNSPRHandle(aHandle
);
1832 NS_ENSURE_SUCCESS(rv
, rv
);
1834 NSPRHandleUsed(aHandle
);
1837 // Check again, OpenNSPRHandle could figure out the file was gone.
1838 if (!aHandle
->mFileExists
) {
1839 NS_WARNING("Trying to read from non-existent file");
1840 return NS_ERROR_NOT_AVAILABLE
;
1843 int64_t offset
= PR_Seek64(aHandle
->mFD
, aOffset
, PR_SEEK_SET
);
1845 return NS_ERROR_FAILURE
;
1848 int32_t bytesRead
= PR_Read(aHandle
->mFD
, aBuf
, aCount
);
1849 if (bytesRead
!= aCount
) {
1850 return NS_ERROR_FAILURE
;
1858 CacheFileIOManager::Write(CacheFileHandle
*aHandle
, int64_t aOffset
,
1859 const char *aBuf
, int32_t aCount
, bool aValidate
,
1860 CacheFileIOListener
*aCallback
)
1862 LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
1863 "validate=%d, listener=%p]", aHandle
, aOffset
, aCount
, aValidate
,
1867 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1869 if (aHandle
->IsClosed() || !ioMan
) {
1871 // When no callback is provided, CacheFileIOManager is responsible for
1872 // releasing the buffer. We must release it even in case of failure.
1873 free(const_cast<char *>(aBuf
));
1875 return NS_ERROR_NOT_INITIALIZED
;
1878 nsRefPtr
<WriteEvent
> ev
= new WriteEvent(aHandle
, aOffset
, aBuf
, aCount
,
1879 aValidate
, aCallback
);
1880 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::WRITE
);
1881 NS_ENSURE_SUCCESS(rv
, rv
);
1887 CacheFileIOManager::WriteInternal(CacheFileHandle
*aHandle
, int64_t aOffset
,
1888 const char *aBuf
, int32_t aCount
,
1891 LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%lld, count=%d, "
1892 "validate=%d]", aHandle
, aOffset
, aCount
, aValidate
));
1896 if (!aHandle
->mFileExists
) {
1897 rv
= CreateFile(aHandle
);
1898 NS_ENSURE_SUCCESS(rv
, rv
);
1901 if (!aHandle
->mFD
) {
1902 rv
= OpenNSPRHandle(aHandle
);
1903 NS_ENSURE_SUCCESS(rv
, rv
);
1905 NSPRHandleUsed(aHandle
);
1908 // Check again, OpenNSPRHandle could figure out the file was gone.
1909 if (!aHandle
->mFileExists
) {
1910 return NS_ERROR_NOT_AVAILABLE
;
1913 // Check whether this write would cause critical low disk space.
1914 if (aHandle
->mFileSize
< aOffset
+ aCount
) {
1915 int64_t freeSpace
= -1;
1916 rv
= mCacheDirectory
->GetDiskSpaceAvailable(&freeSpace
);
1917 if (NS_WARN_IF(NS_FAILED(rv
))) {
1918 LOG(("CacheFileIOManager::WriteInternal() - GetDiskSpaceAvailable() "
1919 "failed! [rv=0x%08x]", rv
));
1921 uint32_t limit
= CacheObserver::DiskFreeSpaceHardLimit();
1922 if (freeSpace
- aOffset
- aCount
+ aHandle
->mFileSize
< limit
) {
1923 LOG(("CacheFileIOManager::WriteInternal() - Low free space, refusing "
1924 "to write! [freeSpace=%lld, limit=%u]", freeSpace
, limit
));
1925 return NS_ERROR_FILE_DISK_FULL
;
1930 // Write invalidates the entry by default
1931 aHandle
->mInvalid
= true;
1933 int64_t offset
= PR_Seek64(aHandle
->mFD
, aOffset
, PR_SEEK_SET
);
1935 return NS_ERROR_FAILURE
;
1938 int32_t bytesWritten
= PR_Write(aHandle
->mFD
, aBuf
, aCount
);
1940 if (bytesWritten
!= -1 && aHandle
->mFileSize
< aOffset
+bytesWritten
) {
1941 aHandle
->mFileSize
= aOffset
+bytesWritten
;
1943 if (!aHandle
->IsDoomed() && !aHandle
->IsSpecialFile()) {
1944 uint32_t size
= aHandle
->FileSizeInK();
1945 CacheIndex::UpdateEntry(aHandle
->Hash(), nullptr, nullptr, &size
);
1946 EvictIfOverLimitInternal();
1950 if (bytesWritten
!= aCount
) {
1951 return NS_ERROR_FAILURE
;
1954 // Write was successful and this write validates the entry (i.e. metadata)
1956 aHandle
->mInvalid
= false;
1964 CacheFileIOManager::DoomFile(CacheFileHandle
*aHandle
,
1965 CacheFileIOListener
*aCallback
)
1967 LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
1968 aHandle
, aCallback
));
1971 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
1973 if (aHandle
->IsClosed() || !ioMan
) {
1974 return NS_ERROR_NOT_INITIALIZED
;
1977 nsRefPtr
<DoomFileEvent
> ev
= new DoomFileEvent(aHandle
, aCallback
);
1978 rv
= ioMan
->mIOThread
->Dispatch(ev
, aHandle
->IsPriority()
1979 ? CacheIOThread::OPEN_PRIORITY
1980 : CacheIOThread::OPEN
);
1981 NS_ENSURE_SUCCESS(rv
, rv
);
1987 CacheFileIOManager::DoomFileInternal(CacheFileHandle
*aHandle
)
1989 LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle
));
1994 if (aHandle
->IsDoomed()) {
1998 if (aHandle
->mFileExists
) {
1999 // we need to move the current file to the doomed directory
2001 ReleaseNSPRHandleInternal(aHandle
);
2004 // find unused filename
2005 nsCOMPtr
<nsIFile
> file
;
2006 rv
= GetDoomedFile(getter_AddRefs(file
));
2007 NS_ENSURE_SUCCESS(rv
, rv
);
2009 nsCOMPtr
<nsIFile
> parentDir
;
2010 rv
= file
->GetParent(getter_AddRefs(parentDir
));
2011 NS_ENSURE_SUCCESS(rv
, rv
);
2013 nsAutoCString leafName
;
2014 rv
= file
->GetNativeLeafName(leafName
);
2015 NS_ENSURE_SUCCESS(rv
, rv
);
2017 rv
= aHandle
->mFile
->MoveToNative(parentDir
, leafName
);
2018 if (NS_ERROR_FILE_NOT_FOUND
== rv
|| NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
== rv
) {
2019 LOG((" file already removed under our hands"));
2020 aHandle
->mFileExists
= false;
2023 NS_ENSURE_SUCCESS(rv
, rv
);
2024 aHandle
->mFile
.swap(file
);
2028 if (!aHandle
->IsSpecialFile()) {
2029 CacheIndex::RemoveEntry(aHandle
->Hash());
2032 aHandle
->mIsDoomed
= true;
2034 if (!aHandle
->IsSpecialFile()) {
2035 nsRefPtr
<CacheStorageService
> storageService
= CacheStorageService::Self();
2036 if (storageService
) {
2037 nsAutoCString idExtension
, url
;
2038 nsCOMPtr
<nsILoadContextInfo
> info
=
2039 CacheFileUtils::ParseKey(aHandle
->Key(), &idExtension
, &url
);
2042 storageService
->CacheFileDoomed(info
, idExtension
, url
);
2052 CacheFileIOManager::DoomFileByKey(const nsACString
&aKey
,
2053 CacheFileIOListener
*aCallback
)
2055 LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
2056 PromiseFlatCString(aKey
).get(), aCallback
));
2059 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2062 return NS_ERROR_NOT_INITIALIZED
;
2065 nsRefPtr
<DoomFileByKeyEvent
> ev
= new DoomFileByKeyEvent(aKey
, aCallback
);
2066 rv
= ioMan
->mIOThread
->DispatchAfterPendingOpens(ev
);
2067 NS_ENSURE_SUCCESS(rv
, rv
);
2073 CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash
*aHash
,
2074 bool aFailIfAlreadyDoomed
)
2076 LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x,"
2077 " failIfAlreadyDoomed=%d]", LOGSHA1(aHash
), aFailIfAlreadyDoomed
));
2079 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2083 if (mShuttingDown
) {
2084 return NS_ERROR_NOT_INITIALIZED
;
2087 if (!mCacheDirectory
) {
2088 return NS_ERROR_FILE_INVALID_PATH
;
2091 // Find active handle
2092 nsRefPtr
<CacheFileHandle
> handle
;
2093 mHandles
.GetHandle(aHash
, true, getter_AddRefs(handle
));
2098 if (handle
->IsDoomed()) {
2099 return aFailIfAlreadyDoomed
? NS_ERROR_NOT_AVAILABLE
: NS_OK
;
2102 return DoomFileInternal(handle
);
2105 // There is no handle for this file, delete the file if exists
2106 nsCOMPtr
<nsIFile
> file
;
2107 rv
= GetFile(aHash
, getter_AddRefs(file
));
2108 NS_ENSURE_SUCCESS(rv
, rv
);
2111 rv
= file
->Exists(&exists
);
2112 NS_ENSURE_SUCCESS(rv
, rv
);
2115 return NS_ERROR_NOT_AVAILABLE
;
2118 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
2120 rv
= file
->Remove(false);
2121 if (NS_FAILED(rv
)) {
2122 NS_WARNING("Cannot remove old entry from the disk");
2123 LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
2124 "[rv=0x%08x]", rv
));
2127 CacheIndex::RemoveEntry(aHash
);
2134 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle
*aHandle
)
2136 LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle
));
2139 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2141 if (aHandle
->IsClosed() || !ioMan
) {
2142 return NS_ERROR_NOT_INITIALIZED
;
2145 nsRefPtr
<ReleaseNSPRHandleEvent
> ev
= new ReleaseNSPRHandleEvent(aHandle
);
2146 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::CLOSE
);
2147 NS_ENSURE_SUCCESS(rv
, rv
);
2153 CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle
*aHandle
)
2155 LOG(("CacheFileIOManager::ReleaseNSPRHandleInternal() [handle=%p]", aHandle
));
2157 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2158 MOZ_ASSERT(aHandle
->mFD
);
2160 DebugOnly
<bool> found
;
2161 found
= mHandlesByLastUsed
.RemoveElement(aHandle
);
2164 PR_Close(aHandle
->mFD
);
2165 aHandle
->mFD
= nullptr;
2172 CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle
*aHandle
,
2173 int64_t aTruncatePos
, int64_t aEOFPos
,
2174 CacheFileIOListener
*aCallback
)
2176 LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
2177 "EOFPos=%lld, listener=%p]", aHandle
, aTruncatePos
, aEOFPos
, aCallback
));
2180 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2182 if (aHandle
->IsClosed() || !ioMan
) {
2183 return NS_ERROR_NOT_INITIALIZED
;
2186 nsRefPtr
<TruncateSeekSetEOFEvent
> ev
= new TruncateSeekSetEOFEvent(
2187 aHandle
, aTruncatePos
, aEOFPos
,
2189 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::WRITE
);
2190 NS_ENSURE_SUCCESS(rv
, rv
);
2196 void CacheFileIOManager::GetCacheDirectory(nsIFile
** result
)
2200 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2205 ioMan
->mCacheDirectory
->Clone(result
);
2208 #if defined(MOZ_WIDGET_ANDROID)
2211 void CacheFileIOManager::GetProfilelessCacheDirectory(nsIFile
** result
)
2215 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2216 if (!ioMan
|| !ioMan
->mCacheProfilelessDirectory
) {
2220 ioMan
->mCacheProfilelessDirectory
->Clone(result
);
2227 CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash
*aHash
,
2228 CacheStorageService::EntryInfoCallback
*aCallback
)
2230 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
2234 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2236 return NS_ERROR_NOT_INITIALIZED
;
2239 nsAutoCString enhanceId
;
2240 nsAutoCString uriSpec
;
2242 nsRefPtr
<CacheFileHandle
> handle
;
2243 ioMan
->mHandles
.GetHandle(aHash
, false, getter_AddRefs(handle
));
2245 nsRefPtr
<nsILoadContextInfo
> info
=
2246 CacheFileUtils::ParseKey(handle
->Key(), &enhanceId
, &uriSpec
);
2250 return NS_OK
; // ignore
2253 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
2255 return NS_ERROR_NOT_INITIALIZED
;
2258 // Invokes OnCacheEntryInfo when an existing entry is found
2259 if (service
->GetCacheEntryInfo(info
, enhanceId
, uriSpec
, aCallback
)) {
2263 // When we are here, there is no existing entry and we need
2264 // to synchrnously load metadata from a disk file.
2267 // Locate the actual file
2268 nsCOMPtr
<nsIFile
> file
;
2269 ioMan
->GetFile(aHash
, getter_AddRefs(file
));
2271 // Read metadata from the file synchronously
2272 nsRefPtr
<CacheFileMetadata
> metadata
= new CacheFileMetadata();
2273 rv
= metadata
->SyncReadMetadata(file
);
2274 if (NS_FAILED(rv
)) {
2278 // Now get the context + enhance id + URL from the key.
2280 metadata
->GetKey(key
);
2282 nsRefPtr
<nsILoadContextInfo
> info
=
2283 CacheFileUtils::ParseKey(key
, &enhanceId
, &uriSpec
);
2289 // Pick all data to pass to the callback.
2290 int64_t dataSize
= metadata
->Offset();
2291 uint32_t fetchCount
;
2292 if (NS_FAILED(metadata
->GetFetchCount(&fetchCount
))) {
2295 uint32_t expirationTime
;
2296 if (NS_FAILED(metadata
->GetExpirationTime(&expirationTime
))) {
2299 uint32_t lastModified
;
2300 if (NS_FAILED(metadata
->GetLastModified(&lastModified
))) {
2304 // Call directly on the callback.
2305 aCallback
->OnEntryInfo(uriSpec
, enhanceId
, dataSize
, fetchCount
,
2306 lastModified
, expirationTime
);
2312 TruncFile(PRFileDesc
*aFD
, uint32_t aEOF
)
2314 #if defined(XP_UNIX)
2315 if (ftruncate(PR_FileDesc2NativeHandle(aFD
), aEOF
) != 0) {
2316 NS_ERROR("ftruncate failed");
2317 return NS_ERROR_FAILURE
;
2319 #elif defined(XP_WIN)
2320 int32_t cnt
= PR_Seek(aFD
, aEOF
, PR_SEEK_SET
);
2322 return NS_ERROR_FAILURE
;
2324 if (!SetEndOfFile((HANDLE
) PR_FileDesc2NativeHandle(aFD
))) {
2325 NS_ERROR("SetEndOfFile failed");
2326 return NS_ERROR_FAILURE
;
2329 MOZ_ASSERT(false, "Not implemented!");
2336 CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle
*aHandle
,
2337 int64_t aTruncatePos
,
2340 LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
2341 "truncatePos=%lld, EOFPos=%lld]", aHandle
, aTruncatePos
, aEOFPos
));
2345 if (!aHandle
->mFileExists
) {
2346 rv
= CreateFile(aHandle
);
2347 NS_ENSURE_SUCCESS(rv
, rv
);
2350 if (!aHandle
->mFD
) {
2351 rv
= OpenNSPRHandle(aHandle
);
2352 NS_ENSURE_SUCCESS(rv
, rv
);
2354 NSPRHandleUsed(aHandle
);
2357 // Check again, OpenNSPRHandle could figure out the file was gone.
2358 if (!aHandle
->mFileExists
) {
2359 return NS_ERROR_NOT_AVAILABLE
;
2362 // This operation always invalidates the entry
2363 aHandle
->mInvalid
= true;
2365 rv
= TruncFile(aHandle
->mFD
, static_cast<uint32_t>(aTruncatePos
));
2366 NS_ENSURE_SUCCESS(rv
, rv
);
2368 rv
= TruncFile(aHandle
->mFD
, static_cast<uint32_t>(aEOFPos
));
2369 NS_ENSURE_SUCCESS(rv
, rv
);
2376 CacheFileIOManager::RenameFile(CacheFileHandle
*aHandle
,
2377 const nsACString
&aNewName
,
2378 CacheFileIOListener
*aCallback
)
2380 LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
2381 aHandle
, PromiseFlatCString(aNewName
).get(), aCallback
));
2384 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2386 if (aHandle
->IsClosed() || !ioMan
) {
2387 return NS_ERROR_NOT_INITIALIZED
;
2390 if (!aHandle
->IsSpecialFile()) {
2391 return NS_ERROR_UNEXPECTED
;
2394 nsRefPtr
<RenameFileEvent
> ev
= new RenameFileEvent(aHandle
, aNewName
,
2396 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::WRITE
);
2397 NS_ENSURE_SUCCESS(rv
, rv
);
2403 CacheFileIOManager::RenameFileInternal(CacheFileHandle
*aHandle
,
2404 const nsACString
&aNewName
)
2406 LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
2407 aHandle
, PromiseFlatCString(aNewName
).get()));
2411 MOZ_ASSERT(aHandle
->IsSpecialFile());
2413 if (aHandle
->IsDoomed()) {
2414 return NS_ERROR_NOT_AVAILABLE
;
2417 // Doom old handle if it exists and is not doomed
2418 for (uint32_t i
= 0 ; i
< mSpecialHandles
.Length() ; i
++) {
2419 if (!mSpecialHandles
[i
]->IsDoomed() &&
2420 mSpecialHandles
[i
]->Key() == aNewName
) {
2421 MOZ_ASSERT(aHandle
!= mSpecialHandles
[i
]);
2422 rv
= DoomFileInternal(mSpecialHandles
[i
]);
2423 NS_ENSURE_SUCCESS(rv
, rv
);
2428 nsCOMPtr
<nsIFile
> file
;
2429 rv
= GetSpecialFile(aNewName
, getter_AddRefs(file
));
2430 NS_ENSURE_SUCCESS(rv
, rv
);
2433 rv
= file
->Exists(&exists
);
2434 NS_ENSURE_SUCCESS(rv
, rv
);
2437 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
2439 rv
= file
->Remove(false);
2440 if (NS_FAILED(rv
)) {
2441 NS_WARNING("Cannot remove file from the disk");
2442 LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
2443 ". [rv=0x%08x]", rv
));
2447 if (!aHandle
->FileExists()) {
2448 aHandle
->mKey
= aNewName
;
2453 ReleaseNSPRHandleInternal(aHandle
);
2456 rv
= aHandle
->mFile
->MoveToNative(nullptr, aNewName
);
2457 NS_ENSURE_SUCCESS(rv
, rv
);
2459 aHandle
->mKey
= aNewName
;
2465 CacheFileIOManager::EvictIfOverLimit()
2467 LOG(("CacheFileIOManager::EvictIfOverLimit()"));
2470 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2473 return NS_ERROR_NOT_INITIALIZED
;
2476 nsCOMPtr
<nsIRunnable
> ev
;
2477 ev
= NS_NewRunnableMethod(ioMan
,
2478 &CacheFileIOManager::EvictIfOverLimitInternal
);
2480 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::EVICT
);
2481 NS_ENSURE_SUCCESS(rv
, rv
);
2487 CacheFileIOManager::EvictIfOverLimitInternal()
2489 LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
2493 MOZ_ASSERT(mIOThread
->IsCurrentThread());
2495 if (mShuttingDown
) {
2496 return NS_ERROR_NOT_INITIALIZED
;
2499 if (mOverLimitEvicting
) {
2500 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
2506 rv
= mCacheDirectory
->GetDiskSpaceAvailable(&freeSpace
);
2507 if (NS_WARN_IF(NS_FAILED(rv
))) {
2510 // Do not change smart size.
2511 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
2512 "GetDiskSpaceAvailable() failed! [rv=0x%08x]", rv
));
2514 UpdateSmartCacheSize(freeSpace
);
2517 uint32_t cacheUsage
;
2518 rv
= CacheIndex::GetCacheSize(&cacheUsage
);
2519 NS_ENSURE_SUCCESS(rv
, rv
);
2521 uint32_t cacheLimit
= CacheObserver::DiskCacheCapacity() >> 10;
2522 uint32_t freeSpaceLimit
= CacheObserver::DiskFreeSpaceSoftLimit();
2524 if (cacheUsage
<= cacheLimit
&&
2525 (freeSpace
== -1 || freeSpace
>= freeSpaceLimit
)) {
2526 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size and free "
2527 "space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
2528 "freeSpace=%lld, freeSpaceLimit=%u]", cacheUsage
, cacheLimit
,
2529 freeSpace
, freeSpaceLimit
));
2533 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
2534 "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
2535 cacheUsage
, cacheLimit
));
2537 nsCOMPtr
<nsIRunnable
> ev
;
2538 ev
= NS_NewRunnableMethod(this,
2539 &CacheFileIOManager::OverLimitEvictionInternal
);
2541 rv
= mIOThread
->Dispatch(ev
, CacheIOThread::EVICT
);
2542 NS_ENSURE_SUCCESS(rv
, rv
);
2544 mOverLimitEvicting
= true;
2549 CacheFileIOManager::OverLimitEvictionInternal()
2551 LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
2555 MOZ_ASSERT(mIOThread
->IsCurrentThread());
2557 // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
2558 // here and set it to true again once we dispatch another event that will
2559 // continue with the eviction. The reason why we do so is that we can fail
2560 // early anywhere in this method and the variable will contain a correct
2561 // value. Otherwise we would need to set it to false on every failing place.
2562 mOverLimitEvicting
= false;
2564 if (mShuttingDown
) {
2565 return NS_ERROR_NOT_INITIALIZED
;
2569 int64_t freeSpace
= -1;
2570 rv
= mCacheDirectory
->GetDiskSpaceAvailable(&freeSpace
);
2571 if (NS_WARN_IF(NS_FAILED(rv
))) {
2572 // Do not change smart size.
2573 LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
2574 "GetDiskSpaceAvailable() failed! [rv=0x%08x]", rv
));
2576 UpdateSmartCacheSize(freeSpace
);
2579 uint32_t cacheUsage
;
2580 rv
= CacheIndex::GetCacheSize(&cacheUsage
);
2581 NS_ENSURE_SUCCESS(rv
, rv
);
2583 uint32_t cacheLimit
= CacheObserver::DiskCacheCapacity() >> 10;
2584 uint32_t freeSpaceLimit
= CacheObserver::DiskFreeSpaceSoftLimit();
2586 if (cacheUsage
> cacheLimit
) {
2587 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
2588 "limit. [cacheSize=%u, limit=%u]", cacheUsage
, cacheLimit
));
2589 } else if (freeSpace
!= 1 && freeSpace
< freeSpaceLimit
) {
2590 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Free space under "
2591 "limit. [freeSpace=%lld, freeSpaceLimit=%u]", freeSpace
,
2594 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size and "
2595 "free space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
2596 "freeSpace=%lld, freeSpaceLimit=%u]", cacheUsage
, cacheLimit
,
2597 freeSpace
, freeSpaceLimit
));
2601 if (CacheIOThread::YieldAndRerun()) {
2602 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
2603 "for higher level events."));
2604 mOverLimitEvicting
= true;
2610 static uint32_t consecutiveFailures
= 0;
2611 rv
= CacheIndex::GetEntryForEviction(&hash
, &cnt
);
2612 NS_ENSURE_SUCCESS(rv
, rv
);
2614 rv
= DoomFileByKeyInternal(&hash
, true);
2615 if (NS_SUCCEEDED(rv
)) {
2616 consecutiveFailures
= 0;
2617 } else if (rv
== NS_ERROR_NOT_AVAILABLE
) {
2618 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2619 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv
));
2620 // TODO index is outdated, start update
2623 // Dooming should never fail due to already doomed handle, but bug 1028415
2624 // shows that this unexpected state can happen. Assert in debug build so
2625 // we can find the cause if we ever find a way to reproduce it with NSPR
2627 nsRefPtr
<CacheFileHandle
> handle
;
2628 mHandles
.GetHandle(&hash
, true, getter_AddRefs(handle
));
2629 MOZ_ASSERT(!handle
|| !handle
->IsDoomed());
2632 // Make sure index won't return the same entry again
2633 CacheIndex::RemoveEntry(&hash
);
2634 consecutiveFailures
= 0;
2636 // This shouldn't normally happen, but the eviction must not fail
2637 // completely if we ever encounter this problem.
2638 NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
2639 "failure of DoomFileByKeyInternal()");
2641 LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2642 "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv
));
2644 // Normally, CacheIndex::UpdateEntry() is called only to update newly
2645 // created/opened entries which are always fresh and UpdateEntry() expects
2646 // and checks this flag. The way we use UpdateEntry() here is a kind of
2647 // hack and we must make sure the flag is set by calling
2648 // EnsureEntryExists().
2649 rv
= CacheIndex::EnsureEntryExists(&hash
);
2650 NS_ENSURE_SUCCESS(rv
, rv
);
2652 // Move the entry at the end of both lists to make sure we won't end up
2653 // failing on one entry forever.
2654 uint32_t frecency
= 0;
2655 uint32_t expTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
2656 rv
= CacheIndex::UpdateEntry(&hash
, &frecency
, &expTime
, nullptr);
2657 NS_ENSURE_SUCCESS(rv
, rv
);
2659 consecutiveFailures
++;
2660 if (consecutiveFailures
>= cnt
) {
2661 // This doesn't necessarily mean that we've tried to doom every entry
2662 // but we've reached a sane number of tries. It is likely that another
2663 // eviction will start soon. And as said earlier, this normally doesn't
2670 NS_NOTREACHED("We should never get here");
2676 CacheFileIOManager::EvictAll()
2678 LOG(("CacheFileIOManager::EvictAll()"));
2681 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2684 return NS_ERROR_NOT_INITIALIZED
;
2687 nsCOMPtr
<nsIRunnable
> ev
;
2688 ev
= NS_NewRunnableMethod(ioMan
, &CacheFileIOManager::EvictAllInternal
);
2690 rv
= ioMan
->mIOThread
->DispatchAfterPendingOpens(ev
);
2691 if (NS_WARN_IF(NS_FAILED(rv
))) {
2700 class EvictionNotifierRunnable
: public nsRunnable
2707 EvictionNotifierRunnable::Run()
2709 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
2711 obsSvc
->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
2716 } // anonymous namespace
2719 CacheFileIOManager::EvictAllInternal()
2721 LOG(("CacheFileIOManager::EvictAllInternal()"));
2725 MOZ_ASSERT(mIOThread
->IsCurrentThread());
2727 nsRefPtr
<EvictionNotifierRunnable
> r
= new EvictionNotifierRunnable();
2729 if (!mCacheDirectory
) {
2730 // This is a kind of hack. Somebody called EvictAll() without a profile.
2731 // This happens in xpcshell tests that use cache without profile. We need
2732 // to notify observers in this case since the tests are waiting for it.
2733 NS_DispatchToMainThread(r
);
2734 return NS_ERROR_FILE_INVALID_PATH
;
2737 if (mShuttingDown
) {
2738 return NS_ERROR_NOT_INITIALIZED
;
2741 if (!mTreeCreated
) {
2742 rv
= CreateCacheTree();
2743 if (NS_FAILED(rv
)) {
2748 // Doom all active handles
2749 nsTArray
<nsRefPtr
<CacheFileHandle
> > handles
;
2750 mHandles
.GetActiveHandles(&handles
);
2752 for (uint32_t i
= 0; i
< handles
.Length(); ++i
) {
2753 rv
= DoomFileInternal(handles
[i
]);
2754 if (NS_WARN_IF(NS_FAILED(rv
))) {
2755 LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
2756 "[handle=%p]", handles
[i
].get()));
2760 nsCOMPtr
<nsIFile
> file
;
2761 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
2762 if (NS_WARN_IF(NS_FAILED(rv
))) {
2766 rv
= file
->AppendNative(NS_LITERAL_CSTRING(kEntriesDir
));
2767 if (NS_WARN_IF(NS_FAILED(rv
))) {
2771 // Trash current entries directory
2772 rv
= TrashDirectory(file
);
2773 if (NS_WARN_IF(NS_FAILED(rv
))) {
2777 // Files are now inaccessible in entries directory, notify observers.
2778 NS_DispatchToMainThread(r
);
2780 // Create a new empty entries directory
2781 rv
= CheckAndCreateDir(mCacheDirectory
, kEntriesDir
, false);
2782 if (NS_WARN_IF(NS_FAILED(rv
))) {
2786 CacheIndex::RemoveAll();
2793 CacheFileIOManager::EvictByContext(nsILoadContextInfo
*aLoadContextInfo
)
2795 LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
2799 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
2802 return NS_ERROR_NOT_INITIALIZED
;
2805 nsCOMPtr
<nsIRunnable
> ev
;
2806 ev
= NS_NewRunnableMethodWithArg
<nsCOMPtr
<nsILoadContextInfo
> >
2807 (ioMan
, &CacheFileIOManager::EvictByContextInternal
, aLoadContextInfo
);
2809 rv
= ioMan
->mIOThread
->DispatchAfterPendingOpens(ev
);
2810 if (NS_WARN_IF(NS_FAILED(rv
))) {
2818 CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo
*aLoadContextInfo
)
2820 LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
2821 "anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo
,
2822 aLoadContextInfo
->IsAnonymous(), aLoadContextInfo
->IsInBrowserElement(),
2823 aLoadContextInfo
->AppId()));
2827 MOZ_ASSERT(mIOThread
->IsCurrentThread());
2829 MOZ_ASSERT(!aLoadContextInfo
->IsPrivate());
2830 if (aLoadContextInfo
->IsPrivate()) {
2831 return NS_ERROR_INVALID_ARG
;
2834 if (!mCacheDirectory
) {
2835 return NS_ERROR_FILE_INVALID_PATH
;
2838 if (mShuttingDown
) {
2839 return NS_ERROR_NOT_INITIALIZED
;
2842 if (!mTreeCreated
) {
2843 rv
= CreateCacheTree();
2844 if (NS_FAILED(rv
)) {
2849 // Doom all active handles that matches the load context
2850 nsTArray
<nsRefPtr
<CacheFileHandle
> > handles
;
2851 mHandles
.GetActiveHandles(&handles
);
2853 for (uint32_t i
= 0; i
< handles
.Length(); ++i
) {
2855 rv
= CacheFileUtils::KeyMatchesLoadContextInfo(handles
[i
]->Key(),
2858 if (NS_FAILED(rv
)) {
2859 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
2860 "handle! [handle=%p, key=%s]", handles
[i
].get(),
2861 handles
[i
]->Key().get()));
2862 MOZ_CRASH("Unexpected error!");
2866 rv
= DoomFileInternal(handles
[i
]);
2867 if (NS_WARN_IF(NS_FAILED(rv
))) {
2868 LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
2869 " [handle=%p]", handles
[i
].get()));
2874 if (!mContextEvictor
) {
2875 mContextEvictor
= new CacheFileContextEvictor();
2876 mContextEvictor
->Init(mCacheDirectory
);
2879 mContextEvictor
->AddContext(aLoadContextInfo
);
2886 CacheFileIOManager::CacheIndexStateChanged()
2888 LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
2892 // CacheFileIOManager lives longer than CacheIndex so gInstance must be
2894 MOZ_ASSERT(gInstance
);
2896 // We have to re-distatch even if we are on IO thread to prevent reentering
2897 // the lock in CacheIndex
2898 nsCOMPtr
<nsIRunnable
> ev
;
2899 ev
= NS_NewRunnableMethod(
2900 gInstance
, &CacheFileIOManager::CacheIndexStateChangedInternal
);
2902 nsCOMPtr
<nsIEventTarget
> ioTarget
= IOTarget();
2903 MOZ_ASSERT(ioTarget
);
2905 rv
= ioTarget
->Dispatch(ev
, nsIEventTarget::DISPATCH_NORMAL
);
2906 if (NS_WARN_IF(NS_FAILED(rv
))) {
2914 CacheFileIOManager::CacheIndexStateChangedInternal()
2916 if (mShuttingDown
) {
2917 // ignore notification during shutdown
2921 if (!mContextEvictor
) {
2925 mContextEvictor
->CacheIndexStateChanged();
2930 CacheFileIOManager::TrashDirectory(nsIFile
*aFile
)
2934 aFile
->GetNativePath(path
);
2936 LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path
.get()));
2940 MOZ_ASSERT(mIOThread
->IsCurrentThread());
2941 MOZ_ASSERT(mCacheDirectory
);
2943 // When the directory is empty, it is cheaper to remove it directly instead of
2944 // using the trash mechanism.
2946 rv
= IsEmptyDirectory(aFile
, &isEmpty
);
2947 NS_ENSURE_SUCCESS(rv
, rv
);
2950 rv
= aFile
->Remove(false);
2951 LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
2957 nsCOMPtr
<nsIFile
> dirCheck
;
2958 rv
= aFile
->GetParent(getter_AddRefs(dirCheck
));
2959 NS_ENSURE_SUCCESS(rv
, rv
);
2961 bool equals
= false;
2962 rv
= dirCheck
->Equals(mCacheDirectory
, &equals
);
2963 NS_ENSURE_SUCCESS(rv
, rv
);
2968 nsCOMPtr
<nsIFile
> dir
, trash
;
2971 rv
= aFile
->Clone(getter_AddRefs(dir
));
2972 NS_ENSURE_SUCCESS(rv
, rv
);
2974 rv
= aFile
->Clone(getter_AddRefs(trash
));
2975 NS_ENSURE_SUCCESS(rv
, rv
);
2977 srand(static_cast<unsigned>(PR_Now()));
2980 leaf
.AppendInt(rand());
2981 rv
= trash
->SetNativeLeafName(leaf
);
2982 NS_ENSURE_SUCCESS(rv
, rv
);
2985 if (NS_SUCCEEDED(trash
->Exists(&exists
)) && !exists
) {
2990 LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
2993 rv
= dir
->MoveToNative(nullptr, leaf
);
2994 NS_ENSURE_SUCCESS(rv
, rv
);
2996 StartRemovingTrash();
3002 CacheFileIOManager::OnTrashTimer(nsITimer
*aTimer
, void *aClosure
)
3004 LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer
,
3007 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
3013 ioMan
->mTrashTimer
= nullptr;
3014 ioMan
->StartRemovingTrash();
3018 CacheFileIOManager::StartRemovingTrash()
3020 LOG(("CacheFileIOManager::StartRemovingTrash()"));
3024 MOZ_ASSERT(mIOThread
->IsCurrentThread());
3026 if (mShuttingDown
) {
3027 return NS_ERROR_NOT_INITIALIZED
;
3030 if (!mCacheDirectory
) {
3031 return NS_ERROR_FILE_INVALID_PATH
;
3035 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
3039 if (mRemovingTrashDirs
) {
3040 LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
3045 uint32_t elapsed
= (TimeStamp::NowLoRes() - mStartTime
).ToMilliseconds();
3046 if (elapsed
< kRemoveTrashStartDelay
) {
3047 nsCOMPtr
<nsITimer
> timer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
3048 NS_ENSURE_SUCCESS(rv
, rv
);
3050 nsCOMPtr
<nsIEventTarget
> ioTarget
= IOTarget();
3051 MOZ_ASSERT(ioTarget
);
3053 rv
= timer
->SetTarget(ioTarget
);
3054 NS_ENSURE_SUCCESS(rv
, rv
);
3056 rv
= timer
->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer
, nullptr,
3057 kRemoveTrashStartDelay
- elapsed
,
3058 nsITimer::TYPE_ONE_SHOT
);
3059 NS_ENSURE_SUCCESS(rv
, rv
);
3061 mTrashTimer
.swap(timer
);
3065 nsCOMPtr
<nsIRunnable
> ev
;
3066 ev
= NS_NewRunnableMethod(this,
3067 &CacheFileIOManager::RemoveTrashInternal
);
3069 rv
= mIOThread
->Dispatch(ev
, CacheIOThread::EVICT
);
3070 NS_ENSURE_SUCCESS(rv
, rv
);
3072 mRemovingTrashDirs
= true;
3077 CacheFileIOManager::RemoveTrashInternal()
3079 LOG(("CacheFileIOManager::RemoveTrashInternal()"));
3083 MOZ_ASSERT(mIOThread
->IsCurrentThread());
3085 if (mShuttingDown
) {
3086 return NS_ERROR_NOT_INITIALIZED
;
3089 MOZ_ASSERT(!mTrashTimer
);
3090 MOZ_ASSERT(mRemovingTrashDirs
);
3092 if (!mTreeCreated
) {
3093 rv
= CreateCacheTree();
3094 if (NS_FAILED(rv
)) {
3099 // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
3100 // here and set it again once we dispatch a continuation event. By doing so,
3101 // we don't have to drop the flag on any possible early return.
3102 mRemovingTrashDirs
= false;
3105 if (CacheIOThread::YieldAndRerun()) {
3106 LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
3107 "higher level events."));
3108 mRemovingTrashDirs
= true;
3112 // Find some trash directory
3114 MOZ_ASSERT(!mTrashDirEnumerator
);
3116 rv
= FindTrashDirToRemove();
3117 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
3118 LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
3122 NS_ENSURE_SUCCESS(rv
, rv
);
3124 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
3125 rv
= mTrashDir
->GetDirectoryEntries(getter_AddRefs(enumerator
));
3126 if (NS_SUCCEEDED(rv
)) {
3127 mTrashDirEnumerator
= do_QueryInterface(enumerator
, &rv
);
3128 NS_ENSURE_SUCCESS(rv
, rv
);
3131 continue; // check elapsed time
3134 // We null out mTrashDirEnumerator once we remove all files in the
3135 // directory, so remove the trash directory if we don't have enumerator.
3136 if (!mTrashDirEnumerator
) {
3137 rv
= mTrashDir
->Remove(false);
3138 if (NS_FAILED(rv
)) {
3139 // There is no reason why removing an empty directory should fail, but
3140 // if it does, we should continue and try to remove all other trash
3142 nsAutoCString leafName
;
3143 mTrashDir
->GetNativeLeafName(leafName
);
3144 mFailedTrashDirs
.AppendElement(leafName
);
3145 LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
3146 "trashdir. [name=%s]", leafName
.get()));
3149 mTrashDir
= nullptr;
3150 continue; // check elapsed time
3153 nsCOMPtr
<nsIFile
> file
;
3154 rv
= mTrashDirEnumerator
->GetNextFile(getter_AddRefs(file
));
3156 mTrashDirEnumerator
->Close();
3157 mTrashDirEnumerator
= nullptr;
3158 continue; // check elapsed time
3161 file
->IsDirectory(&isDir
);
3163 NS_WARNING("Found a directory in a trash directory! It will be removed "
3164 "recursively, but this can block IO thread for a while!");
3167 file
->GetNativePath(path
);
3169 LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
3170 "directory! It will be removed recursively, but this can block IO "
3171 "thread for a while! [file=%s]", path
.get()));
3173 file
->Remove(isDir
);
3177 NS_NOTREACHED("We should never get here");
3182 CacheFileIOManager::FindTrashDirToRemove()
3184 LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
3188 // We call this method on the main thread during shutdown when user wants to
3189 // remove all cache files.
3190 MOZ_ASSERT(mIOThread
->IsCurrentThread() || mShuttingDown
);
3192 nsCOMPtr
<nsISimpleEnumerator
> iter
;
3193 rv
= mCacheDirectory
->GetDirectoryEntries(getter_AddRefs(iter
));
3194 NS_ENSURE_SUCCESS(rv
, rv
);
3197 nsCOMPtr
<nsISupports
> elem
;
3199 while (NS_SUCCEEDED(iter
->HasMoreElements(&more
)) && more
) {
3200 rv
= iter
->GetNext(getter_AddRefs(elem
));
3201 if (NS_FAILED(rv
)) {
3205 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(elem
);
3211 file
->IsDirectory(&isDir
);
3216 nsAutoCString leafName
;
3217 rv
= file
->GetNativeLeafName(leafName
);
3218 if (NS_FAILED(rv
)) {
3222 if (leafName
.Length() < strlen(kTrashDir
)) {
3226 if (!StringBeginsWith(leafName
, NS_LITERAL_CSTRING(kTrashDir
))) {
3230 if (mFailedTrashDirs
.Contains(leafName
)) {
3234 LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
3241 // When we're here we've tried to delete all trash directories. Clear
3242 // mFailedTrashDirs so we will try to delete them again when we start removing
3243 // trash directories next time.
3244 mFailedTrashDirs
.Clear();
3245 return NS_ERROR_NOT_AVAILABLE
;
3250 CacheFileIOManager::InitIndexEntry(CacheFileHandle
*aHandle
,
3255 LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
3256 ", inBrowser=%d]", aHandle
, aAppId
, aAnonymous
, aInBrowser
));
3259 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
3261 if (aHandle
->IsClosed() || !ioMan
) {
3262 return NS_ERROR_NOT_INITIALIZED
;
3265 if (aHandle
->IsSpecialFile()) {
3266 return NS_ERROR_UNEXPECTED
;
3269 nsRefPtr
<InitIndexEntryEvent
> ev
=
3270 new InitIndexEntryEvent(aHandle
, aAppId
, aAnonymous
, aInBrowser
);
3271 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::WRITE
);
3272 NS_ENSURE_SUCCESS(rv
, rv
);
3279 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle
*aHandle
,
3280 const uint32_t *aFrecency
,
3281 const uint32_t *aExpirationTime
)
3283 LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
3284 "expirationTime=%s]", aHandle
,
3285 aFrecency
? nsPrintfCString("%u", *aFrecency
).get() : "",
3286 aExpirationTime
? nsPrintfCString("%u", *aExpirationTime
).get() : ""));
3289 nsRefPtr
<CacheFileIOManager
> ioMan
= gInstance
;
3291 if (aHandle
->IsClosed() || !ioMan
) {
3292 return NS_ERROR_NOT_INITIALIZED
;
3295 if (aHandle
->IsSpecialFile()) {
3296 return NS_ERROR_UNEXPECTED
;
3299 nsRefPtr
<UpdateIndexEntryEvent
> ev
=
3300 new UpdateIndexEntryEvent(aHandle
, aFrecency
, aExpirationTime
);
3301 rv
= ioMan
->mIOThread
->Dispatch(ev
, CacheIOThread::WRITE
);
3302 NS_ENSURE_SUCCESS(rv
, rv
);
3308 CacheFileIOManager::CreateFile(CacheFileHandle
*aHandle
)
3310 MOZ_ASSERT(!aHandle
->mFD
);
3311 MOZ_ASSERT(aHandle
->mFile
);
3315 if (aHandle
->IsDoomed()) {
3316 nsCOMPtr
<nsIFile
> file
;
3318 rv
= GetDoomedFile(getter_AddRefs(file
));
3319 NS_ENSURE_SUCCESS(rv
, rv
);
3321 aHandle
->mFile
.swap(file
);
3324 if (NS_SUCCEEDED(aHandle
->mFile
->Exists(&exists
)) && exists
) {
3325 NS_WARNING("Found a file that should not exist!");
3329 rv
= OpenNSPRHandle(aHandle
, true);
3330 NS_ENSURE_SUCCESS(rv
, rv
);
3332 aHandle
->mFileSize
= 0;
3338 CacheFileIOManager::HashToStr(const SHA1Sum::Hash
*aHash
, nsACString
&_retval
)
3341 const char hexChars
[] = {'0', '1', '2', '3', '4', '5', '6', '7',
3342 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
3343 for (uint32_t i
=0 ; i
<sizeof(SHA1Sum::Hash
) ; i
++) {
3344 _retval
.Append(hexChars
[(*aHash
)[i
] >> 4]);
3345 _retval
.Append(hexChars
[(*aHash
)[i
] & 0xF]);
3351 CacheFileIOManager::StrToHash(const nsACString
&aHash
, SHA1Sum::Hash
*_retval
)
3353 if (aHash
.Length() != 2*sizeof(SHA1Sum::Hash
)) {
3354 return NS_ERROR_INVALID_ARG
;
3357 for (uint32_t i
=0 ; i
<aHash
.Length() ; i
++) {
3360 if (aHash
[i
] >= '0' && aHash
[i
] <= '9') {
3361 value
= aHash
[i
] - '0';
3362 } else if (aHash
[i
] >= 'A' && aHash
[i
] <= 'F') {
3363 value
= aHash
[i
] - 'A' + 10;
3364 } else if (aHash
[i
] >= 'a' && aHash
[i
] <= 'f') {
3365 value
= aHash
[i
] - 'a' + 10;
3367 return NS_ERROR_INVALID_ARG
;
3371 (reinterpret_cast<uint8_t *>(_retval
))[i
/2] = value
<< 4;
3373 (reinterpret_cast<uint8_t *>(_retval
))[i
/2] += value
;
3381 CacheFileIOManager::GetFile(const SHA1Sum::Hash
*aHash
, nsIFile
**_retval
)
3384 nsCOMPtr
<nsIFile
> file
;
3385 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
3386 NS_ENSURE_SUCCESS(rv
, rv
);
3388 rv
= file
->AppendNative(NS_LITERAL_CSTRING(kEntriesDir
));
3389 NS_ENSURE_SUCCESS(rv
, rv
);
3391 nsAutoCString leafName
;
3392 HashToStr(aHash
, leafName
);
3394 rv
= file
->AppendNative(leafName
);
3395 NS_ENSURE_SUCCESS(rv
, rv
);
3397 file
.swap(*_retval
);
3402 CacheFileIOManager::GetSpecialFile(const nsACString
&aKey
, nsIFile
**_retval
)
3405 nsCOMPtr
<nsIFile
> file
;
3406 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
3407 NS_ENSURE_SUCCESS(rv
, rv
);
3409 rv
= file
->AppendNative(aKey
);
3410 NS_ENSURE_SUCCESS(rv
, rv
);
3412 file
.swap(*_retval
);
3417 CacheFileIOManager::GetDoomedFile(nsIFile
**_retval
)
3420 nsCOMPtr
<nsIFile
> file
;
3421 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
3422 NS_ENSURE_SUCCESS(rv
, rv
);
3424 rv
= file
->AppendNative(NS_LITERAL_CSTRING(kDoomedDir
));
3425 NS_ENSURE_SUCCESS(rv
, rv
);
3427 rv
= file
->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
3428 NS_ENSURE_SUCCESS(rv
, rv
);
3430 srand(static_cast<unsigned>(PR_Now()));
3431 nsAutoCString leafName
;
3435 leafName
.AppendInt(rand());
3436 rv
= file
->SetNativeLeafName(leafName
);
3437 NS_ENSURE_SUCCESS(rv
, rv
);
3440 if (NS_SUCCEEDED(file
->Exists(&exists
)) && !exists
) {
3444 leafName
.Truncate();
3447 // Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
3449 file
.swap(*_retval
);
3454 CacheFileIOManager::IsEmptyDirectory(nsIFile
*aFile
, bool *_retval
)
3456 MOZ_ASSERT(mIOThread
->IsCurrentThread());
3460 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
3461 rv
= aFile
->GetDirectoryEntries(getter_AddRefs(enumerator
));
3462 NS_ENSURE_SUCCESS(rv
, rv
);
3464 bool hasMoreElements
= false;
3465 rv
= enumerator
->HasMoreElements(&hasMoreElements
);
3466 NS_ENSURE_SUCCESS(rv
, rv
);
3468 *_retval
= !hasMoreElements
;
3473 CacheFileIOManager::CheckAndCreateDir(nsIFile
*aFile
, const char *aDir
,
3474 bool aEnsureEmptyDir
)
3478 nsCOMPtr
<nsIFile
> file
;
3482 nsAutoCString
dir(aDir
);
3483 rv
= aFile
->Clone(getter_AddRefs(file
));
3484 NS_ENSURE_SUCCESS(rv
, rv
);
3485 rv
= file
->AppendNative(dir
);
3486 NS_ENSURE_SUCCESS(rv
, rv
);
3489 bool exists
= false;
3490 rv
= file
->Exists(&exists
);
3491 if (NS_SUCCEEDED(rv
) && exists
) {
3492 bool isDirectory
= false;
3493 rv
= file
->IsDirectory(&isDirectory
);
3494 if (NS_FAILED(rv
) || !isDirectory
) {
3495 // Try to remove the file
3496 rv
= file
->Remove(false);
3497 if (NS_SUCCEEDED(rv
)) {
3501 NS_ENSURE_SUCCESS(rv
, rv
);
3504 if (aEnsureEmptyDir
&& NS_SUCCEEDED(rv
) && exists
) {
3506 rv
= IsEmptyDirectory(file
, &isEmpty
);
3507 NS_ENSURE_SUCCESS(rv
, rv
);
3510 rv
= TrashDirectory(file
);
3511 NS_ENSURE_SUCCESS(rv
, rv
);
3517 if (NS_SUCCEEDED(rv
) && !exists
) {
3518 rv
= file
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
3520 if (NS_FAILED(rv
)) {
3521 NS_WARNING("Cannot create directory");
3522 return NS_ERROR_FAILURE
;
3529 CacheFileIOManager::CreateCacheTree()
3531 MOZ_ASSERT(mIOThread
->IsCurrentThread());
3532 MOZ_ASSERT(!mTreeCreated
);
3534 if (!mCacheDirectory
) {
3535 return NS_ERROR_FILE_INVALID_PATH
;
3540 // ensure parent directory exists
3541 nsCOMPtr
<nsIFile
> parentDir
;
3542 rv
= mCacheDirectory
->GetParent(getter_AddRefs(parentDir
));
3543 NS_ENSURE_SUCCESS(rv
, rv
);
3544 rv
= CheckAndCreateDir(parentDir
, nullptr, false);
3545 NS_ENSURE_SUCCESS(rv
, rv
);
3547 // ensure cache directory exists
3548 rv
= CheckAndCreateDir(mCacheDirectory
, nullptr, false);
3549 NS_ENSURE_SUCCESS(rv
, rv
);
3551 // ensure entries directory exists
3552 rv
= CheckAndCreateDir(mCacheDirectory
, kEntriesDir
, false);
3553 NS_ENSURE_SUCCESS(rv
, rv
);
3555 // ensure doomed directory exists
3556 rv
= CheckAndCreateDir(mCacheDirectory
, kDoomedDir
, true);
3557 NS_ENSURE_SUCCESS(rv
, rv
);
3559 mTreeCreated
= true;
3561 if (!mContextEvictor
) {
3562 nsRefPtr
<CacheFileContextEvictor
> contextEvictor
;
3563 contextEvictor
= new CacheFileContextEvictor();
3565 // Init() method will try to load unfinished contexts from the disk. Store
3566 // the evictor as a member only when there is some unfinished job.
3567 contextEvictor
->Init(mCacheDirectory
);
3568 if (contextEvictor
->ContextsCount()) {
3569 contextEvictor
.swap(mContextEvictor
);
3573 StartRemovingTrash();
3579 CacheFileIOManager::OpenNSPRHandle(CacheFileHandle
*aHandle
, bool aCreate
)
3581 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
3582 MOZ_ASSERT(!aHandle
->mFD
);
3583 MOZ_ASSERT(mHandlesByLastUsed
.IndexOf(aHandle
) == mHandlesByLastUsed
.NoIndex
);
3584 MOZ_ASSERT(mHandlesByLastUsed
.Length() <= kOpenHandlesLimit
);
3585 MOZ_ASSERT((aCreate
&& !aHandle
->mFileExists
) ||
3586 (!aCreate
&& aHandle
->mFileExists
));
3590 if (mHandlesByLastUsed
.Length() == kOpenHandlesLimit
) {
3591 // close handle that hasn't been used for the longest time
3592 rv
= ReleaseNSPRHandleInternal(mHandlesByLastUsed
[0]);
3593 NS_ENSURE_SUCCESS(rv
, rv
);
3597 rv
= aHandle
->mFile
->OpenNSPRFileDesc(
3598 PR_RDWR
| PR_CREATE_FILE
| PR_TRUNCATE
, 0600, &aHandle
->mFD
);
3599 NS_ENSURE_SUCCESS(rv
, rv
);
3601 aHandle
->mFileExists
= true;
3603 rv
= aHandle
->mFile
->OpenNSPRFileDesc(PR_RDWR
, 0600, &aHandle
->mFD
);
3604 if (NS_ERROR_FILE_NOT_FOUND
== rv
) {
3605 LOG((" file doesn't exists"));
3606 aHandle
->mFileExists
= false;
3607 return DoomFileInternal(aHandle
);
3609 NS_ENSURE_SUCCESS(rv
, rv
);
3612 mHandlesByLastUsed
.AppendElement(aHandle
);
3617 CacheFileIOManager::NSPRHandleUsed(CacheFileHandle
*aHandle
)
3619 MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
3620 MOZ_ASSERT(aHandle
->mFD
);
3622 DebugOnly
<bool> found
;
3623 found
= mHandlesByLastUsed
.RemoveElement(aHandle
);
3626 mHandlesByLastUsed
.AppendElement(aHandle
);
3630 CacheFileIOManager::SyncRemoveDir(nsIFile
*aFile
, const char *aDir
)
3633 nsCOMPtr
<nsIFile
> file
;
3638 rv
= aFile
->Clone(getter_AddRefs(file
));
3639 if (NS_WARN_IF(NS_FAILED(rv
))) {
3643 rv
= file
->AppendNative(nsDependentCString(aDir
));
3644 if (NS_WARN_IF(NS_FAILED(rv
))) {
3651 file
->GetNativePath(path
);
3654 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
3657 rv
= file
->Remove(true);
3658 if (NS_WARN_IF(NS_FAILED(rv
))) {
3659 LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08x]",
3667 CacheFileIOManager::SyncRemoveAllCacheFiles()
3669 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
3673 SyncRemoveDir(mCacheDirectory
, kEntriesDir
);
3674 SyncRemoveDir(mCacheDirectory
, kDoomedDir
);
3676 // Clear any intermediate state of trash dir enumeration.
3677 mFailedTrashDirs
.Clear();
3678 mTrashDir
= nullptr;
3681 // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
3682 rv
= FindTrashDirToRemove();
3683 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
3684 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
3688 if (NS_WARN_IF(NS_FAILED(rv
))) {
3689 LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
3690 "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08x]",
3695 rv
= SyncRemoveDir(mTrashDir
, nullptr);
3696 if (NS_FAILED(rv
)) {
3697 nsAutoCString leafName
;
3698 mTrashDir
->GetNativeLeafName(leafName
);
3699 mFailedTrashDirs
.AppendElement(leafName
);
3704 // Returns default ("smart") size (in KB) of cache, given available disk space
3707 SmartCacheSize(const uint32_t availKB
)
3709 uint32_t maxSize
= kMaxCacheSizeKB
;
3711 if (availKB
> 100 * 1024 * 1024) {
3712 return maxSize
; // skip computing if we're over 100 GB
3715 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
3716 // don't shrink cache and evict items every time we startup (it's important
3717 // that we don't slow down startup benchmarks).
3718 uint32_t sz10MBs
= 0;
3719 uint32_t avail10MBs
= availKB
/ (1024*10);
3721 // .5% of space above 25 GB
3722 if (avail10MBs
> 2500) {
3723 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 2500)*.005);
3726 // 1% of space between 7GB -> 25 GB
3727 if (avail10MBs
> 700) {
3728 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 700)*.01);
3731 // 5% of space between 500 MB -> 7 GB
3732 if (avail10MBs
> 50) {
3733 sz10MBs
+= static_cast<uint32_t>((avail10MBs
- 50)*.05);
3738 // On Android, smaller/older devices may have very little storage and
3739 // device owners may be sensitive to storage footprint: Use a smaller
3740 // percentage of available space and a smaller minimum.
3742 // 20% of space up to 500 MB (10 MB min)
3743 sz10MBs
+= std::max
<uint32_t>(1, static_cast<uint32_t>(avail10MBs
* .2));
3745 // 40% of space up to 500 MB (50 MB min)
3746 sz10MBs
+= std::max
<uint32_t>(5, static_cast<uint32_t>(avail10MBs
* .4));
3749 return std::min
<uint32_t>(maxSize
, sz10MBs
* 10 * 1024);
3753 CacheFileIOManager::UpdateSmartCacheSize(int64_t aFreeSpace
)
3755 MOZ_ASSERT(mIOThread
->IsCurrentThread());
3759 if (!CacheObserver::UseNewCache()) {
3760 return NS_ERROR_NOT_AVAILABLE
;
3763 if (!CacheObserver::SmartCacheSizeEnabled()) {
3764 return NS_ERROR_NOT_AVAILABLE
;
3767 // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
3768 static const TimeDuration kUpdateLimit
=
3769 TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval
);
3770 if (!mLastSmartSizeTime
.IsNull() &&
3771 (TimeStamp::NowLoRes() - mLastSmartSizeTime
) < kUpdateLimit
) {
3775 // Do not compute smart size when cache size is not reliable.
3776 bool isUpToDate
= false;
3777 CacheIndex::IsUpToDate(&isUpToDate
);
3779 return NS_ERROR_NOT_AVAILABLE
;
3782 uint32_t cacheUsage
;
3783 rv
= CacheIndex::GetCacheSize(&cacheUsage
);
3784 if (NS_WARN_IF(NS_FAILED(rv
))) {
3785 LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
3786 "[rv=0x%08x]", rv
));
3790 mLastSmartSizeTime
= TimeStamp::NowLoRes();
3792 uint32_t smartSize
= SmartCacheSize(static_cast<uint32_t>(aFreeSpace
/ 1024) +
3795 if (smartSize
== (CacheObserver::DiskCacheCapacity() >> 10)) {
3796 // Smart size has not changed.
3800 CacheObserver::SetDiskCacheCapacity(smartSize
<< 10);
3809 // A helper class that dispatches and waits for an event that gets result of
3810 // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
3811 // to safely get handles memory report.
3812 // We must do this, since the handle list is only accessed and managed w/o
3813 // locking on the I/O thread. That is by design.
3814 class SizeOfHandlesRunnable
: public nsRunnable
3817 SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf
,
3818 CacheFileHandles
const &handles
,
3819 nsTArray
<CacheFileHandle
*> const &specialHandles
)
3820 : mMonitor("SizeOfHandlesRunnable.mMonitor")
3821 , mMallocSizeOf(mallocSizeOf
)
3823 , mSpecialHandles(specialHandles
)
3827 size_t Get(CacheIOThread
* thread
)
3829 nsCOMPtr
<nsIEventTarget
> target
= thread
->Target();
3831 NS_ERROR("If we have the I/O thread we also must have the I/O target");
3835 mozilla::MonitorAutoLock
mon(mMonitor
);
3836 nsresult rv
= target
->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL
);
3837 if (NS_FAILED(rv
)) {
3838 NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
3848 mozilla::MonitorAutoLock
mon(mMonitor
);
3849 // Excluding this since the object itself is a member of CacheFileIOManager
3850 // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
3851 mSize
= mHandles
.SizeOfExcludingThis(mMallocSizeOf
);
3852 for (uint32_t i
= 0; i
< mSpecialHandles
.Length(); ++i
) {
3853 mSize
+= mSpecialHandles
[i
]->SizeOfIncludingThis(mMallocSizeOf
);
3861 mozilla::Monitor mMonitor
;
3862 mozilla::MallocSizeOf mMallocSizeOf
;
3863 CacheFileHandles
const &mHandles
;
3864 nsTArray
<CacheFileHandle
*> const &mSpecialHandles
;
3871 CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf
) const
3874 nsCOMPtr
<nsISizeOf
> sizeOf
;
3877 n
+= mIOThread
->SizeOfIncludingThis(mallocSizeOf
);
3879 // mHandles and mSpecialHandles must be accessed only on the I/O thread,
3880 // must sync dispatch.
3881 nsRefPtr
<SizeOfHandlesRunnable
> sizeOfHandlesRunnable
=
3882 new SizeOfHandlesRunnable(mallocSizeOf
, mHandles
, mSpecialHandles
);
3883 n
+= sizeOfHandlesRunnable
->Get(mIOThread
);
3886 // mHandlesByLastUsed just refers handles reported by mHandles.
3888 sizeOf
= do_QueryInterface(mCacheDirectory
);
3890 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3892 sizeOf
= do_QueryInterface(mMetadataWritesTimer
);
3894 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3896 sizeOf
= do_QueryInterface(mTrashTimer
);
3898 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3900 sizeOf
= do_QueryInterface(mTrashDir
);
3902 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3904 for (uint32_t i
= 0; i
< mFailedTrashDirs
.Length(); ++i
) {
3905 n
+= mFailedTrashDirs
[i
].SizeOfExcludingThisIfUnshared(mallocSizeOf
);
3913 CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
)
3918 return gInstance
->SizeOfExcludingThisInternal(mallocSizeOf
);
3923 CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
)
3925 return mallocSizeOf(gInstance
) + SizeOfExcludingThis(mallocSizeOf
);