netwerk/cache2/CacheFileIOManager.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     5 #include "CacheLog.h"
     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"
    16 #include "nsIFile.h"
    17 #include "CacheFileContextEvictor.h"
    18 #include "nsITimer.h"
    19 #include "nsISimpleEnumerator.h"
    20 #include "nsIDirectoryEnumerator.h"
    21 #include "nsIObserverService.h"
    22 #include "nsISizeOf.h"
    23 #include "mozilla/Telemetry.h"
    24 #include "mozilla/DebugOnly.h"
    25 #include "mozilla/Services.h"
    26 #include "nsDirectoryServiceUtils.h"
    27 #include "nsAppDirectoryServiceDefs.h"
    28 #include "private/pprio.h"
    29 #include "mozilla/VisualEventTracer.h"
    30 #include "mozilla/Preferences.h"
    32 // include files for ftruncate (or equivalent)
    33 #if defined(XP_UNIX)
    34 #include <unistd.h>
    35 #elif defined(XP_WIN)
    36 #include <windows.h>
    37 #undef CreateFile
    38 #undef CREATE_NEW
    39 #else
    40 // XXX add necessary include file for ftruncate (or equivalent)
    41 #endif
    44 namespace mozilla {
    45 namespace net {
    47 #define kOpenHandlesLimit        64
    48 #define kMetadataWriteDelay      5000
    49 #define kRemoveTrashStartDelay   60000 // in milliseconds
    50 #define kSmartSizeUpdateInterval 60000 // in milliseconds
    52 #ifdef ANDROID
    53 const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
    54 #else
    55 const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
    56 #endif
    58 bool
    59 CacheFileHandle::DispatchRelease()
    60 {
    61   if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
    62     return false;
    63   }
    65   nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
    66   if (!ioTarget) {
    67     return false;
    68   }
    70   nsRefPtr<nsRunnableMethod<CacheFileHandle, MozExternalRefCountType, false> > event =
    71     NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release);
    72   nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    73   if (NS_FAILED(rv)) {
    74     return false;
    75   }
    77   return true;
    78 }
    80 NS_IMPL_ADDREF(CacheFileHandle)
    81 NS_IMETHODIMP_(MozExternalRefCountType)
    82 CacheFileHandle::Release()
    83 {
    84   nsrefcnt count = mRefCnt - 1;
    85   if (DispatchRelease()) {
    86     // Redispatched to the IO thread.
    87     return count;
    88   }
    90   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    92   LOG(("CacheFileHandle::Release() [this=%p, refcnt=%d]", this, mRefCnt.get()));
    93   NS_PRECONDITION(0 != mRefCnt, "dup release");
    94   count = --mRefCnt;
    95   NS_LOG_RELEASE(this, count, "CacheFileHandle");
    97   if (0 == count) {
    98     mRefCnt = 1;
    99     delete (this);
   100     return 0;
   101   }
   103   return count;
   104 }
   106 NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
   107   NS_INTERFACE_MAP_ENTRY(nsISupports)
   108 NS_INTERFACE_MAP_END_THREADSAFE
   110 CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority)
   111   : mHash(aHash)
   112   , mIsDoomed(false)
   113   , mPriority(aPriority)
   114   , mClosed(false)
   115   , mInvalid(false)
   116   , mFileExists(false)
   117   , mFileSize(-1)
   118   , mFD(nullptr)
   119 {
   120   LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
   121        , this, LOGSHA1(aHash)));
   122 }
   124 CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority)
   125   : mHash(nullptr)
   126   , mIsDoomed(false)
   127   , mPriority(aPriority)
   128   , mClosed(false)
   129   , mInvalid(false)
   130   , mFileExists(false)
   131   , mFileSize(-1)
   132   , mFD(nullptr)
   133   , mKey(aKey)
   134 {
   135   LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
   136        PromiseFlatCString(aKey).get()));
   137 }
   139 CacheFileHandle::~CacheFileHandle()
   140 {
   141   LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
   143   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   145   nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
   146   if (ioMan) {
   147     ioMan->CloseHandleInternal(this);
   148   }
   149 }
   151 void
   152 CacheFileHandle::Log()
   153 {
   154   nsAutoCString leafName;
   155   if (mFile) {
   156     mFile->GetNativeLeafName(leafName);
   157   }
   159   if (!mHash) {
   160     // special file
   161     LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
   162          "priority=%d, closed=%d, invalid=%d, "
   163          "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
   164          this, mIsDoomed, mPriority, mClosed, mInvalid,
   165          mFileExists, mFileSize, leafName.get(), mKey.get()));
   166   } else {
   167     LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
   168          "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
   169          "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
   170          this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
   171          mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
   172   }
   173 }
   175 uint32_t
   176 CacheFileHandle::FileSizeInK() const
   177 {
   178   MOZ_ASSERT(mFileSize != -1);
   179   uint64_t size64 = mFileSize;
   181   size64 += 0x3FF;
   182   size64 >>= 10;
   184   uint32_t size;
   185   if (size64 >> 32) {
   186     NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
   187                "truncating to PR_UINT32_MAX");
   188     size = PR_UINT32_MAX;
   189   } else {
   190     size = static_cast<uint32_t>(size64);
   191   }
   193   return size;
   194 }
   196 // Memory reporting
   198 size_t
   199 CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   200 {
   201   size_t n = 0;
   202   nsCOMPtr<nsISizeOf> sizeOf;
   204   sizeOf = do_QueryInterface(mFile);
   205   if (sizeOf) {
   206     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
   207   }
   209   n += mallocSizeOf(mFD);
   210   n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
   211   return n;
   212 }
   214 size_t
   215 CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   216 {
   217   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
   218 }
   220 /******************************************************************************
   221  *  CacheFileHandles::HandleHashKey
   222  *****************************************************************************/
   224 void
   225 CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
   226 {
   227   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   229   mHandles.InsertElementAt(0, aHandle);
   230 }
   232 void
   233 CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
   234 {
   235   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   237   DebugOnly<bool> found;
   238   found = mHandles.RemoveElement(aHandle);
   239   MOZ_ASSERT(found);
   240 }
   242 already_AddRefed<CacheFileHandle>
   243 CacheFileHandles::HandleHashKey::GetNewestHandle()
   244 {
   245   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   247   nsRefPtr<CacheFileHandle> handle;
   248   if (mHandles.Length()) {
   249     handle = mHandles[0];
   250   }
   252   return handle.forget();
   253 }
   255 void
   256 CacheFileHandles::HandleHashKey::GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult)
   257 {
   258   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   260   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   261     CacheFileHandle* handle = mHandles[i];
   262     aResult.AppendElement(handle);
   263   }
   264 }
   266 #ifdef DEBUG
   268 void
   269 CacheFileHandles::HandleHashKey::AssertHandlesState()
   270 {
   271   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   272     CacheFileHandle* handle = mHandles[i];
   273     MOZ_ASSERT(handle->IsDoomed());
   274   }
   275 }
   277 #endif
   279 size_t
   280 CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   281 {
   282   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   284   size_t n = 0;
   285   n += mallocSizeOf(mHash);
   286   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
   287     n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
   288   }
   290   return n;
   291 }
   293 /******************************************************************************
   294  *  CacheFileHandles
   295  *****************************************************************************/
   297 CacheFileHandles::CacheFileHandles()
   298 {
   299   LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
   300   MOZ_COUNT_CTOR(CacheFileHandles);
   301 }
   303 CacheFileHandles::~CacheFileHandles()
   304 {
   305   LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
   306   MOZ_COUNT_DTOR(CacheFileHandles);
   307 }
   309 nsresult
   310 CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
   311                             bool aReturnDoomed,
   312                             CacheFileHandle **_retval)
   313 {
   314   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   315   MOZ_ASSERT(aHash);
   317 #ifdef DEBUG_HANDLES
   318   LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
   319        LOGSHA1(aHash)));
   320 #endif
   322   // find hash entry for key
   323   HandleHashKey *entry = mTable.GetEntry(*aHash);
   324   if (!entry) {
   325     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   326          "no handle entries found", LOGSHA1(aHash)));
   327     return NS_ERROR_NOT_AVAILABLE;
   328   }
   330 #ifdef DEBUG_HANDLES
   331   Log(entry);
   332 #endif
   334   // Check if the entry is doomed
   335   nsRefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
   336   if (!handle) {
   337     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   338          "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   339     return NS_ERROR_NOT_AVAILABLE;
   340   }
   342   if (handle->IsDoomed()) {
   343     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   344          "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   346     // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
   347     if (!aReturnDoomed) {
   348       return NS_ERROR_NOT_AVAILABLE;
   349     }
   350   } else {
   351     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
   352          "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   353   }
   355   handle.forget(_retval);
   356   return NS_OK;
   357 }
   360 nsresult
   361 CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
   362                             bool aPriority,
   363                             CacheFileHandle **_retval)
   364 {
   365   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   366   MOZ_ASSERT(aHash);
   368 #ifdef DEBUG_HANDLES
   369   LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
   370 #endif
   372   // find hash entry for key
   373   HandleHashKey *entry = mTable.PutEntry(*aHash);
   375 #ifdef DEBUG_HANDLES
   376   Log(entry);
   377 #endif
   379 #ifdef DEBUG
   380   entry->AssertHandlesState();
   381 #endif
   383   nsRefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority);
   384   entry->AddHandle(handle);
   386   LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
   387        "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
   389   handle.forget(_retval);
   390   return NS_OK;
   391 }
   393 void
   394 CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
   395 {
   396   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   397   MOZ_ASSERT(aHandle);
   399   if (!aHandle) {
   400     return;
   401   }
   403 #ifdef DEBUG_HANDLES
   404   LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
   405        , aHandle, LOGSHA1(aHandle->Hash())));
   406 #endif
   408   // find hash entry for key
   409   HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
   410   if (!entry) {
   411     MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
   412       "Should find entry when removing a handle before shutdown");
   414     LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   415          "no entries found", LOGSHA1(aHandle->Hash())));
   416     return;
   417   }
   419 #ifdef DEBUG_HANDLES
   420   Log(entry);
   421 #endif
   423   LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   424        "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
   425   entry->RemoveHandle(aHandle);
   427   if (entry->IsEmpty()) {
   428     LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
   429          "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
   430     mTable.RemoveEntry(*entry->Hash());
   431   }
   432 }
   434 static PLDHashOperator
   435 GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
   436 {
   437   nsTArray<nsRefPtr<CacheFileHandle> > *array =
   438     static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
   440   aEntry->GetHandles(*array);
   441   return PL_DHASH_NEXT;
   442 }
   444 void
   445 CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
   446 {
   447   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   448   mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
   449 }
   451 static PLDHashOperator
   452 GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
   453 {
   454   nsTArray<nsRefPtr<CacheFileHandle> > *array =
   455     static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
   457   nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
   458   MOZ_ASSERT(handle);
   460   if (!handle->IsDoomed()) {
   461     array->AppendElement(handle);
   462   }
   464   return PL_DHASH_NEXT;
   465 }
   467 void
   468 CacheFileHandles::GetActiveHandles(
   469   nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
   470 {
   471   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   472   mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
   473 }
   475 void
   476 CacheFileHandles::ClearAll()
   477 {
   478   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   479   mTable.Clear();
   480 }
   482 uint32_t
   483 CacheFileHandles::HandleCount()
   484 {
   485   return mTable.Count();
   486 }
   488 #ifdef DEBUG_HANDLES
   489 void
   490 CacheFileHandles::Log(CacheFileHandlesEntry *entry)
   491 {
   492   LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
   494   nsTArray<nsRefPtr<CacheFileHandle> > array;
   495   aEntry->GetHandles(array);
   497   for (uint32_t i = 0; i < array.Length(); ++i) {
   498     CacheFileHandle *handle = array[i];
   499     handle->Log();
   500   }
   502   LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
   503 }
   504 #endif
   506 // Memory reporting
   508 namespace { // anon
   510 size_t
   511 CollectHandlesMemory(CacheFileHandles::HandleHashKey* key,
   512                      mozilla::MallocSizeOf mallocSizeOf,
   513                      void *arg)
   514 {
   515   return key->SizeOfExcludingThis(mallocSizeOf);
   516 }
   518 } // anon
   520 size_t
   521 CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   522 {
   523   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   525   return mTable.SizeOfExcludingThis(&CollectHandlesMemory, mallocSizeOf);
   526 }
   528 // Events
   530 class ShutdownEvent : public nsRunnable {
   531 public:
   532   ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
   533     : mLock(aLock)
   534     , mCondVar(aCondVar)
   535   {
   536     MOZ_COUNT_CTOR(ShutdownEvent);
   537   }
   539   ~ShutdownEvent()
   540   {
   541     MOZ_COUNT_DTOR(ShutdownEvent);
   542   }
   544   NS_IMETHOD Run()
   545   {
   546     MutexAutoLock lock(*mLock);
   548     CacheFileIOManager::gInstance->ShutdownInternal();
   550     mCondVar->Notify();
   551     return NS_OK;
   552   }
   554 protected:
   555   mozilla::Mutex   *mLock;
   556   mozilla::CondVar *mCondVar;
   557 };
   559 class OpenFileEvent : public nsRunnable {
   560 public:
   561   OpenFileEvent(const nsACString &aKey,
   562                 uint32_t aFlags, bool aResultOnAnyThread,
   563                 CacheFileIOListener *aCallback)
   564     : mFlags(aFlags)
   565     , mResultOnAnyThread(aResultOnAnyThread)
   566     , mCallback(aCallback)
   567     , mRV(NS_ERROR_FAILURE)
   568     , mKey(aKey)
   569   {
   570     MOZ_COUNT_CTOR(OpenFileEvent);
   572     if (!aResultOnAnyThread) {
   573       mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   574       MOZ_ASSERT(mTarget);
   575     }
   577     mIOMan = CacheFileIOManager::gInstance;
   579     MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aKey.BeginReading());
   580     MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-background");
   581   }
   583   ~OpenFileEvent()
   584   {
   585     MOZ_COUNT_DTOR(OpenFileEvent);
   586   }
   588   NS_IMETHOD Run()
   589   {
   590     if (mResultOnAnyThread || mTarget) {
   591       mRV = NS_OK;
   593       if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
   594         SHA1Sum sum;
   595         sum.update(mKey.BeginReading(), mKey.Length());
   596         sum.finish(mHash);
   597       }
   599       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this),
   600                             "net::cache::open-background");
   601       if (NS_SUCCEEDED(mRV)) {
   602         if (!mIOMan) {
   603           mRV = NS_ERROR_NOT_INITIALIZED;
   604         } else {
   605           if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
   606             mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
   607                                                   getter_AddRefs(mHandle));
   608           } else {
   609             mRV = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
   610                                            getter_AddRefs(mHandle));
   611           }
   612           mIOMan = nullptr;
   613           if (mHandle) {
   614             MOZ_EVENT_TRACER_NAME_OBJECT(mHandle.get(), mKey.get());
   615             if (mHandle->Key().IsEmpty()) {
   616               mHandle->Key() = mKey;
   617             }
   618           }
   619         }
   620       }
   621       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-background");
   623       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   625       if (mTarget) {
   626         nsCOMPtr<nsIEventTarget> target;
   627         mTarget.swap(target);
   628         return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   629       }
   630     }
   632     if (!mTarget) {
   633       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   634       mCallback->OnFileOpened(mHandle, mRV);
   635       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-result");
   636     }
   638     return NS_OK;
   639   }
   641 protected:
   642   SHA1Sum::Hash                 mHash;
   643   uint32_t                      mFlags;
   644   bool                          mResultOnAnyThread;
   645   nsCOMPtr<CacheFileIOListener> mCallback;
   646   nsCOMPtr<nsIEventTarget>      mTarget;
   647   nsRefPtr<CacheFileIOManager>  mIOMan;
   648   nsRefPtr<CacheFileHandle>     mHandle;
   649   nsresult                      mRV;
   650   nsCString                     mKey;
   651 };
   653 class ReadEvent : public nsRunnable {
   654 public:
   655   ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
   656             int32_t aCount, bool aResultOnAnyThread, CacheFileIOListener *aCallback)
   657     : mHandle(aHandle)
   658     , mOffset(aOffset)
   659     , mBuf(aBuf)
   660     , mCount(aCount)
   661     , mResultOnAnyThread(aResultOnAnyThread)
   662     , mCallback(aCallback)
   663     , mRV(NS_ERROR_FAILURE)
   664   {
   665     MOZ_COUNT_CTOR(ReadEvent);
   667     if (!aResultOnAnyThread) {
   668       mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   669     }
   671     MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   672     MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   673   }
   675   ~ReadEvent()
   676   {
   677     MOZ_COUNT_DTOR(ReadEvent);
   678   }
   680   NS_IMETHOD Run()
   681   {
   682     if (mResultOnAnyThread || mTarget) {
   683       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   684       if (mHandle->IsClosed()) {
   685         mRV = NS_ERROR_NOT_INITIALIZED;
   686       } else {
   687         mRV = CacheFileIOManager::gInstance->ReadInternal(
   688           mHandle, mOffset, mBuf, mCount);
   689       }
   690       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-background");
   692       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   694       if (mTarget) {
   695         nsCOMPtr<nsIEventTarget> target;
   696         mTarget.swap(target);
   697         return target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   698       }
   699     }
   701     if (!mTarget && mCallback) {
   702       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   703       mCallback->OnDataRead(mHandle, mBuf, mRV);
   704       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-result");
   705     }
   707     return NS_OK;
   708   }
   710 protected:
   711   nsRefPtr<CacheFileHandle>     mHandle;
   712   int64_t                       mOffset;
   713   char                         *mBuf;
   714   int32_t                       mCount;
   715   bool                          mResultOnAnyThread;
   716   nsCOMPtr<CacheFileIOListener> mCallback;
   717   nsCOMPtr<nsIEventTarget>      mTarget;
   718   nsresult                      mRV;
   719 };
   721 class WriteEvent : public nsRunnable {
   722 public:
   723   WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
   724              int32_t aCount, bool aValidate, CacheFileIOListener *aCallback)
   725     : mHandle(aHandle)
   726     , mOffset(aOffset)
   727     , mBuf(aBuf)
   728     , mCount(aCount)
   729     , mValidate(aValidate)
   730     , mCallback(aCallback)
   731     , mRV(NS_ERROR_FAILURE)
   732   {
   733     MOZ_COUNT_CTOR(WriteEvent);
   734     mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   736     MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   737     MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   738   }
   740   ~WriteEvent()
   741   {
   742     MOZ_COUNT_DTOR(WriteEvent);
   744     if (!mCallback && mBuf) {
   745       free(const_cast<char *>(mBuf));
   746     }
   747   }
   749   NS_IMETHOD Run()
   750   {
   751     if (mTarget) {
   752       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   753       if (mHandle->IsClosed()) {
   754         mRV = NS_ERROR_NOT_INITIALIZED;
   755       } else {
   756         mRV = CacheFileIOManager::gInstance->WriteInternal(
   757           mHandle, mOffset, mBuf, mCount, mValidate);
   758       }
   759       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
   761       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   762       nsCOMPtr<nsIEventTarget> target;
   763       mTarget.swap(target);
   764       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   765     } else {
   766       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   767       if (mCallback) {
   768         mCallback->OnDataWritten(mHandle, mBuf, mRV);
   769       } else {
   770         free(const_cast<char *>(mBuf));
   771         mBuf = nullptr;
   772       }
   773       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-result");
   774     }
   775     return NS_OK;
   776   }
   778 protected:
   779   nsRefPtr<CacheFileHandle>     mHandle;
   780   int64_t                       mOffset;
   781   const char                   *mBuf;
   782   int32_t                       mCount;
   783   bool                          mValidate;
   784   nsCOMPtr<CacheFileIOListener> mCallback;
   785   nsCOMPtr<nsIEventTarget>      mTarget;
   786   nsresult                      mRV;
   787 };
   789 class DoomFileEvent : public nsRunnable {
   790 public:
   791   DoomFileEvent(CacheFileHandle *aHandle,
   792                 CacheFileIOListener *aCallback)
   793     : mCallback(aCallback)
   794     , mHandle(aHandle)
   795     , mRV(NS_ERROR_FAILURE)
   796   {
   797     MOZ_COUNT_CTOR(DoomFileEvent);
   798     mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   800     MOZ_EVENT_TRACER_NAME_OBJECT(static_cast<nsIRunnable*>(this), aHandle->Key().get());
   801     MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   802   }
   804   ~DoomFileEvent()
   805   {
   806     MOZ_COUNT_DTOR(DoomFileEvent);
   807   }
   809   NS_IMETHOD Run()
   810   {
   811     if (mTarget) {
   812       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   813       if (mHandle->IsClosed()) {
   814         mRV = NS_ERROR_NOT_INITIALIZED;
   815       } else {
   816         mRV = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
   817       }
   818       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
   820       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   821       nsCOMPtr<nsIEventTarget> target;
   822       mTarget.swap(target);
   823       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   824     } else {
   825       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   826       if (mCallback) {
   827         mCallback->OnFileDoomed(mHandle, mRV);
   828       }
   829       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
   830     }
   831     return NS_OK;
   832   }
   834 protected:
   835   nsCOMPtr<CacheFileIOListener> mCallback;
   836   nsCOMPtr<nsIEventTarget>      mTarget;
   837   nsRefPtr<CacheFileHandle>     mHandle;
   838   nsresult                      mRV;
   839 };
   841 class DoomFileByKeyEvent : public nsRunnable {
   842 public:
   843   DoomFileByKeyEvent(const nsACString &aKey,
   844                      CacheFileIOListener *aCallback)
   845     : mCallback(aCallback)
   846     , mRV(NS_ERROR_FAILURE)
   847   {
   848     MOZ_COUNT_CTOR(DoomFileByKeyEvent);
   850     SHA1Sum sum;
   851     sum.update(aKey.BeginReading(), aKey.Length());
   852     sum.finish(mHash);
   854     mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   855     mIOMan = CacheFileIOManager::gInstance;
   856     MOZ_ASSERT(mTarget);
   857   }
   859   ~DoomFileByKeyEvent()
   860   {
   861     MOZ_COUNT_DTOR(DoomFileByKeyEvent);
   862   }
   864   NS_IMETHOD Run()
   865   {
   866     if (mTarget) {
   867       if (!mIOMan) {
   868         mRV = NS_ERROR_NOT_INITIALIZED;
   869       } else {
   870         mRV = mIOMan->DoomFileByKeyInternal(&mHash);
   871         mIOMan = nullptr;
   872       }
   874       nsCOMPtr<nsIEventTarget> target;
   875       mTarget.swap(target);
   876       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   877     } else {
   878       if (mCallback) {
   879         mCallback->OnFileDoomed(nullptr, mRV);
   880       }
   881     }
   882     return NS_OK;
   883   }
   885 protected:
   886   SHA1Sum::Hash                 mHash;
   887   nsCOMPtr<CacheFileIOListener> mCallback;
   888   nsCOMPtr<nsIEventTarget>      mTarget;
   889   nsRefPtr<CacheFileIOManager>  mIOMan;
   890   nsresult                      mRV;
   891 };
   893 class ReleaseNSPRHandleEvent : public nsRunnable {
   894 public:
   895   ReleaseNSPRHandleEvent(CacheFileHandle *aHandle)
   896     : mHandle(aHandle)
   897   {
   898     MOZ_COUNT_CTOR(ReleaseNSPRHandleEvent);
   899   }
   901   ~ReleaseNSPRHandleEvent()
   902   {
   903     MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent);
   904   }
   906   NS_IMETHOD Run()
   907   {
   908     if (mHandle->mFD && !mHandle->IsClosed()) {
   909       CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle);
   910     }
   912     return NS_OK;
   913   }
   915 protected:
   916   nsRefPtr<CacheFileHandle>     mHandle;
   917 };
   919 class TruncateSeekSetEOFEvent : public nsRunnable {
   920 public:
   921   TruncateSeekSetEOFEvent(CacheFileHandle *aHandle, int64_t aTruncatePos,
   922                           int64_t aEOFPos, CacheFileIOListener *aCallback)
   923     : mHandle(aHandle)
   924     , mTruncatePos(aTruncatePos)
   925     , mEOFPos(aEOFPos)
   926     , mCallback(aCallback)
   927     , mRV(NS_ERROR_FAILURE)
   928   {
   929     MOZ_COUNT_CTOR(TruncateSeekSetEOFEvent);
   930     mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   931   }
   933   ~TruncateSeekSetEOFEvent()
   934   {
   935     MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent);
   936   }
   938   NS_IMETHOD Run()
   939   {
   940     if (mTarget) {
   941       if (mHandle->IsClosed()) {
   942         mRV = NS_ERROR_NOT_INITIALIZED;
   943       } else {
   944         mRV = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
   945           mHandle, mTruncatePos, mEOFPos);
   946       }
   948       nsCOMPtr<nsIEventTarget> target;
   949       mTarget.swap(target);
   950       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   951     } else {
   952       if (mCallback) {
   953         mCallback->OnEOFSet(mHandle, mRV);
   954       }
   955     }
   956     return NS_OK;
   957   }
   959 protected:
   960   nsRefPtr<CacheFileHandle>     mHandle;
   961   int64_t                       mTruncatePos;
   962   int64_t                       mEOFPos;
   963   nsCOMPtr<CacheFileIOListener> mCallback;
   964   nsCOMPtr<nsIEventTarget>      mTarget;
   965   nsresult                      mRV;
   966 };
   968 class RenameFileEvent : public nsRunnable {
   969 public:
   970   RenameFileEvent(CacheFileHandle *aHandle, const nsACString &aNewName,
   971                   CacheFileIOListener *aCallback)
   972     : mHandle(aHandle)
   973     , mNewName(aNewName)
   974     , mCallback(aCallback)
   975     , mRV(NS_ERROR_FAILURE)
   976   {
   977     MOZ_COUNT_CTOR(RenameFileEvent);
   978     mTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   979   }
   981   ~RenameFileEvent()
   982   {
   983     MOZ_COUNT_DTOR(RenameFileEvent);
   984   }
   986   NS_IMETHOD Run()
   987   {
   988     if (mTarget) {
   989       if (mHandle->IsClosed()) {
   990         mRV = NS_ERROR_NOT_INITIALIZED;
   991       } else {
   992         mRV = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
   993                                                                 mNewName);
   994       }
   996       nsCOMPtr<nsIEventTarget> target;
   997       mTarget.swap(target);
   998       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
   999     } else {
  1000       if (mCallback) {
  1001         mCallback->OnFileRenamed(mHandle, mRV);
  1004     return NS_OK;
  1007 protected:
  1008   nsRefPtr<CacheFileHandle>     mHandle;
  1009   nsCString                     mNewName;
  1010   nsCOMPtr<CacheFileIOListener> mCallback;
  1011   nsCOMPtr<nsIEventTarget>      mTarget;
  1012   nsresult                      mRV;
  1013 };
  1015 class InitIndexEntryEvent : public nsRunnable {
  1016 public:
  1017   InitIndexEntryEvent(CacheFileHandle *aHandle, uint32_t aAppId,
  1018                       bool aAnonymous, bool aInBrowser)
  1019     : mHandle(aHandle)
  1020     , mAppId(aAppId)
  1021     , mAnonymous(aAnonymous)
  1022     , mInBrowser(aInBrowser)
  1024     MOZ_COUNT_CTOR(InitIndexEntryEvent);
  1027   ~InitIndexEntryEvent()
  1029     MOZ_COUNT_DTOR(InitIndexEntryEvent);
  1032   NS_IMETHOD Run()
  1034     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
  1035       return NS_OK;
  1038     CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
  1040     // We cannot set the filesize before we init the entry. If we're opening
  1041     // an existing entry file, frecency and expiration time will be set after
  1042     // parsing the entry file, but we must set the filesize here since nobody is
  1043     // going to set it if there is no write to the file.
  1044     uint32_t sizeInK = mHandle->FileSizeInK();
  1045     CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, &sizeInK);
  1047     return NS_OK;
  1050 protected:
  1051   nsRefPtr<CacheFileHandle> mHandle;
  1052   uint32_t                  mAppId;
  1053   bool                      mAnonymous;
  1054   bool                      mInBrowser;
  1055 };
  1057 class UpdateIndexEntryEvent : public nsRunnable {
  1058 public:
  1059   UpdateIndexEntryEvent(CacheFileHandle *aHandle, const uint32_t *aFrecency,
  1060                         const uint32_t *aExpirationTime)
  1061     : mHandle(aHandle)
  1062     , mHasFrecency(false)
  1063     , mHasExpirationTime(false)
  1065     MOZ_COUNT_CTOR(UpdateIndexEntryEvent);
  1066     if (aFrecency) {
  1067       mHasFrecency = true;
  1068       mFrecency = *aFrecency;
  1070     if (aExpirationTime) {
  1071       mHasExpirationTime = true;
  1072       mExpirationTime = *aExpirationTime;
  1076   ~UpdateIndexEntryEvent()
  1078     MOZ_COUNT_DTOR(UpdateIndexEntryEvent);
  1081   NS_IMETHOD Run()
  1083     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
  1084       return NS_OK;
  1087     CacheIndex::UpdateEntry(mHandle->Hash(),
  1088                             mHasFrecency ? &mFrecency : nullptr,
  1089                             mHasExpirationTime ? &mExpirationTime : nullptr,
  1090                             nullptr);
  1091     return NS_OK;
  1094 protected:
  1095   nsRefPtr<CacheFileHandle> mHandle;
  1096   bool                      mHasFrecency;
  1097   bool                      mHasExpirationTime;
  1098   uint32_t                  mFrecency;
  1099   uint32_t                  mExpirationTime;
  1100 };
  1102 class MetadataWriteScheduleEvent : public nsRunnable
  1104 public:
  1105   enum EMode {
  1106     SCHEDULE,
  1107     UNSCHEDULE,
  1108     SHUTDOWN
  1109   } mMode;
  1111   nsRefPtr<CacheFile> mFile;
  1112   nsRefPtr<CacheFileIOManager> mIOMan;
  1114   MetadataWriteScheduleEvent(CacheFileIOManager * aManager,
  1115                              CacheFile * aFile,
  1116                              EMode aMode)
  1117     : mMode(aMode)
  1118     , mFile(aFile)
  1119     , mIOMan(aManager)
  1120   { }
  1122   virtual ~MetadataWriteScheduleEvent() { }
  1124   NS_IMETHOD Run()
  1126     nsRefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
  1127     if (!ioMan) {
  1128       NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
  1129       return NS_OK;
  1132     switch (mMode)
  1134     case SCHEDULE:
  1135       ioMan->ScheduleMetadataWriteInternal(mFile);
  1136       break;
  1137     case UNSCHEDULE:
  1138       ioMan->UnscheduleMetadataWriteInternal(mFile);
  1139       break;
  1140     case SHUTDOWN:
  1141       ioMan->ShutdownMetadataWriteSchedulingInternal();
  1142       break;
  1144     return NS_OK;
  1146 };
  1148 CacheFileIOManager * CacheFileIOManager::gInstance = nullptr;
  1150 NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
  1152 CacheFileIOManager::CacheFileIOManager()
  1153   : mShuttingDown(false)
  1154   , mTreeCreated(false)
  1155   , mOverLimitEvicting(false)
  1156   , mRemovingTrashDirs(false)
  1158   LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
  1159   MOZ_COUNT_CTOR(CacheFileIOManager);
  1160   MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
  1163 CacheFileIOManager::~CacheFileIOManager()
  1165   LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
  1166   MOZ_COUNT_DTOR(CacheFileIOManager);
  1169 // static
  1170 nsresult
  1171 CacheFileIOManager::Init()
  1173   LOG(("CacheFileIOManager::Init()"));
  1175   MOZ_ASSERT(NS_IsMainThread());
  1177   if (gInstance) {
  1178     return NS_ERROR_ALREADY_INITIALIZED;
  1181   nsRefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
  1183   nsresult rv = ioMan->InitInternal();
  1184   NS_ENSURE_SUCCESS(rv, rv);
  1186   ioMan.swap(gInstance);
  1187   return NS_OK;
  1190 nsresult
  1191 CacheFileIOManager::InitInternal()
  1193   nsresult rv;
  1195   mIOThread = new CacheIOThread();
  1197   rv = mIOThread->Init();
  1198   MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
  1199   NS_ENSURE_SUCCESS(rv, rv);
  1201   mStartTime = TimeStamp::NowLoRes();
  1203   return NS_OK;
  1206 // static
  1207 nsresult
  1208 CacheFileIOManager::Shutdown()
  1210   LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
  1212   MOZ_ASSERT(NS_IsMainThread());
  1214   if (!gInstance) {
  1215     return NS_ERROR_NOT_INITIALIZED;
  1218   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
  1220   CacheIndex::PreShutdown();
  1222   ShutdownMetadataWriteScheduling();
  1225     mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
  1226     mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
  1228     MutexAutoLock autoLock(lock);
  1229     nsRefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
  1230     DebugOnly<nsresult> rv;
  1231     rv = gInstance->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
  1232     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1233     condVar.Wait();
  1236   MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
  1237   MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
  1239   if (gInstance->mIOThread) {
  1240     gInstance->mIOThread->Shutdown();
  1243   CacheIndex::Shutdown();
  1245   if (CacheObserver::ClearCacheOnShutdown()) {
  1246     gInstance->SyncRemoveAllCacheFiles();
  1249   nsRefPtr<CacheFileIOManager> ioMan;
  1250   ioMan.swap(gInstance);
  1252   return NS_OK;
  1255 nsresult
  1256 CacheFileIOManager::ShutdownInternal()
  1258   LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
  1260   MOZ_ASSERT(mIOThread->IsCurrentThread());
  1262   // No new handles can be created after this flag is set
  1263   mShuttingDown = true;
  1265   // close all handles and delete all associated files
  1266   nsTArray<nsRefPtr<CacheFileHandle> > handles;
  1267   mHandles.GetAllHandles(&handles);
  1268   handles.AppendElements(mSpecialHandles);
  1270   for (uint32_t i=0 ; i<handles.Length() ; i++) {
  1271     CacheFileHandle *h = handles[i];
  1272     h->mClosed = true;
  1274     h->Log();
  1276     // Close file handle
  1277     if (h->mFD) {
  1278       ReleaseNSPRHandleInternal(h);
  1281     // Remove file if entry is doomed or invalid
  1282     if (h->mFileExists && (h->mIsDoomed || h->mInvalid)) {
  1283       LOG(("CacheFileIOManager::ShutdownInternal() - Removing file from disk"));
  1284       h->mFile->Remove(false);
  1287     if (!h->IsSpecialFile() && !h->mIsDoomed &&
  1288         (h->mInvalid || !h->mFileExists)) {
  1289       CacheIndex::RemoveEntry(h->Hash());
  1292     // Remove the handle from mHandles/mSpecialHandles
  1293     if (h->IsSpecialFile()) {
  1294       mSpecialHandles.RemoveElement(h);
  1295     } else {
  1296       mHandles.RemoveHandle(h);
  1300   // Assert the table is empty. When we are here, no new handles can be added
  1301   // and handles will no longer remove them self from this table and we don't 
  1302   // want to keep invalid handles here. Also, there is no lookup after this 
  1303   // point to happen.
  1304   MOZ_ASSERT(mHandles.HandleCount() == 0);
  1306   // Release trash directory enumerator
  1307   if (mTrashDirEnumerator) {
  1308     mTrashDirEnumerator->Close();
  1309     mTrashDirEnumerator = nullptr;
  1312   return NS_OK;
  1315 // static
  1316 nsresult
  1317 CacheFileIOManager::OnProfile()
  1319   LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
  1321   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1322   if (!ioMan) {
  1323     // CacheFileIOManager::Init() failed, probably could not create the IO
  1324     // thread, just go with it...
  1325     return NS_ERROR_NOT_INITIALIZED;
  1328   nsresult rv;
  1330   nsCOMPtr<nsIFile> directory;
  1332   CacheObserver::ParentDirOverride(getter_AddRefs(directory));
  1334 #if defined(MOZ_WIDGET_ANDROID)
  1335   char* cachePath = getenv("CACHE_DIRECTORY");
  1336   if (!directory && cachePath && *cachePath) {
  1337     rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
  1338                                true, getter_AddRefs(directory));
  1340 #endif
  1342   if (!directory) {
  1343     rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
  1344                                 getter_AddRefs(directory));
  1347   if (!directory) {
  1348     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  1349                                 getter_AddRefs(directory));
  1352   if (directory) {
  1353     rv = directory->Append(NS_LITERAL_STRING("cache2"));
  1354     NS_ENSURE_SUCCESS(rv, rv);
  1357   // All functions return a clone.
  1358   ioMan->mCacheDirectory.swap(directory);
  1360   if (ioMan->mCacheDirectory) {
  1361     CacheIndex::Init(ioMan->mCacheDirectory);
  1364   return NS_OK;
  1367 // static
  1368 already_AddRefed<nsIEventTarget>
  1369 CacheFileIOManager::IOTarget()
  1371   nsCOMPtr<nsIEventTarget> target;
  1372   if (gInstance && gInstance->mIOThread) {
  1373     target = gInstance->mIOThread->Target();
  1376   return target.forget();
  1379 // static
  1380 already_AddRefed<CacheIOThread>
  1381 CacheFileIOManager::IOThread()
  1383   nsRefPtr<CacheIOThread> thread;
  1384   if (gInstance) {
  1385     thread = gInstance->mIOThread;
  1388   return thread.forget();
  1391 // static
  1392 bool
  1393 CacheFileIOManager::IsOnIOThread()
  1395   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1396   if (ioMan && ioMan->mIOThread) {
  1397     return ioMan->mIOThread->IsCurrentThread();
  1400   return false;
  1403 // static
  1404 bool
  1405 CacheFileIOManager::IsOnIOThreadOrCeased()
  1407   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1408   if (ioMan && ioMan->mIOThread) {
  1409     return ioMan->mIOThread->IsCurrentThread();
  1412   // Ceased...
  1413   return true;
  1416 // static
  1417 bool
  1418 CacheFileIOManager::IsShutdown()
  1420   if (!gInstance) {
  1421     return true;
  1423   return gInstance->mShuttingDown;
  1426 // static
  1427 nsresult
  1428 CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
  1430   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1431   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1433   NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
  1435   nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1436     ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
  1437   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1438   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1439   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1442 nsresult
  1443 CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
  1445   MOZ_ASSERT(IsOnIOThreadOrCeased());
  1447   nsresult rv;
  1449   if (!mMetadataWritesTimer) {
  1450     mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  1451     NS_ENSURE_SUCCESS(rv, rv);
  1453     rv = mMetadataWritesTimer->InitWithCallback(
  1454       this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
  1455     NS_ENSURE_SUCCESS(rv, rv);
  1458   if (mScheduledMetadataWrites.IndexOf(aFile) !=
  1459       mScheduledMetadataWrites.NoIndex) {
  1460     return NS_OK;
  1463   mScheduledMetadataWrites.AppendElement(aFile);
  1465   return NS_OK;
  1468 // static
  1469 nsresult
  1470 CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
  1472   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1473   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1475   NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
  1477   nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1478     ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
  1479   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1480   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1481   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1484 nsresult
  1485 CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
  1487   MOZ_ASSERT(IsOnIOThreadOrCeased());
  1489   mScheduledMetadataWrites.RemoveElement(aFile);
  1491   if (mScheduledMetadataWrites.Length() == 0 &&
  1492       mMetadataWritesTimer) {
  1493     mMetadataWritesTimer->Cancel();
  1494     mMetadataWritesTimer = nullptr;
  1497   return NS_OK;
  1500 // static
  1501 nsresult
  1502 CacheFileIOManager::ShutdownMetadataWriteScheduling()
  1504   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1505   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
  1507   nsRefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
  1508     ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
  1509   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
  1510   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
  1511   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  1514 nsresult
  1515 CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
  1517   MOZ_ASSERT(IsOnIOThreadOrCeased());
  1519   nsTArray<nsRefPtr<CacheFile> > files;
  1520   files.SwapElements(mScheduledMetadataWrites);
  1521   for (uint32_t i = 0; i < files.Length(); ++i) {
  1522     CacheFile * file = files[i];
  1523     file->WriteMetadataIfNeeded();
  1526   if (mMetadataWritesTimer) {
  1527     mMetadataWritesTimer->Cancel();
  1528     mMetadataWritesTimer = nullptr;
  1531   return NS_OK;
  1534 NS_IMETHODIMP
  1535 CacheFileIOManager::Notify(nsITimer * aTimer)
  1537   MOZ_ASSERT(IsOnIOThreadOrCeased());
  1538   MOZ_ASSERT(mMetadataWritesTimer == aTimer);
  1540   mMetadataWritesTimer = nullptr;
  1542   nsTArray<nsRefPtr<CacheFile> > files;
  1543   files.SwapElements(mScheduledMetadataWrites);
  1544   for (uint32_t i = 0; i < files.Length(); ++i) {
  1545     CacheFile * file = files[i];
  1546     file->WriteMetadataIfNeeded();
  1549   return NS_OK;
  1552 // static
  1553 nsresult
  1554 CacheFileIOManager::OpenFile(const nsACString &aKey,
  1555                              uint32_t aFlags, bool aResultOnAnyThread,
  1556                              CacheFileIOListener *aCallback)
  1558   LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
  1559        PromiseFlatCString(aKey).get(), aFlags, aCallback));
  1561   nsresult rv;
  1562   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1564   if (!ioMan) {
  1565     return NS_ERROR_NOT_INITIALIZED;
  1568   bool priority = aFlags & CacheFileIOManager::PRIORITY;
  1569   nsRefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aResultOnAnyThread, aCallback);
  1570   rv = ioMan->mIOThread->Dispatch(ev, priority
  1571     ? CacheIOThread::OPEN_PRIORITY
  1572     : CacheIOThread::OPEN);
  1573   NS_ENSURE_SUCCESS(rv, rv);
  1575   return NS_OK;
  1578 nsresult
  1579 CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
  1580                                      const nsACString &aKey,
  1581                                      uint32_t aFlags,
  1582                                      CacheFileHandle **_retval)
  1584   LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
  1585        "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
  1586        aFlags));
  1588   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
  1590   nsresult rv;
  1592   if (mShuttingDown) {
  1593     return NS_ERROR_NOT_INITIALIZED;
  1596   if (!mTreeCreated) {
  1597     rv = CreateCacheTree();
  1598     if (NS_FAILED(rv)) return rv;
  1601   nsCOMPtr<nsIFile> file;
  1602   rv = GetFile(aHash, getter_AddRefs(file));
  1603   NS_ENSURE_SUCCESS(rv, rv);
  1605   nsRefPtr<CacheFileHandle> handle;
  1606   mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
  1608   if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
  1609     if (handle) {
  1610       rv = DoomFileInternal(handle);
  1611       NS_ENSURE_SUCCESS(rv, rv);
  1612       handle = nullptr;
  1615     rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
  1616     NS_ENSURE_SUCCESS(rv, rv);
  1618     bool exists;
  1619     rv = file->Exists(&exists);
  1620     NS_ENSURE_SUCCESS(rv, rv);
  1622     if (exists) {
  1623       CacheIndex::RemoveEntry(aHash);
  1625       LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
  1626            "disk"));
  1627       rv = file->Remove(false);
  1628       if (NS_FAILED(rv)) {
  1629         NS_WARNING("Cannot remove old entry from the disk");
  1630         LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
  1631              ". [rv=0x%08x]", rv));
  1635     CacheIndex::AddEntry(aHash);
  1636     handle->mFile.swap(file);
  1637     handle->mFileSize = 0;
  1640   if (handle) {
  1641     handle.swap(*_retval);
  1642     return NS_OK;
  1645   bool exists;
  1646   rv = file->Exists(&exists);
  1647   NS_ENSURE_SUCCESS(rv, rv);
  1649   if (exists && mContextEvictor) {
  1650     if (mContextEvictor->ContextsCount() == 0) {
  1651       mContextEvictor = nullptr;
  1652     } else {
  1653       bool wasEvicted = false;
  1654       mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
  1655       if (wasEvicted) {
  1656         LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
  1657              "entry was evicted by EvictByContext()"));
  1658         exists = false;
  1659         file->Remove(false);
  1660         CacheIndex::RemoveEntry(aHash);
  1665   if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
  1666     return NS_ERROR_NOT_AVAILABLE;
  1669   rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
  1670   NS_ENSURE_SUCCESS(rv, rv);
  1672   if (exists) {
  1673     rv = file->GetFileSize(&handle->mFileSize);
  1674     NS_ENSURE_SUCCESS(rv, rv);
  1676     handle->mFileExists = true;
  1678     CacheIndex::EnsureEntryExists(aHash);
  1679   } else {
  1680     handle->mFileSize = 0;
  1682     CacheIndex::AddEntry(aHash);
  1685   handle->mFile.swap(file);
  1686   handle.swap(*_retval);
  1687   return NS_OK;
  1690 nsresult
  1691 CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
  1692                                             uint32_t aFlags,
  1693                                             CacheFileHandle **_retval)
  1695   LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
  1696        PromiseFlatCString(aKey).get(), aFlags));
  1698   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
  1700   nsresult rv;
  1702   if (mShuttingDown) {
  1703     return NS_ERROR_NOT_INITIALIZED;
  1706   if (!mTreeCreated) {
  1707     rv = CreateCacheTree();
  1708     if (NS_FAILED(rv)) return rv;
  1711   nsCOMPtr<nsIFile> file;
  1712   rv = GetSpecialFile(aKey, getter_AddRefs(file));
  1713   NS_ENSURE_SUCCESS(rv, rv);
  1715   nsRefPtr<CacheFileHandle> handle;
  1716   for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
  1717     if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
  1718       handle = mSpecialHandles[i];
  1719       break;
  1723   if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
  1724     if (handle) {
  1725       rv = DoomFileInternal(handle);
  1726       NS_ENSURE_SUCCESS(rv, rv);
  1727       handle = nullptr;
  1730     handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
  1731     mSpecialHandles.AppendElement(handle);
  1733     bool exists;
  1734     rv = file->Exists(&exists);
  1735     NS_ENSURE_SUCCESS(rv, rv);
  1737     if (exists) {
  1738       LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
  1739            "disk"));
  1740       rv = file->Remove(false);
  1741       if (NS_FAILED(rv)) {
  1742         NS_WARNING("Cannot remove old entry from the disk");
  1743         LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
  1744              "failed. [rv=0x%08x]", rv));
  1748     handle->mFile.swap(file);
  1749     handle->mFileSize = 0;
  1752   if (handle) {
  1753     handle.swap(*_retval);
  1754     return NS_OK;
  1757   bool exists;
  1758   rv = file->Exists(&exists);
  1759   NS_ENSURE_SUCCESS(rv, rv);
  1761   if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
  1762     return NS_ERROR_NOT_AVAILABLE;
  1765   handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
  1766   mSpecialHandles.AppendElement(handle);
  1768   if (exists) {
  1769     rv = file->GetFileSize(&handle->mFileSize);
  1770     NS_ENSURE_SUCCESS(rv, rv);
  1772     handle->mFileExists = true;
  1773   } else {
  1774     handle->mFileSize = 0;
  1777   handle->mFile.swap(file);
  1778   handle.swap(*_retval);
  1779   return NS_OK;
  1782 nsresult
  1783 CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
  1785   LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
  1786   aHandle->Log();
  1788   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  1790   // Close file handle
  1791   if (aHandle->mFD) {
  1792     ReleaseNSPRHandleInternal(aHandle);
  1795   // Delete the file if the entry was doomed or invalid
  1796   if (aHandle->mIsDoomed || aHandle->mInvalid) {
  1797     LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
  1798          "disk"));
  1799     aHandle->mFile->Remove(false);
  1802   if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
  1803       (aHandle->mInvalid || !aHandle->mFileExists)) {
  1804     CacheIndex::RemoveEntry(aHandle->Hash());
  1807   // Don't remove handles after shutdown
  1808   if (!mShuttingDown) {
  1809     if (aHandle->IsSpecialFile()) {
  1810       mSpecialHandles.RemoveElement(aHandle);
  1811     } else {
  1812       mHandles.RemoveHandle(aHandle);
  1816   return NS_OK;
  1819 // static
  1820 nsresult
  1821 CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
  1822                          char *aBuf, int32_t aCount, bool aResultOnAnyThread,
  1823                          CacheFileIOListener *aCallback)
  1825   LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
  1826        "listener=%p]", aHandle, aOffset, aCount, aCallback));
  1828   nsresult rv;
  1829   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1831   if (aHandle->IsClosed() || !ioMan) {
  1832     return NS_ERROR_NOT_INITIALIZED;
  1835   nsRefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
  1836                                          aResultOnAnyThread, aCallback);
  1837   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
  1838     ? CacheIOThread::READ_PRIORITY
  1839     : CacheIOThread::READ);
  1840   NS_ENSURE_SUCCESS(rv, rv);
  1842   return NS_OK;
  1845 nsresult
  1846 CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
  1847                                  char *aBuf, int32_t aCount)
  1849   LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%lld, count=%d]",
  1850        aHandle, aOffset, aCount));
  1852   nsresult rv;
  1854   if (!aHandle->mFileExists) {
  1855     NS_WARNING("Trying to read from non-existent file");
  1856     return NS_ERROR_NOT_AVAILABLE;
  1859   if (!aHandle->mFD) {
  1860     rv = OpenNSPRHandle(aHandle);
  1861     NS_ENSURE_SUCCESS(rv, rv);
  1862   } else {
  1863     NSPRHandleUsed(aHandle);
  1866   // Check again, OpenNSPRHandle could figure out the file was gone.
  1867   if (!aHandle->mFileExists) {
  1868     NS_WARNING("Trying to read from non-existent file");
  1869     return NS_ERROR_NOT_AVAILABLE;
  1872   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
  1873   if (offset == -1) {
  1874     return NS_ERROR_FAILURE;
  1877   int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
  1878   if (bytesRead != aCount) {
  1879     return NS_ERROR_FAILURE;
  1882   return NS_OK;
  1885 // static
  1886 nsresult
  1887 CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
  1888                           const char *aBuf, int32_t aCount, bool aValidate,
  1889                           CacheFileIOListener *aCallback)
  1891   LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
  1892        "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
  1893        aCallback));
  1895   nsresult rv;
  1896   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1898   if (aHandle->IsClosed() || !ioMan) {
  1899     return NS_ERROR_NOT_INITIALIZED;
  1902   nsRefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
  1903                                            aValidate, aCallback);
  1904   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  1905   NS_ENSURE_SUCCESS(rv, rv);
  1907   return NS_OK;
  1910 nsresult
  1911 CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
  1912                                   const char *aBuf, int32_t aCount,
  1913                                   bool aValidate)
  1915   LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%lld, count=%d, "
  1916        "validate=%d]", aHandle, aOffset, aCount, aValidate));
  1918   nsresult rv;
  1920   if (!aHandle->mFileExists) {
  1921     rv = CreateFile(aHandle);
  1922     NS_ENSURE_SUCCESS(rv, rv);
  1925   if (!aHandle->mFD) {
  1926     rv = OpenNSPRHandle(aHandle);
  1927     NS_ENSURE_SUCCESS(rv, rv);
  1928   } else {
  1929     NSPRHandleUsed(aHandle);
  1932   // Check again, OpenNSPRHandle could figure out the file was gone.
  1933   if (!aHandle->mFileExists) {
  1934     return NS_ERROR_NOT_AVAILABLE;
  1937   // Write invalidates the entry by default
  1938   aHandle->mInvalid = true;
  1940   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
  1941   if (offset == -1) {
  1942     return NS_ERROR_FAILURE;
  1945   int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
  1947   if (bytesWritten != -1 && aHandle->mFileSize < aOffset+bytesWritten) {
  1948     aHandle->mFileSize = aOffset+bytesWritten;
  1950     if (!aHandle->IsDoomed() && !aHandle->IsSpecialFile()) {
  1951       uint32_t size = aHandle->FileSizeInK();
  1952       CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &size);
  1953       EvictIfOverLimitInternal();
  1957   if (bytesWritten != aCount) {
  1958     return NS_ERROR_FAILURE;
  1961   // Write was successful and this write validates the entry (i.e. metadata)
  1962   if (aValidate) {
  1963     aHandle->mInvalid = false;
  1966   return NS_OK;
  1969 // static
  1970 nsresult
  1971 CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
  1972                              CacheFileIOListener *aCallback)
  1974   LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
  1975        aHandle, aCallback));
  1977   nsresult rv;
  1978   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  1980   if (aHandle->IsClosed() || !ioMan) {
  1981     return NS_ERROR_NOT_INITIALIZED;
  1984   nsRefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
  1985   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
  1986     ? CacheIOThread::OPEN_PRIORITY
  1987     : CacheIOThread::OPEN);
  1988   NS_ENSURE_SUCCESS(rv, rv);
  1990   return NS_OK;
  1993 nsresult
  1994 CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
  1996   LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
  1997   aHandle->Log();
  1999   nsresult rv;
  2001   if (aHandle->IsDoomed()) {
  2002     return NS_OK;
  2005   if (aHandle->mFileExists) {
  2006     // we need to move the current file to the doomed directory
  2007     if (aHandle->mFD) {
  2008       ReleaseNSPRHandleInternal(aHandle);
  2011     // find unused filename
  2012     nsCOMPtr<nsIFile> file;
  2013     rv = GetDoomedFile(getter_AddRefs(file));
  2014     NS_ENSURE_SUCCESS(rv, rv);
  2016     nsCOMPtr<nsIFile> parentDir;
  2017     rv = file->GetParent(getter_AddRefs(parentDir));
  2018     NS_ENSURE_SUCCESS(rv, rv);
  2020     nsAutoCString leafName;
  2021     rv = file->GetNativeLeafName(leafName);
  2022     NS_ENSURE_SUCCESS(rv, rv);
  2024     rv = aHandle->mFile->MoveToNative(parentDir, leafName);
  2025     if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
  2026       LOG(("  file already removed under our hands"));
  2027       aHandle->mFileExists = false;
  2028       rv = NS_OK;
  2029     } else {
  2030       NS_ENSURE_SUCCESS(rv, rv);
  2031       aHandle->mFile.swap(file);
  2035   if (!aHandle->IsSpecialFile()) {
  2036     CacheIndex::RemoveEntry(aHandle->Hash());
  2039   aHandle->mIsDoomed = true;
  2041   if (!aHandle->IsSpecialFile()) {
  2042     nsRefPtr<CacheStorageService> storageService = CacheStorageService::Self();
  2043     if (storageService) {
  2044       nsAutoCString idExtension, url;
  2045       nsCOMPtr<nsILoadContextInfo> info =
  2046         CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
  2047       MOZ_ASSERT(info);
  2048       if (info) {
  2049         storageService->CacheFileDoomed(info, idExtension, url);
  2054   return NS_OK;
  2057 // static
  2058 nsresult
  2059 CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
  2060                                   CacheFileIOListener *aCallback)
  2062   LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
  2063        PromiseFlatCString(aKey).get(), aCallback));
  2065   nsresult rv;
  2066   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2068   if (!ioMan) {
  2069     return NS_ERROR_NOT_INITIALIZED;
  2072   nsRefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
  2073   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  2074   NS_ENSURE_SUCCESS(rv, rv);
  2076   return NS_OK;
  2079 nsresult
  2080 CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
  2082   LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
  2083        , LOGSHA1(aHash)));
  2085   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  2087   nsresult rv;
  2089   if (mShuttingDown) {
  2090     return NS_ERROR_NOT_INITIALIZED;
  2093   if (!mCacheDirectory) {
  2094     return NS_ERROR_FILE_INVALID_PATH;
  2097   // Find active handle
  2098   nsRefPtr<CacheFileHandle> handle;
  2099   mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
  2101   if (handle) {
  2102     handle->Log();
  2104     if (handle->IsDoomed()) {
  2105       return NS_OK;
  2108     return DoomFileInternal(handle);
  2111   // There is no handle for this file, delete the file if exists
  2112   nsCOMPtr<nsIFile> file;
  2113   rv = GetFile(aHash, getter_AddRefs(file));
  2114   NS_ENSURE_SUCCESS(rv, rv);
  2116   bool exists;
  2117   rv = file->Exists(&exists);
  2118   NS_ENSURE_SUCCESS(rv, rv);
  2120   if (!exists) {
  2121     return NS_ERROR_NOT_AVAILABLE;
  2124   LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
  2125        "disk"));
  2126   rv = file->Remove(false);
  2127   if (NS_FAILED(rv)) {
  2128     NS_WARNING("Cannot remove old entry from the disk");
  2129     LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
  2130          "[rv=0x%08x]", rv));
  2133   CacheIndex::RemoveEntry(aHash);
  2135   return NS_OK;
  2138 // static
  2139 nsresult
  2140 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
  2142   LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
  2144   nsresult rv;
  2145   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2147   if (aHandle->IsClosed() || !ioMan) {
  2148     return NS_ERROR_NOT_INITIALIZED;
  2151   nsRefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
  2152   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
  2153   NS_ENSURE_SUCCESS(rv, rv);
  2155   return NS_OK;
  2158 nsresult
  2159 CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle)
  2161   LOG(("CacheFileIOManager::ReleaseNSPRHandleInternal() [handle=%p]", aHandle));
  2163   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  2164   MOZ_ASSERT(aHandle->mFD);
  2166   DebugOnly<bool> found;
  2167   found = mHandlesByLastUsed.RemoveElement(aHandle);
  2168   MOZ_ASSERT(found);
  2170   PR_Close(aHandle->mFD);
  2171   aHandle->mFD = nullptr;
  2173   return NS_OK;
  2176 // static
  2177 nsresult
  2178 CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
  2179                                        int64_t aTruncatePos, int64_t aEOFPos,
  2180                                        CacheFileIOListener *aCallback)
  2182   LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
  2183        "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
  2185   nsresult rv;
  2186   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2188   if (aHandle->IsClosed() || !ioMan) {
  2189     return NS_ERROR_NOT_INITIALIZED;
  2192   nsRefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
  2193                                            aHandle, aTruncatePos, aEOFPos,
  2194                                            aCallback);
  2195   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  2196   NS_ENSURE_SUCCESS(rv, rv);
  2198   return NS_OK;
  2201 // static
  2202 void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
  2204   *result = nullptr;
  2206   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2207   if (!ioMan) {
  2208     return;
  2211   nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
  2212   file.forget(result);
  2215 static nsresult
  2216 TruncFile(PRFileDesc *aFD, uint32_t aEOF)
  2218 #if defined(XP_UNIX)
  2219   if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
  2220     NS_ERROR("ftruncate failed");
  2221     return NS_ERROR_FAILURE;
  2223 #elif defined(XP_WIN)
  2224   int32_t cnt = PR_Seek(aFD, aEOF, PR_SEEK_SET);
  2225   if (cnt == -1) {
  2226     return NS_ERROR_FAILURE;
  2228   if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
  2229     NS_ERROR("SetEndOfFile failed");
  2230     return NS_ERROR_FAILURE;
  2232 #else
  2233   MOZ_ASSERT(false, "Not implemented!");
  2234 #endif
  2236   return NS_OK;
  2239 nsresult
  2240 CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
  2241                                                int64_t aTruncatePos,
  2242                                                int64_t aEOFPos)
  2244   LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
  2245        "truncatePos=%lld, EOFPos=%lld]", aHandle, aTruncatePos, aEOFPos));
  2247   nsresult rv;
  2249   if (!aHandle->mFileExists) {
  2250     rv = CreateFile(aHandle);
  2251     NS_ENSURE_SUCCESS(rv, rv);
  2254   if (!aHandle->mFD) {
  2255     rv = OpenNSPRHandle(aHandle);
  2256     NS_ENSURE_SUCCESS(rv, rv);
  2257   } else {
  2258     NSPRHandleUsed(aHandle);
  2261   // Check again, OpenNSPRHandle could figure out the file was gone.
  2262   if (!aHandle->mFileExists) {
  2263     return NS_ERROR_NOT_AVAILABLE;
  2266   // This operation always invalidates the entry
  2267   aHandle->mInvalid = true;
  2269   rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aTruncatePos));
  2270   NS_ENSURE_SUCCESS(rv, rv);
  2272   rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aEOFPos));
  2273   NS_ENSURE_SUCCESS(rv, rv);
  2275   return NS_OK;
  2278 // static
  2279 nsresult
  2280 CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
  2281                                const nsACString &aNewName,
  2282                                CacheFileIOListener *aCallback)
  2284   LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
  2285        aHandle, PromiseFlatCString(aNewName).get(), aCallback));
  2287   nsresult rv;
  2288   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2290   if (aHandle->IsClosed() || !ioMan) {
  2291     return NS_ERROR_NOT_INITIALIZED;
  2294   if (!aHandle->IsSpecialFile()) {
  2295     return NS_ERROR_UNEXPECTED;
  2298   nsRefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
  2299                                                      aCallback);
  2300   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  2301   NS_ENSURE_SUCCESS(rv, rv);
  2303   return NS_OK;
  2306 nsresult
  2307 CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
  2308                                        const nsACString &aNewName)
  2310   LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
  2311        aHandle, PromiseFlatCString(aNewName).get()));
  2313   nsresult rv;
  2315   MOZ_ASSERT(aHandle->IsSpecialFile());
  2317   if (aHandle->IsDoomed()) {
  2318     return NS_ERROR_NOT_AVAILABLE;
  2321   // Doom old handle if it exists and is not doomed
  2322   for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
  2323     if (!mSpecialHandles[i]->IsDoomed() &&
  2324         mSpecialHandles[i]->Key() == aNewName) {
  2325       MOZ_ASSERT(aHandle != mSpecialHandles[i]);
  2326       rv = DoomFileInternal(mSpecialHandles[i]);
  2327       NS_ENSURE_SUCCESS(rv, rv);
  2328       break;
  2332   nsCOMPtr<nsIFile> file;
  2333   rv = GetSpecialFile(aNewName, getter_AddRefs(file));
  2334   NS_ENSURE_SUCCESS(rv, rv);
  2336   bool exists;
  2337   rv = file->Exists(&exists);
  2338   NS_ENSURE_SUCCESS(rv, rv);
  2340   if (exists) {
  2341     LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
  2342          "disk"));
  2343     rv = file->Remove(false);
  2344     if (NS_FAILED(rv)) {
  2345       NS_WARNING("Cannot remove file from the disk");
  2346       LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
  2347            ". [rv=0x%08x]", rv));
  2351   if (!aHandle->FileExists()) {
  2352     aHandle->mKey = aNewName;
  2353     return NS_OK;
  2356   if (aHandle->mFD) {
  2357     ReleaseNSPRHandleInternal(aHandle);
  2360   rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
  2361   NS_ENSURE_SUCCESS(rv, rv);
  2363   aHandle->mKey = aNewName;
  2364   return NS_OK;
  2367 // static
  2368 nsresult
  2369 CacheFileIOManager::EvictIfOverLimit()
  2371   LOG(("CacheFileIOManager::EvictIfOverLimit()"));
  2373   nsresult rv;
  2374   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2376   if (!ioMan) {
  2377     return NS_ERROR_NOT_INITIALIZED;
  2380   nsCOMPtr<nsIRunnable> ev;
  2381   ev = NS_NewRunnableMethod(ioMan,
  2382                             &CacheFileIOManager::EvictIfOverLimitInternal);
  2384   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  2385   NS_ENSURE_SUCCESS(rv, rv);
  2387   return NS_OK;
  2390 nsresult
  2391 CacheFileIOManager::EvictIfOverLimitInternal()
  2393   LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
  2395   nsresult rv;
  2397   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2399   if (mShuttingDown) {
  2400     return NS_ERROR_NOT_INITIALIZED;
  2403   if (mOverLimitEvicting) {
  2404     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
  2405          "running."));
  2406     return NS_OK;
  2409   UpdateSmartCacheSize();
  2411   uint32_t cacheUsage;
  2412   rv = CacheIndex::GetCacheSize(&cacheUsage);
  2413   NS_ENSURE_SUCCESS(rv, rv);
  2415   uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
  2416   if (cacheUsage <= cacheLimit) {
  2417     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size under "
  2418          "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  2419     return NS_OK;
  2422   LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
  2423        "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
  2424        cacheUsage, cacheLimit));
  2426   nsCOMPtr<nsIRunnable> ev;
  2427   ev = NS_NewRunnableMethod(this,
  2428                             &CacheFileIOManager::OverLimitEvictionInternal);
  2430   rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  2431   NS_ENSURE_SUCCESS(rv, rv);
  2433   mOverLimitEvicting = true;
  2434   return NS_OK;
  2437 nsresult
  2438 CacheFileIOManager::OverLimitEvictionInternal()
  2440   LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
  2442   nsresult rv;
  2444   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2446   // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
  2447   // here and set it to true again once we dispatch another event that will
  2448   // continue with the eviction. The reason why we do so is that we can fail
  2449   // early anywhere in this method and the variable will contain a correct
  2450   // value. Otherwise we would need to set it to false on every failing place.
  2451   mOverLimitEvicting = false;
  2453   if (mShuttingDown) {
  2454     return NS_ERROR_NOT_INITIALIZED;
  2457   UpdateSmartCacheSize();
  2459   while (true) {
  2460     uint32_t cacheUsage;
  2461     rv = CacheIndex::GetCacheSize(&cacheUsage);
  2462     NS_ENSURE_SUCCESS(rv, rv);
  2464     uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
  2465     if (cacheUsage <= cacheLimit) {
  2466       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size under "
  2467            "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  2468       return NS_OK;
  2471     LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
  2472          "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
  2474     if (CacheIOThread::YieldAndRerun()) {
  2475       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
  2476            "for higher level events."));
  2477       mOverLimitEvicting = true;
  2478       return NS_OK;
  2481     SHA1Sum::Hash hash;
  2482     uint32_t cnt;
  2483     static uint32_t consecutiveFailures = 0;
  2484     rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
  2485     NS_ENSURE_SUCCESS(rv, rv);
  2487     rv = DoomFileByKeyInternal(&hash);
  2488     if (NS_SUCCEEDED(rv)) {
  2489       consecutiveFailures = 0;
  2490     } else if (rv == NS_ERROR_NOT_AVAILABLE) {
  2491       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
  2492            "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
  2493       // TODO index is outdated, start update
  2495       // Make sure index won't return the same entry again
  2496       CacheIndex::RemoveEntry(&hash);
  2497       consecutiveFailures = 0;
  2498     } else {
  2499       // This shouldn't normally happen, but the eviction must not fail
  2500       // completely if we ever encounter this problem.
  2501       NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
  2502                  "failure of DoomFileByKeyInternal()");
  2504       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
  2505            "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
  2507       // Normally, CacheIndex::UpdateEntry() is called only to update newly
  2508       // created/opened entries which are always fresh and UpdateEntry() expects
  2509       // and checks this flag. The way we use UpdateEntry() here is a kind of
  2510       // hack and we must make sure the flag is set by calling
  2511       // EnsureEntryExists().
  2512       rv = CacheIndex::EnsureEntryExists(&hash);
  2513       NS_ENSURE_SUCCESS(rv, rv);
  2515       // Move the entry at the end of both lists to make sure we won't end up
  2516       // failing on one entry forever.
  2517       uint32_t frecency = 0;
  2518       uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
  2519       rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr);
  2520       NS_ENSURE_SUCCESS(rv, rv);
  2522       consecutiveFailures++;
  2523       if (consecutiveFailures >= cnt) {
  2524         // This doesn't necessarily mean that we've tried to doom every entry
  2525         // but we've reached a sane number of tries. It is likely that another
  2526         // eviction will start soon. And as said earlier, this normally doesn't
  2527         // happen at all.
  2528         return NS_OK;
  2533   NS_NOTREACHED("We should never get here");
  2534   return NS_OK;
  2537 // static
  2538 nsresult
  2539 CacheFileIOManager::EvictAll()
  2541   LOG(("CacheFileIOManager::EvictAll()"));
  2543   nsresult rv;
  2544   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2546   if (!ioMan) {
  2547     return NS_ERROR_NOT_INITIALIZED;
  2550   nsCOMPtr<nsIRunnable> ev;
  2551   ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
  2553   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  2554   if (NS_WARN_IF(NS_FAILED(rv))) {
  2555     return rv;
  2558   return NS_OK;
  2561 namespace {
  2563 class EvictionNotifierRunnable : public nsRunnable
  2565 public:
  2566   NS_DECL_NSIRUNNABLE
  2567 };
  2569 NS_IMETHODIMP
  2570 EvictionNotifierRunnable::Run()
  2572   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
  2573   if (obsSvc) {
  2574     obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
  2576   return NS_OK;
  2579 } // anonymous namespace
  2581 nsresult
  2582 CacheFileIOManager::EvictAllInternal()
  2584   LOG(("CacheFileIOManager::EvictAllInternal()"));
  2586   nsresult rv;
  2588   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2590   nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
  2592   if (!mCacheDirectory) {
  2593     // This is a kind of hack. Somebody called EvictAll() without a profile.
  2594     // This happens in xpcshell tests that use cache without profile. We need
  2595     // to notify observers in this case since the tests are waiting for it.
  2596     NS_DispatchToMainThread(r);
  2597     return NS_ERROR_FILE_INVALID_PATH;
  2600   if (mShuttingDown) {
  2601     return NS_ERROR_NOT_INITIALIZED;
  2604   if (!mTreeCreated) {
  2605     rv = CreateCacheTree();
  2606     if (NS_FAILED(rv)) {
  2607       return rv;
  2611   // Doom all active handles
  2612   nsTArray<nsRefPtr<CacheFileHandle> > handles;
  2613   mHandles.GetActiveHandles(&handles);
  2615   for (uint32_t i = 0; i < handles.Length(); ++i) {
  2616     rv = DoomFileInternal(handles[i]);
  2617     if (NS_WARN_IF(NS_FAILED(rv))) {
  2618       LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
  2619            "[handle=%p]", handles[i].get()));
  2623   nsCOMPtr<nsIFile> file;
  2624   rv = mCacheDirectory->Clone(getter_AddRefs(file));
  2625   if (NS_WARN_IF(NS_FAILED(rv))) {
  2626     return rv;
  2629   rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
  2630   if (NS_WARN_IF(NS_FAILED(rv))) {
  2631     return rv;
  2634   // Trash current entries directory
  2635   rv = TrashDirectory(file);
  2636   if (NS_WARN_IF(NS_FAILED(rv))) {
  2637     return rv;
  2640   // Files are now inaccessible in entries directory, notify observers.
  2641   NS_DispatchToMainThread(r);
  2643   // Create a new empty entries directory
  2644   rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
  2645   if (NS_WARN_IF(NS_FAILED(rv))) {
  2646     return rv;
  2649   CacheIndex::RemoveAll();
  2651   return NS_OK;
  2654 // static
  2655 nsresult
  2656 CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
  2658   LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
  2659        aLoadContextInfo));
  2661   nsresult rv;
  2662   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2664   if (!ioMan) {
  2665     return NS_ERROR_NOT_INITIALIZED;
  2668   nsCOMPtr<nsIRunnable> ev;
  2669   ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
  2670          (ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
  2672   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
  2673   if (NS_WARN_IF(NS_FAILED(rv))) {
  2674     return rv;
  2677   return NS_OK;
  2680 nsresult
  2681 CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
  2683   LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
  2684        "anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo,
  2685        aLoadContextInfo->IsAnonymous(), aLoadContextInfo->IsInBrowserElement(),
  2686        aLoadContextInfo->AppId()));
  2688   nsresult rv;
  2690   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2692   MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
  2693   if (aLoadContextInfo->IsPrivate()) {
  2694     return NS_ERROR_INVALID_ARG;
  2697   if (!mCacheDirectory) {
  2698     return NS_ERROR_FILE_INVALID_PATH;
  2701   if (mShuttingDown) {
  2702     return NS_ERROR_NOT_INITIALIZED;
  2705   if (!mTreeCreated) {
  2706     rv = CreateCacheTree();
  2707     if (NS_FAILED(rv)) {
  2708       return rv;
  2712   // Doom all active handles that matches the load context
  2713   nsTArray<nsRefPtr<CacheFileHandle> > handles;
  2714   mHandles.GetActiveHandles(&handles);
  2716   for (uint32_t i = 0; i < handles.Length(); ++i) {
  2717     bool equals;
  2718     rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
  2719                                                    aLoadContextInfo,
  2720                                                    &equals);
  2721     if (NS_FAILED(rv)) {
  2722       LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
  2723            "handle! [handle=%p, key=%s]", handles[i].get(),
  2724            handles[i]->Key().get()));
  2725       MOZ_CRASH("Unexpected error!");
  2728     if (equals) {
  2729       rv = DoomFileInternal(handles[i]);
  2730       if (NS_WARN_IF(NS_FAILED(rv))) {
  2731         LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
  2732              " [handle=%p]", handles[i].get()));
  2737   if (!mContextEvictor) {
  2738     mContextEvictor = new CacheFileContextEvictor();
  2739     mContextEvictor->Init(mCacheDirectory);
  2742   mContextEvictor->AddContext(aLoadContextInfo);
  2744   return NS_OK;
  2747 // static
  2748 nsresult
  2749 CacheFileIOManager::CacheIndexStateChanged()
  2751   LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
  2753   nsresult rv;
  2755   // CacheFileIOManager lives longer than CacheIndex so gInstance must be
  2756   // non-null here.
  2757   MOZ_ASSERT(gInstance);
  2759   // We have to re-distatch even if we are on IO thread to prevent reentering
  2760   // the lock in CacheIndex
  2761   nsCOMPtr<nsIRunnable> ev;
  2762   ev = NS_NewRunnableMethod(
  2763     gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal);
  2765   nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
  2766   MOZ_ASSERT(ioTarget);
  2768   rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
  2769   if (NS_WARN_IF(NS_FAILED(rv))) {
  2770     return rv;
  2773   return NS_OK;
  2776 nsresult
  2777 CacheFileIOManager::CacheIndexStateChangedInternal()
  2779   if (mShuttingDown) {
  2780     // ignore notification during shutdown
  2781     return NS_OK;
  2784   if (!mContextEvictor) {
  2785     return NS_OK;
  2788   mContextEvictor->CacheIndexStateChanged();
  2789   return NS_OK;
  2792 nsresult
  2793 CacheFileIOManager::TrashDirectory(nsIFile *aFile)
  2795 #ifdef PR_LOGGING
  2796   nsAutoCString path;
  2797   aFile->GetNativePath(path);
  2798 #endif
  2799   LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
  2801   nsresult rv;
  2803   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2804   MOZ_ASSERT(mCacheDirectory);
  2806   // When the directory is empty, it is cheaper to remove it directly instead of
  2807   // using the trash mechanism.
  2808   bool isEmpty;
  2809   rv = IsEmptyDirectory(aFile, &isEmpty);
  2810   NS_ENSURE_SUCCESS(rv, rv);
  2812   if (isEmpty) {
  2813     rv = aFile->Remove(false);
  2814     LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
  2815          rv));
  2816     return rv;
  2819 #ifdef DEBUG
  2820   nsCOMPtr<nsIFile> dirCheck;
  2821   rv = aFile->GetParent(getter_AddRefs(dirCheck));
  2822   NS_ENSURE_SUCCESS(rv, rv);
  2824   bool equals = false;
  2825   rv = dirCheck->Equals(mCacheDirectory, &equals);
  2826   NS_ENSURE_SUCCESS(rv, rv);
  2828   MOZ_ASSERT(equals);
  2829 #endif
  2831   nsCOMPtr<nsIFile> dir, trash;
  2832   nsAutoCString leaf;
  2834   rv = aFile->Clone(getter_AddRefs(dir));
  2835   NS_ENSURE_SUCCESS(rv, rv);
  2837   rv = aFile->Clone(getter_AddRefs(trash));
  2838   NS_ENSURE_SUCCESS(rv, rv);
  2840   srand(static_cast<unsigned>(PR_Now()));
  2841   while (true) {
  2842     leaf = kTrashDir;
  2843     leaf.AppendInt(rand());
  2844     rv = trash->SetNativeLeafName(leaf);
  2845     NS_ENSURE_SUCCESS(rv, rv);
  2847     bool exists;
  2848     if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
  2849       break;
  2853   LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
  2854        leaf.get()));
  2856   rv = dir->MoveToNative(nullptr, leaf);
  2857   NS_ENSURE_SUCCESS(rv, rv);
  2859   StartRemovingTrash();
  2860   return NS_OK;
  2863 // static
  2864 void
  2865 CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
  2867   LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
  2868        aClosure));
  2870   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  2872   if (!ioMan) {
  2873     return;
  2876   ioMan->mTrashTimer = nullptr;
  2877   ioMan->StartRemovingTrash();
  2880 nsresult
  2881 CacheFileIOManager::StartRemovingTrash()
  2883   LOG(("CacheFileIOManager::StartRemovingTrash()"));
  2885   nsresult rv;
  2887   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2889   if (mShuttingDown) {
  2890     return NS_ERROR_NOT_INITIALIZED;
  2893   if (!mCacheDirectory) {
  2894     return NS_ERROR_FILE_INVALID_PATH;
  2897   if (mTrashTimer) {
  2898     LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
  2899     return NS_OK;
  2902   if (mRemovingTrashDirs) {
  2903     LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
  2904          "progress."));
  2905     return NS_OK;
  2908   uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
  2909   if (elapsed < kRemoveTrashStartDelay) {
  2910     nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  2911     NS_ENSURE_SUCCESS(rv, rv);
  2913     nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
  2914     MOZ_ASSERT(ioTarget);
  2916     rv = timer->SetTarget(ioTarget);
  2917     NS_ENSURE_SUCCESS(rv, rv);
  2919     rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr,
  2920                                      kRemoveTrashStartDelay - elapsed,
  2921                                      nsITimer::TYPE_ONE_SHOT);
  2922     NS_ENSURE_SUCCESS(rv, rv);
  2924     mTrashTimer.swap(timer);
  2925     return NS_OK;
  2928   nsCOMPtr<nsIRunnable> ev;
  2929   ev = NS_NewRunnableMethod(this,
  2930                             &CacheFileIOManager::RemoveTrashInternal);
  2932   rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
  2933   NS_ENSURE_SUCCESS(rv, rv);
  2935   mRemovingTrashDirs = true;
  2936   return NS_OK;
  2939 nsresult
  2940 CacheFileIOManager::RemoveTrashInternal()
  2942   LOG(("CacheFileIOManager::RemoveTrashInternal()"));
  2944   nsresult rv;
  2946   MOZ_ASSERT(mIOThread->IsCurrentThread());
  2948   if (mShuttingDown) {
  2949     return NS_ERROR_NOT_INITIALIZED;
  2952   MOZ_ASSERT(!mTrashTimer);
  2953   MOZ_ASSERT(mRemovingTrashDirs);
  2955   if (!mTreeCreated) {
  2956     rv = CreateCacheTree();
  2957     if (NS_FAILED(rv)) {
  2958       return rv;
  2962   // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
  2963   // here and set it again once we dispatch a continuation event. By doing so,
  2964   // we don't have to drop the flag on any possible early return.
  2965   mRemovingTrashDirs = false;
  2967   while (true) {
  2968     if (CacheIOThread::YieldAndRerun()) {
  2969       LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
  2970            "higher level events."));
  2971       mRemovingTrashDirs = true;
  2972       return NS_OK;
  2975     // Find some trash directory
  2976     if (!mTrashDir) {
  2977       MOZ_ASSERT(!mTrashDirEnumerator);
  2979       rv = FindTrashDirToRemove();
  2980       if (rv == NS_ERROR_NOT_AVAILABLE) {
  2981         LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
  2982              "found."));
  2983         return NS_OK;
  2985       NS_ENSURE_SUCCESS(rv, rv);
  2987       nsCOMPtr<nsISimpleEnumerator> enumerator;
  2988       rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
  2989       if (NS_SUCCEEDED(rv)) {
  2990         mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
  2991         NS_ENSURE_SUCCESS(rv, rv);
  2994       continue; // check elapsed time
  2997     // We null out mTrashDirEnumerator once we remove all files in the
  2998     // directory, so remove the trash directory if we don't have enumerator.
  2999     if (!mTrashDirEnumerator) {
  3000       rv = mTrashDir->Remove(false);
  3001       if (NS_FAILED(rv)) {
  3002         // There is no reason why removing an empty directory should fail, but
  3003         // if it does, we should continue and try to remove all other trash
  3004         // directories.
  3005         nsAutoCString leafName;
  3006         mTrashDir->GetNativeLeafName(leafName);
  3007         mFailedTrashDirs.AppendElement(leafName);
  3008         LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
  3009              "trashdir. [name=%s]", leafName.get()));
  3012       mTrashDir = nullptr;
  3013       continue; // check elapsed time
  3016     nsCOMPtr<nsIFile> file;
  3017     rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
  3018     if (!file) {
  3019       mTrashDirEnumerator->Close();
  3020       mTrashDirEnumerator = nullptr;
  3021       continue; // check elapsed time
  3022     } else {
  3023       bool isDir = false;
  3024       file->IsDirectory(&isDir);
  3025       if (isDir) {
  3026         NS_WARNING("Found a directory in a trash directory! It will be removed "
  3027                    "recursively, but this can block IO thread for a while!");
  3028 #ifdef PR_LOGGING
  3029         nsAutoCString path;
  3030         file->GetNativePath(path);
  3031 #endif
  3032         LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
  3033             "directory! It will be removed recursively, but this can block IO "
  3034             "thread for a while! [file=%s]", path.get()));
  3036       file->Remove(isDir);
  3040   NS_NOTREACHED("We should never get here");
  3041   return NS_OK;
  3044 nsresult
  3045 CacheFileIOManager::FindTrashDirToRemove()
  3047   LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
  3049   nsresult rv;
  3051   // We call this method on the main thread during shutdown when user wants to
  3052   // remove all cache files.
  3053   MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
  3055   nsCOMPtr<nsISimpleEnumerator> iter;
  3056   rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
  3057   NS_ENSURE_SUCCESS(rv, rv);
  3059   bool more;
  3060   nsCOMPtr<nsISupports> elem;
  3062   while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
  3063     rv = iter->GetNext(getter_AddRefs(elem));
  3064     if (NS_FAILED(rv)) {
  3065       continue;
  3068     nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
  3069     if (!file) {
  3070       continue;
  3073     bool isDir = false;
  3074     file->IsDirectory(&isDir);
  3075     if (!isDir) {
  3076       continue;
  3079     nsAutoCString leafName;
  3080     rv = file->GetNativeLeafName(leafName);
  3081     if (NS_FAILED(rv)) {
  3082       continue;
  3085     if (leafName.Length() < strlen(kTrashDir)) {
  3086       continue;
  3089     if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(kTrashDir))) {
  3090       continue;
  3093     if (mFailedTrashDirs.Contains(leafName)) {
  3094       continue;
  3097     LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
  3098          leafName.get()));
  3100     mTrashDir = file;
  3101     return NS_OK;
  3104   // When we're here we've tried to delete all trash directories. Clear
  3105   // mFailedTrashDirs so we will try to delete them again when we start removing
  3106   // trash directories next time.
  3107   mFailedTrashDirs.Clear();
  3108   return NS_ERROR_NOT_AVAILABLE;
  3111 // static
  3112 nsresult
  3113 CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
  3114                                    uint32_t         aAppId,
  3115                                    bool             aAnonymous,
  3116                                    bool             aInBrowser)
  3118   LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
  3119        ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
  3121   nsresult rv;
  3122   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  3124   if (aHandle->IsClosed() || !ioMan) {
  3125     return NS_ERROR_NOT_INITIALIZED;
  3128   if (aHandle->IsSpecialFile()) {
  3129     return NS_ERROR_UNEXPECTED;
  3132   nsRefPtr<InitIndexEntryEvent> ev =
  3133     new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
  3134   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  3135   NS_ENSURE_SUCCESS(rv, rv);
  3137   return NS_OK;
  3140 // static
  3141 nsresult
  3142 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
  3143                                      const uint32_t  *aFrecency,
  3144                                      const uint32_t  *aExpirationTime)
  3146   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
  3147        "expirationTime=%s]", aHandle,
  3148        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
  3149        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
  3151   nsresult rv;
  3152   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
  3154   if (aHandle->IsClosed() || !ioMan) {
  3155     return NS_ERROR_NOT_INITIALIZED;
  3158   if (aHandle->IsSpecialFile()) {
  3159     return NS_ERROR_UNEXPECTED;
  3162   nsRefPtr<UpdateIndexEntryEvent> ev =
  3163     new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
  3164   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
  3165   NS_ENSURE_SUCCESS(rv, rv);
  3167   return NS_OK;
  3170 nsresult
  3171 CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
  3173   MOZ_ASSERT(!aHandle->mFD);
  3174   MOZ_ASSERT(aHandle->mFile);
  3176   nsresult rv;
  3178   if (aHandle->IsDoomed()) {
  3179     nsCOMPtr<nsIFile> file;
  3181     rv = GetDoomedFile(getter_AddRefs(file));
  3182     NS_ENSURE_SUCCESS(rv, rv);
  3184     aHandle->mFile.swap(file);
  3185   } else {
  3186     bool exists;
  3187     if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
  3188       NS_WARNING("Found a file that should not exist!");
  3192   rv = OpenNSPRHandle(aHandle, true);
  3193   NS_ENSURE_SUCCESS(rv, rv);
  3195   aHandle->mFileSize = 0;
  3196   return NS_OK;
  3199 // static
  3200 void
  3201 CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
  3203   _retval.Assign("");
  3204   const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  3205                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  3206   for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
  3207     _retval.Append(hexChars[(*aHash)[i] >> 4]);
  3208     _retval.Append(hexChars[(*aHash)[i] & 0xF]);
  3212 // static
  3213 nsresult
  3214 CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
  3216   if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
  3217     return NS_ERROR_INVALID_ARG;
  3220   for (uint32_t i=0 ; i<aHash.Length() ; i++) {
  3221     uint8_t value;
  3223     if (aHash[i] >= '0' && aHash[i] <= '9') {
  3224       value = aHash[i] - '0';
  3225     } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
  3226       value = aHash[i] - 'A' + 10;
  3227     } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
  3228       value = aHash[i] - 'a' + 10;
  3229     } else {
  3230       return NS_ERROR_INVALID_ARG;
  3233     if (i%2 == 0) {
  3234       (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
  3235     } else {
  3236       (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
  3240   return NS_OK;
  3243 nsresult
  3244 CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
  3246   nsresult rv;
  3247   nsCOMPtr<nsIFile> file;
  3248   rv = mCacheDirectory->Clone(getter_AddRefs(file));
  3249   NS_ENSURE_SUCCESS(rv, rv);
  3251   rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
  3252   NS_ENSURE_SUCCESS(rv, rv);
  3254   nsAutoCString leafName;
  3255   HashToStr(aHash, leafName);
  3257   rv = file->AppendNative(leafName);
  3258   NS_ENSURE_SUCCESS(rv, rv);
  3260   file.swap(*_retval);
  3261   return NS_OK;
  3264 nsresult
  3265 CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
  3267   nsresult rv;
  3268   nsCOMPtr<nsIFile> file;
  3269   rv = mCacheDirectory->Clone(getter_AddRefs(file));
  3270   NS_ENSURE_SUCCESS(rv, rv);
  3272   rv = file->AppendNative(aKey);
  3273   NS_ENSURE_SUCCESS(rv, rv);
  3275   file.swap(*_retval);
  3276   return NS_OK;
  3279 nsresult
  3280 CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
  3282   nsresult rv;
  3283   nsCOMPtr<nsIFile> file;
  3284   rv = mCacheDirectory->Clone(getter_AddRefs(file));
  3285   NS_ENSURE_SUCCESS(rv, rv);
  3287   rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
  3288   NS_ENSURE_SUCCESS(rv, rv);
  3290   rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
  3291   NS_ENSURE_SUCCESS(rv, rv);
  3293   srand(static_cast<unsigned>(PR_Now()));
  3294   nsAutoCString leafName;
  3295   uint32_t iter=0;
  3296   while (true) {
  3297     iter++;
  3298     leafName.AppendInt(rand());
  3299     rv = file->SetNativeLeafName(leafName);
  3300     NS_ENSURE_SUCCESS(rv, rv);
  3302     bool exists;
  3303     if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
  3304       break;
  3307     leafName.Truncate();
  3310 //  Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
  3312   file.swap(*_retval);
  3313   return NS_OK;
  3316 nsresult
  3317 CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
  3319   MOZ_ASSERT(mIOThread->IsCurrentThread());
  3321   nsresult rv;
  3323   nsCOMPtr<nsISimpleEnumerator> enumerator;
  3324   rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
  3325   NS_ENSURE_SUCCESS(rv, rv);
  3327   bool hasMoreElements = false;
  3328   rv = enumerator->HasMoreElements(&hasMoreElements);
  3329   NS_ENSURE_SUCCESS(rv, rv);
  3331   *_retval = !hasMoreElements;
  3332   return NS_OK;
  3335 nsresult
  3336 CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
  3337                                       bool aEnsureEmptyDir)
  3339   nsresult rv;
  3341   nsCOMPtr<nsIFile> file;
  3342   if (!aDir) {
  3343     file = aFile;
  3344   } else {
  3345     nsAutoCString dir(aDir);
  3346     rv = aFile->Clone(getter_AddRefs(file));
  3347     NS_ENSURE_SUCCESS(rv, rv);
  3348     rv = file->AppendNative(dir);
  3349     NS_ENSURE_SUCCESS(rv, rv);
  3352   bool exists = false;
  3353   rv = file->Exists(&exists);
  3354   if (NS_SUCCEEDED(rv) && exists) {
  3355     bool isDirectory = false;
  3356     rv = file->IsDirectory(&isDirectory);
  3357     if (NS_FAILED(rv) || !isDirectory) {
  3358       // Try to remove the file
  3359       rv = file->Remove(false);
  3360       if (NS_SUCCEEDED(rv)) {
  3361         exists = false;
  3364     NS_ENSURE_SUCCESS(rv, rv);
  3367   if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
  3368     bool isEmpty;
  3369     rv = IsEmptyDirectory(file, &isEmpty);
  3370     NS_ENSURE_SUCCESS(rv, rv);
  3372     if (!isEmpty) {
  3373       rv = TrashDirectory(file);
  3374       NS_ENSURE_SUCCESS(rv, rv);
  3376       exists = false;
  3380   if (NS_SUCCEEDED(rv) && !exists) {
  3381     rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
  3383   if (NS_FAILED(rv)) {
  3384     NS_WARNING("Cannot create directory");
  3385     return NS_ERROR_FAILURE;
  3388   return NS_OK;
  3391 nsresult
  3392 CacheFileIOManager::CreateCacheTree()
  3394   MOZ_ASSERT(mIOThread->IsCurrentThread());
  3395   MOZ_ASSERT(!mTreeCreated);
  3397   if (!mCacheDirectory) {
  3398     return NS_ERROR_FILE_INVALID_PATH;
  3401   nsresult rv;
  3403   // ensure parent directory exists
  3404   nsCOMPtr<nsIFile> parentDir;
  3405   rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
  3406   NS_ENSURE_SUCCESS(rv, rv);
  3407   rv = CheckAndCreateDir(parentDir, nullptr, false);
  3408   NS_ENSURE_SUCCESS(rv, rv);
  3410   // ensure cache directory exists
  3411   rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
  3412   NS_ENSURE_SUCCESS(rv, rv);
  3414   // ensure entries directory exists
  3415   rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
  3416   NS_ENSURE_SUCCESS(rv, rv);
  3418   // ensure doomed directory exists
  3419   rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir, true);
  3420   NS_ENSURE_SUCCESS(rv, rv);
  3422   mTreeCreated = true;
  3424   if (!mContextEvictor) {
  3425     nsRefPtr<CacheFileContextEvictor> contextEvictor;
  3426     contextEvictor = new CacheFileContextEvictor();
  3428     // Init() method will try to load unfinished contexts from the disk. Store
  3429     // the evictor as a member only when there is some unfinished job.
  3430     contextEvictor->Init(mCacheDirectory);
  3431     if (contextEvictor->ContextsCount()) {
  3432       contextEvictor.swap(mContextEvictor);
  3436   StartRemovingTrash();
  3438   return NS_OK;
  3441 nsresult
  3442 CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
  3444   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  3445   MOZ_ASSERT(!aHandle->mFD);
  3446   MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
  3447   MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
  3448   MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
  3449              (!aCreate && aHandle->mFileExists));
  3451   nsresult rv;
  3453   if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
  3454     // close handle that hasn't been used for the longest time
  3455     rv = ReleaseNSPRHandleInternal(mHandlesByLastUsed[0]);
  3456     NS_ENSURE_SUCCESS(rv, rv);
  3459   if (aCreate) {
  3460     rv = aHandle->mFile->OpenNSPRFileDesc(
  3461            PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
  3462     NS_ENSURE_SUCCESS(rv, rv);
  3464     aHandle->mFileExists = true;
  3465   } else {
  3466     rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
  3467     if (NS_ERROR_FILE_NOT_FOUND == rv) {
  3468       LOG(("  file doesn't exists"));
  3469       aHandle->mFileExists = false;
  3470       return DoomFileInternal(aHandle);
  3472     NS_ENSURE_SUCCESS(rv, rv);
  3475   mHandlesByLastUsed.AppendElement(aHandle);
  3476   return NS_OK;
  3479 void
  3480 CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
  3482   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
  3483   MOZ_ASSERT(aHandle->mFD);
  3485   DebugOnly<bool> found;
  3486   found = mHandlesByLastUsed.RemoveElement(aHandle);
  3487   MOZ_ASSERT(found);
  3489   mHandlesByLastUsed.AppendElement(aHandle);
  3492 nsresult
  3493 CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
  3495   nsresult rv;
  3496   nsCOMPtr<nsIFile> file;
  3498   if (!aDir) {
  3499     file = aFile;
  3500   } else {
  3501     rv = aFile->Clone(getter_AddRefs(file));
  3502     if (NS_WARN_IF(NS_FAILED(rv))) {
  3503       return rv;
  3506     rv = file->AppendNative(nsDependentCString(aDir));
  3507     if (NS_WARN_IF(NS_FAILED(rv))) {
  3508       return rv;
  3512 #ifdef PR_LOGGING
  3513   nsAutoCString path;
  3514   file->GetNativePath(path);
  3515 #endif
  3517   LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
  3518        path.get()));
  3520   rv = file->Remove(true);
  3521   if (NS_WARN_IF(NS_FAILED(rv))) {
  3522     LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08x]",
  3523          rv));
  3526   return rv;
  3529 void
  3530 CacheFileIOManager::SyncRemoveAllCacheFiles()
  3532   LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
  3534   nsresult rv;
  3536   SyncRemoveDir(mCacheDirectory, kEntriesDir);
  3537   SyncRemoveDir(mCacheDirectory, kDoomedDir);
  3539   // Clear any intermediate state of trash dir enumeration.
  3540   mFailedTrashDirs.Clear();
  3541   mTrashDir = nullptr;
  3543   while (true) {
  3544     // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
  3545     rv = FindTrashDirToRemove();
  3546     if (rv == NS_ERROR_NOT_AVAILABLE) {
  3547       LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
  3548            "found."));
  3549       break;
  3551     if (NS_WARN_IF(NS_FAILED(rv))) {
  3552       LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
  3553            "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08x]",
  3554            rv));
  3555       break;
  3558     rv = SyncRemoveDir(mTrashDir, nullptr);
  3559     if (NS_FAILED(rv)) {
  3560       nsAutoCString leafName;
  3561       mTrashDir->GetNativeLeafName(leafName);
  3562       mFailedTrashDirs.AppendElement(leafName);
  3567 // Returns default ("smart") size (in KB) of cache, given available disk space
  3568 // (also in KB)
  3569 static uint32_t
  3570 SmartCacheSize(const uint32_t availKB)
  3572   uint32_t maxSize = kMaxCacheSizeKB;
  3574   if (availKB > 100 * 1024 * 1024) {
  3575     return maxSize;  // skip computing if we're over 100 GB
  3578   // Grow/shrink in 10 MB units, deliberately, so that in the common case we
  3579   // don't shrink cache and evict items every time we startup (it's important
  3580   // that we don't slow down startup benchmarks).
  3581   uint32_t sz10MBs = 0;
  3582   uint32_t avail10MBs = availKB / (1024*10);
  3584   // .5% of space above 25 GB
  3585   if (avail10MBs > 2500) {
  3586     sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
  3587     avail10MBs = 2500;
  3589   // 1% of space between 7GB -> 25 GB
  3590   if (avail10MBs > 700) {
  3591     sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
  3592     avail10MBs = 700;
  3594   // 5% of space between 500 MB -> 7 GB
  3595   if (avail10MBs > 50) {
  3596     sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
  3597     avail10MBs = 50;
  3600 #ifdef ANDROID
  3601   // On Android, smaller/older devices may have very little storage and
  3602   // device owners may be sensitive to storage footprint: Use a smaller
  3603   // percentage of available space and a smaller minimum.
  3605   // 20% of space up to 500 MB (10 MB min)
  3606   sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
  3607 #else
  3608   // 40% of space up to 500 MB (50 MB min)
  3609   sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
  3610 #endif
  3612   return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
  3615 nsresult
  3616 CacheFileIOManager::UpdateSmartCacheSize()
  3618   MOZ_ASSERT(mIOThread->IsCurrentThread());
  3620   nsresult rv;
  3622   if (!CacheObserver::UseNewCache()) {
  3623     return NS_ERROR_NOT_AVAILABLE;
  3626   if (!CacheObserver::SmartCacheSizeEnabled()) {
  3627     return NS_ERROR_NOT_AVAILABLE;
  3630   // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
  3631   static const TimeDuration kUpdateLimit =
  3632     TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
  3633   if (!mLastSmartSizeTime.IsNull() &&
  3634       (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
  3635     return NS_OK;
  3638   // Do not compute smart size when cache size is not reliable.
  3639   bool isUpToDate = false;
  3640   CacheIndex::IsUpToDate(&isUpToDate);
  3641   if (!isUpToDate) {
  3642     return NS_ERROR_NOT_AVAILABLE;
  3645   uint32_t cacheUsage;
  3646   rv = CacheIndex::GetCacheSize(&cacheUsage);
  3647   if (NS_WARN_IF(NS_FAILED(rv))) {
  3648     LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
  3649          "[rv=0x%08x]", rv));
  3650     return rv;
  3653   int64_t avail;
  3654   rv = mCacheDirectory->GetDiskSpaceAvailable(&avail);
  3655   if (NS_WARN_IF(NS_FAILED(rv))) {
  3656     // Do not change smart size.
  3657     LOG(("CacheFileIOManager::UpdateSmartCacheSize() - GetDiskSpaceAvailable() "
  3658          "failed! [rv=0x%08x]", rv));
  3659     return rv;
  3662   mLastSmartSizeTime = TimeStamp::NowLoRes();
  3664   uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(avail / 1024) +
  3665                                       cacheUsage);
  3667   if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
  3668     // Smart size has not changed.
  3669     return NS_OK;
  3672   CacheObserver::SetDiskCacheCapacity(smartSize << 10);
  3674   return NS_OK;
  3677 // Memory reporting
  3679 namespace { // anon
  3681 // A helper class that dispatches and waits for an event that gets result of
  3682 // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
  3683 // to safely get handles memory report.
  3684 // We must do this, since the handle list is only accessed and managed w/o
  3685 // locking on the I/O thread.  That is by design.
  3686 class SizeOfHandlesRunnable : public nsRunnable
  3688 public:
  3689   SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
  3690                         CacheFileHandles const &handles,
  3691                         nsTArray<CacheFileHandle *> const &specialHandles)
  3692     : mMonitor("SizeOfHandlesRunnable.mMonitor")
  3693     , mMallocSizeOf(mallocSizeOf)
  3694     , mHandles(handles)
  3695     , mSpecialHandles(specialHandles)
  3699   size_t Get(CacheIOThread* thread)
  3701     nsCOMPtr<nsIEventTarget> target = thread->Target();
  3702     if (!target) {
  3703       NS_ERROR("If we have the I/O thread we also must have the I/O target");
  3704       return 0;
  3707     mozilla::MonitorAutoLock mon(mMonitor);
  3708     nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
  3709     if (NS_FAILED(rv)) {
  3710       NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
  3711       return 0;
  3714     mon.Wait();
  3715     return mSize;
  3718   NS_IMETHOD Run()
  3720     mozilla::MonitorAutoLock mon(mMonitor);
  3721     // Excluding this since the object itself is a member of CacheFileIOManager
  3722     // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
  3723     mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
  3724     for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
  3725       mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
  3728     mon.Notify();
  3729     return NS_OK;
  3732 private:
  3733   mozilla::Monitor mMonitor;
  3734   mozilla::MallocSizeOf mMallocSizeOf;
  3735   CacheFileHandles const &mHandles;
  3736   nsTArray<CacheFileHandle *> const &mSpecialHandles;
  3737   size_t mSize;
  3738 };
  3740 } // anon
  3742 size_t
  3743 CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
  3745   size_t n = 0;
  3746   nsCOMPtr<nsISizeOf> sizeOf;
  3748   if (mIOThread) {
  3749     n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
  3751     // mHandles and mSpecialHandles must be accessed only on the I/O thread,
  3752     // must sync dispatch.
  3753     nsRefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
  3754       new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
  3755     n += sizeOfHandlesRunnable->Get(mIOThread);
  3758   // mHandlesByLastUsed just refers handles reported by mHandles.
  3760   sizeOf = do_QueryInterface(mCacheDirectory);
  3761   if (sizeOf)
  3762     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  3764   sizeOf = do_QueryInterface(mMetadataWritesTimer);
  3765   if (sizeOf)
  3766     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  3768   sizeOf = do_QueryInterface(mTrashTimer);
  3769   if (sizeOf)
  3770     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  3772   sizeOf = do_QueryInterface(mTrashDir);
  3773   if (sizeOf)
  3774     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  3776   for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
  3777     n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
  3780   return n;
  3783 // static
  3784 size_t
  3785 CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
  3787   if (!gInstance)
  3788     return 0;
  3790   return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
  3793 // static
  3794 size_t
  3795 CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
  3797   return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
  3800 } // net
  3801 } // mozilla

mercurial