Bug 968106 - HTTP cache v2: implementation of the eviction of the whole disk cache, r=honzab
authorMichal Novotny <michal.novotny@gmail.com>
Fri, 07 Mar 2014 12:22:59 +0100
changeset 191186 b5d8474e3042d2c32e85b1fc0435cd593c2ca572
parent 191185 75c53d96a9c94a3c4f8b36f6f5245bf8d06443a4
child 191187 3697a043583b5a422f41997f24f1b5fa636876bd
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs968106
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 968106 - HTTP cache v2: implementation of the eviction of the whole disk cache, r=honzab
netwerk/cache2/CacheEntriesEnumerator.cpp
netwerk/cache2/CacheEntriesEnumerator.h
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheFileIOManager.h
netwerk/cache2/CacheIOThread.h
netwerk/cache2/CacheIndex.cpp
netwerk/cache2/CacheIndex.h
netwerk/cache2/CacheStorageService.cpp
netwerk/cache2/moz.build
deleted file mode 100644
--- a/netwerk/cache2/CacheEntriesEnumerator.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "CacheEntriesEnumerator.h"
-
-#include "CacheFileIOManager.h"
-#include "CacheFile.h"
-
-#include "nsIDirectoryEnumerator.h"
-#include "nsIFile.h"
-#include "nsIThread.h"
-#include "nsISimpleEnumerator.h"
-#include "nsThreadUtils.h"
-
-namespace mozilla {
-namespace net {
-
-CacheEntriesEnumerator::CacheEntriesEnumerator(nsIFile* aEntriesDirectory)
-: mEntriesDirectory(aEntriesDirectory)
-{
-  MOZ_COUNT_CTOR(CacheEntriesEnumerator);
-}
-
-CacheEntriesEnumerator::~CacheEntriesEnumerator()
-{
-  MOZ_COUNT_DTOR(CacheEntriesEnumerator);
-
-  if (mEnumerator) {
-    mEnumerator->Close();
-    ProxyReleaseMainThread(mEnumerator);
-  }
-}
-
-nsresult CacheEntriesEnumerator::Init()
-{
-  nsresult rv;
-
-  nsCOMPtr<nsISimpleEnumerator> e;
-  rv = mEntriesDirectory->GetDirectoryEntries(getter_AddRefs(e));
-
-  if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
-    return NS_OK;
-  }
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mEnumerator = do_QueryInterface(e, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-bool CacheEntriesEnumerator::HasMore()
-{
-#ifdef DEBUG
-  if (!mThreadCheck)
-    mThreadCheck = NS_GetCurrentThread();
-  else
-    MOZ_ASSERT(mThreadCheck == NS_GetCurrentThread());
-#endif
-
-  if (!mEnumerator) {
-    return false;
-  }
-
-  if (mCurrentFile)
-    return true;
-
-  nsresult rv;
-
-  rv = mEnumerator->GetNextFile(getter_AddRefs(mCurrentFile));
-
-  if (NS_FAILED(rv)) {
-    mEnumerator->Close();
-    mEnumerator = nullptr;
-    return false;
-  }
-
-  return !!mCurrentFile;
-}
-
-nsresult CacheEntriesEnumerator::GetNextFile(nsIFile** aFile)
-{
-#ifdef DEBUG
-  MOZ_ASSERT(mThreadCheck == NS_GetCurrentThread());
-#endif
-
-  NS_ENSURE_TRUE(mCurrentFile, NS_ERROR_UNEXPECTED);
-
-  mCurrentFile.forget(aFile);
-  return NS_OK;
-}
-
-} // net
-} // mozilla
deleted file mode 100644
--- a/netwerk/cache2/CacheEntriesEnumerator.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef CacheEntriesEnumerator__h__
-#define CacheEntriesEnumerator__h__
-
-#include "nsCOMPtr.h"
-
-class nsIFile;
-class nsIDirectoryEnumerator;
-class nsIThread;
-
-namespace mozilla {
-namespace net {
-
-class CacheFileIOManager;
-class CacheFileListener;
-class CacheFile;
-
-class CacheEntriesEnumeratorCallback : public nsISupports
-{
-public:
-  virtual void OnFile(CacheFile* aFile) = 0;
-};
-
-class CacheEntriesEnumerator
-{
-public:
-  ~CacheEntriesEnumerator();
-
-  bool HasMore();
-  nsresult GetNextFile(nsIFile** aFile);
-
-protected:
-  friend class CacheFileIOManager;
-  CacheEntriesEnumerator(nsIFile* aEntriesDirectory);
-  nsresult Init();
-
-private:
-  nsCOMPtr<nsIDirectoryEnumerator> mEnumerator;
-  nsCOMPtr<nsIFile> mEntriesDirectory;
-  nsCOMPtr<nsIFile> mCurrentFile;
-
-#ifdef DEBUG
-  nsCOMPtr<nsIThread> mThreadCheck;
-#endif
-};
-
-} // net
-} // mozilla
-
-#endif
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -12,19 +12,21 @@
 #include "CacheFileUtils.h"
 #include "nsThreadUtils.h"
 #include "CacheFile.h"
 #include "CacheObserver.h"
 #include "nsIFile.h"
 #include "nsITimer.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
+#include "nsIObserverService.h"
 #include "nsISizeOf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Services.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "private/pprio.h"
 #include "mozilla/VisualEventTracer.h"
 
 // include files for ftruncate (or equivalent)
 #if defined(XP_UNIX)
 #include <unistd.h>
@@ -432,16 +434,40 @@ GetAllHandlesEnum(CacheFileHandles::Hand
 
 void
 CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
 }
 
+static PLDHashOperator
+GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
+{
+  nsTArray<nsRefPtr<CacheFileHandle> > *array =
+    static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
+
+  nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
+  MOZ_ASSERT(handle);
+
+  if (!handle->IsDoomed()) {
+    array->AppendElement(handle);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+void
+CacheFileHandles::GetActiveHandles(
+  nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
+{
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
+  mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
+}
+
 void
 CacheFileHandles::ClearAll()
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   mTable.Clear();
 }
 
 uint32_t
@@ -1106,16 +1132,17 @@ CacheFileIOManager::CacheFileIOManager()
 }
 
 CacheFileIOManager::~CacheFileIOManager()
 {
   LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
   MOZ_COUNT_DTOR(CacheFileIOManager);
 }
 
+// static
 nsresult
 CacheFileIOManager::Init()
 {
   LOG(("CacheFileIOManager::Init()"));
 
   MOZ_ASSERT(NS_IsMainThread());
 
   if (gInstance) {
@@ -1142,16 +1169,17 @@ CacheFileIOManager::InitInternal()
   MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
   NS_ENSURE_SUCCESS(rv, rv);
 
   mStartTime = TimeStamp::NowLoRes();
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::Shutdown()
 {
   LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
 
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gInstance) {
@@ -1246,16 +1274,17 @@ CacheFileIOManager::ShutdownInternal()
   if (mTrashDirEnumerator) {
     mTrashDirEnumerator->Close();
     mTrashDirEnumerator = nullptr;
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::OnProfile()
 {
   LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
 
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
   if (!ioMan) {
     // CacheFileIOManager::Init() failed, probably could not create the IO
@@ -1482,16 +1511,17 @@ CacheFileIOManager::Notify(nsITimer * aT
   for (uint32_t i = 0; i < files.Length(); ++i) {
     CacheFile * file = files[i];
     file->WriteMetadataIfNeeded();
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::OpenFile(const nsACString &aKey,
                              uint32_t aFlags,
                              CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
        PromiseFlatCString(aKey).get(), aFlags, aCallback));
 
@@ -1728,16 +1758,17 @@ CacheFileIOManager::CloseHandleInternal(
     mSpecialHandles.RemoveElement(aHandle);
   } else if (!mShuttingDown) { // Don't touch after shutdown
     mHandles.RemoveHandle(aHandle);
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
                          char *aBuf, int32_t aCount,
                          CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
        "listener=%p]", aHandle, aOffset, aCount, aCallback));
 
@@ -1793,16 +1824,17 @@ CacheFileIOManager::ReadInternal(CacheFi
   int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
   if (bytesRead != aCount) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
                           const char *aBuf, int32_t aCount, bool aValidate,
                           CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
        "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
        aCallback));
@@ -1876,16 +1908,17 @@ CacheFileIOManager::WriteInternal(CacheF
   // Write was successful and this write validates the entry (i.e. metadata)
   if (aValidate) {
     aHandle->mInvalid = false;
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
                              CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
        aHandle, aCallback));
 
   nsresult rv;
@@ -1963,16 +1996,17 @@ CacheFileIOManager::DoomFileInternal(Cac
         storageService->CacheFileDoomed(info, url);
       }
     }
   }
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
                                   CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
        PromiseFlatCString(aKey).get(), aCallback));
 
   nsresult rv;
@@ -2043,16 +2077,17 @@ CacheFileIOManager::DoomFileByKeyInterna
          "[rv=0x%08x]", rv));
   }
 
   CacheIndex::RemoveEntry(aHash);
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
 {
   LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
@@ -2080,16 +2115,17 @@ CacheFileIOManager::ReleaseNSPRHandleInt
   MOZ_ASSERT(found);
 
   PR_Close(aHandle->mFD);
   aHandle->mFD = nullptr;
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
                                        int64_t aTruncatePos, int64_t aEOFPos,
                                        CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
        "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
 
@@ -2181,16 +2217,17 @@ CacheFileIOManager::TruncateSeekSetEOFIn
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TruncFile(aHandle->mFD, static_cast<uint32_t>(aEOFPos));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
                                const nsACString &aNewName,
                                CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
        aHandle, PromiseFlatCString(aNewName).get(), aCallback));
 
@@ -2269,16 +2306,17 @@ CacheFileIOManager::RenameFileInternal(C
 
   rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aHandle->mKey = aNewName;
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::EvictIfOverLimit()
 {
   LOG(("CacheFileIOManager::EvictIfOverLimit()"));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
@@ -2434,16 +2472,132 @@ CacheFileIOManager::OverLimitEvictionInt
       }
     }
   }
 
   NS_NOTREACHED("We should never get here");
   return NS_OK;
 }
 
+// static
+nsresult
+CacheFileIOManager::EvictAll()
+{
+  LOG(("CacheFileIOManager::EvictAll()"));
+
+  nsresult rv;
+  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
+
+  if (!ioMan) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  nsCOMPtr<nsIRunnable> ev;
+  ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
+
+  rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::OPEN_PRIORITY);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+namespace {
+
+class EvictionNotifierRunnable : public nsRunnable
+{
+public:
+  NS_DECL_NSIRUNNABLE
+};
+
+NS_IMETHODIMP
+EvictionNotifierRunnable::Run()
+{
+  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+  if (obsSvc) {
+    obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
+  }
+  return NS_OK;
+}
+
+} // anonymous namespace
+
+nsresult
+CacheFileIOManager::EvictAllInternal()
+{
+  LOG(("CacheFileIOManager::EvictAllInternal()"));
+
+  nsresult rv;
+
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
+  nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
+
+  if (!mCacheDirectory) {
+    // This is a kind of hack. Somebody called EvictAll() without a profile.
+    // This happens in xpcshell tests that use cache without profile. We need
+    // to notify observers in this case since the tests are waiting for it.
+    NS_DispatchToMainThread(r);
+    return NS_ERROR_FILE_INVALID_PATH;
+  }
+
+  if (mShuttingDown) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (!mTreeCreated) {
+    rv = CreateCacheTree();
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  // Doom all active handles
+  nsTArray<nsRefPtr<CacheFileHandle> > handles;
+  mHandles.GetActiveHandles(&handles);
+
+  for (uint32_t i = 0; i < handles.Length(); ++i) {
+    rv = DoomFileInternal(handles[i]);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIFile> file;
+  rv = mCacheDirectory->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Trash current entries directory
+  rv = TrashDirectory(file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Files are now inaccessible in entries directory, notify observers.
+  NS_DispatchToMainThread(r);
+
+  // Create a new empty entries directory
+  rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  CacheIndex::RemoveAll();
+
+  return NS_OK;
+}
+
 nsresult
 CacheFileIOManager::TrashDirectory(nsIFile *aFile)
 {
 #ifdef PR_LOGGING
   nsAutoCString path;
   aFile->GetNativePath(path);
 #endif
   LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
@@ -2505,16 +2659,17 @@ CacheFileIOManager::TrashDirectory(nsIFi
 
   rv = dir->MoveToNative(nullptr, leaf);
   NS_ENSURE_SUCCESS(rv, rv);
 
   StartRemovingTrash();
   return NS_OK;
 }
 
+// static
 void
 CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
 {
   LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
        aClosure));
 
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
@@ -2750,16 +2905,17 @@ CacheFileIOManager::FindTrashDirToRemove
 
   // When we're here we've tried to delete all trash directories. Clear
   // mFailedTrashDirs so we will try to delete them again when we start removing
   // trash directories next time.
   mFailedTrashDirs.Clear();
   return NS_ERROR_NOT_AVAILABLE;
 }
 
+// static
 nsresult
 CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
                                    uint32_t         aAppId,
                                    bool             aAnonymous,
                                    bool             aInBrowser)
 {
   LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
        ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
@@ -2778,16 +2934,17 @@ CacheFileIOManager::InitIndexEntry(Cache
   nsRefPtr<InitIndexEntryEvent> ev =
     new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+// static
 nsresult
 CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
                                      const uint32_t  *aFrecency,
                                      const uint32_t  *aExpirationTime)
 {
   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
        "expirationTime=%s]", aHandle,
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
@@ -2808,64 +2965,16 @@ CacheFileIOManager::UpdateIndexEntry(Cac
     new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
-CacheFileIOManager::EnumerateEntryFiles(EEnumerateMode aMode,
-                                        CacheEntriesEnumerator** aEnumerator)
-{
-  LOG(("CacheFileIOManager::EnumerateEntryFiles(%d)", aMode));
-
-  nsresult rv;
-  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
-
-  if (!ioMan) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
-  if (!ioMan->mCacheDirectory) {
-    return NS_ERROR_FILE_NOT_FOUND;
-  }
-
-  nsCOMPtr<nsIFile> file;
-  rv = ioMan->mCacheDirectory->Clone(getter_AddRefs(file));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  switch (aMode) {
-  case ENTRIES:
-    rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    break;
-
-  case DOOMED:
-    rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    break;
-
-  default:
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsAutoPtr<CacheEntriesEnumerator> enumerator(
-    new CacheEntriesEnumerator(file));
-
-  rv = enumerator->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aEnumerator = enumerator.forget();
-  return NS_OK;
-}
-
-nsresult
 CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
 {
   MOZ_ASSERT(!aHandle->mFD);
   MOZ_ASSERT(aHandle->mFile);
 
   nsresult rv;
 
   if (aHandle->IsDoomed()) {
@@ -2884,28 +2993,30 @@ CacheFileIOManager::CreateFile(CacheFile
 
   rv = OpenNSPRHandle(aHandle, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aHandle->mFileSize = 0;
   return NS_OK;
 }
 
+// static
 void
 CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
 {
   _retval.Assign("");
   const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
   for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
     _retval.Append(hexChars[(*aHash)[i] >> 4]);
     _retval.Append(hexChars[(*aHash)[i] & 0xF]);
   }
 }
 
+// static
 nsresult
 CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
 {
   if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   for (uint32_t i=0 ; i<aHash.Length() ; i++) {
--- a/netwerk/cache2/CacheFileIOManager.h
+++ b/netwerk/cache2/CacheFileIOManager.h
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CacheFileIOManager__h__
 #define CacheFileIOManager__h__
 
 #include "CacheIOThread.h"
-#include "CacheEntriesEnumerator.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "mozilla/SHA1.h"
 #include "mozilla/TimeStamp.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsTHashtable.h"
@@ -21,16 +20,17 @@
 
 class nsIFile;
 class nsITimer;
 class nsIDirectoryEnumerator;
 
 namespace mozilla {
 namespace net {
 
+class CacheFile;
 #ifdef DEBUG_HANDLES
 class CacheFileHandlesEntry;
 #endif
 
 const char kEntriesDir[] = "entries";
 const char kDoomedDir[]  = "doomed";
 const char kTrashDir[]   = "trash";
 
@@ -85,16 +85,17 @@ class CacheFileHandles {
 public:
   CacheFileHandles();
   ~CacheFileHandles();
 
   nsresult GetHandle(const SHA1Sum::Hash *aHash, bool aReturnDoomed, CacheFileHandle **_retval);
   nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval);
   void     RemoveHandle(CacheFileHandle *aHandlle);
   void     GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
+  void     GetActiveHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
   void     ClearAll();
   uint32_t HandleCount();
 
 #ifdef DEBUG_HANDLES
   void     Log(CacheFileHandlesEntry *entry);
 #endif
 
   // Memory reporting
@@ -250,35 +251,33 @@ public:
   static nsresult ReleaseNSPRHandle(CacheFileHandle *aHandle);
   static nsresult TruncateSeekSetEOF(CacheFileHandle *aHandle,
                                      int64_t aTruncatePos, int64_t aEOFPos,
                                      CacheFileIOListener *aCallback);
   static nsresult RenameFile(CacheFileHandle *aHandle,
                              const nsACString &aNewName,
                              CacheFileIOListener *aCallback);
   static nsresult EvictIfOverLimit();
+  static nsresult EvictAll();
 
   static nsresult InitIndexEntry(CacheFileHandle *aHandle,
                                  uint32_t         aAppId,
                                  bool             aAnonymous,
                                  bool             aInBrowser);
   static nsresult UpdateIndexEntry(CacheFileHandle *aHandle,
                                    const uint32_t  *aFrecency,
                                    const uint32_t  *aExpirationTime);
 
   static nsresult UpdateIndexEntry();
 
   enum EEnumerateMode {
     ENTRIES,
     DOOMED
   };
 
-  static nsresult EnumerateEntryFiles(EEnumerateMode aMode,
-                                      CacheEntriesEnumerator** aEnumerator);
-
   static void GetCacheDirectory(nsIFile** result);
 
   // Memory reporting
   static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
   static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 private:
   friend class CacheFileHandle;
@@ -317,16 +316,17 @@ private:
   nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash);
   nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle);
   nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
                                       int64_t aTruncatePos, int64_t aEOFPos);
   nsresult RenameFileInternal(CacheFileHandle *aHandle,
                               const nsACString &aNewName);
   nsresult EvictIfOverLimitInternal();
   nsresult OverLimitEvictionInternal();
+  nsresult EvictAllInternal();
 
   nsresult TrashDirectory(nsIFile *aFile);
   static void OnTrashTimer(nsITimer *aTimer, void *aClosure);
   nsresult StartRemovingTrash();
   nsresult RemoveTrashInternal();
   nsresult FindTrashDirToRemove();
 
   nsresult CreateFile(CacheFileHandle *aHandle);
--- a/netwerk/cache2/CacheIOThread.h
+++ b/netwerk/cache2/CacheIOThread.h
@@ -29,17 +29,17 @@ public:
   enum ELevel {
     OPEN_PRIORITY,
     READ_PRIORITY,
     OPEN,
     READ,
     WRITE,
     MANAGEMENT,
     CLOSE,
-    BUILD_OR_UPDATE_INDEX,
+    INDEX,
     EVICT,
     LAST_LEVEL,
 
     // This is actually executed as the first level, but we want this enum
     // value merely as an indicator while other values are used as indexes
     // to the queue array.  Hence put at end and not as the first.
     XPCOM_LEVEL
   };
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -11,16 +11,17 @@
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsISizeOf.h"
 #include "nsPrintfCString.h"
 #include "mozilla/DebugOnly.h"
 #include "prinrval.h"
 #include "nsIFile.h"
 #include "nsITimer.h"
+#include "mozilla/AutoRestore.h"
 #include <algorithm>
 
 
 #define kMinUnwrittenChanges   300
 #define kMinDumpInterval       20000 // in milliseconds
 #define kMaxBufSize            16384
 #define kIndexVersion          0x00000001
 #define kBuildIndexStartDelay  10000 // in milliseconds
@@ -216,16 +217,82 @@ public:
     mLocked = false;
   }
 
 private:
   nsRefPtr<CacheIndex> mIndex;
   bool mLocked;
 };
 
+class FileOpenHelper : public CacheFileIOListener
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  FileOpenHelper(CacheIndex* aIndex)
+    : mIndex(aIndex)
+    , mCanceled(false)
+  {}
+
+  virtual ~FileOpenHelper() {}
+
+  void Cancel() {
+    mIndex->AssertOwnsLock();
+    mCanceled = true;
+  }
+
+private:
+  NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
+  NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
+                           nsresult aResult) {
+    MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf,
+                        nsresult aResult) {
+    MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) {
+    MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) {
+    MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) {
+    MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<CacheIndex> mIndex;
+  bool                 mCanceled;
+};
+
+NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle,
+                                           nsresult aResult)
+{
+  CacheIndexAutoLock lock(mIndex);
+
+  if (mCanceled) {
+    if (aHandle) {
+      CacheFileIOManager::DoomFile(aHandle, nullptr);
+    }
+
+    return NS_OK;
+  }
+
+  mIndex->OnFileOpenedInternal(this, aHandle, aResult);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(FileOpenHelper, CacheFileIOListener);
+
 
 CacheIndex * CacheIndex::gInstance = nullptr;
 
 
 NS_IMPL_ADDREF(CacheIndex)
 NS_IMPL_RELEASE(CacheIndex)
 
 NS_INTERFACE_MAP_BEGIN(CacheIndex)
@@ -234,26 +301,26 @@ NS_INTERFACE_MAP_BEGIN(CacheIndex)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 
 CacheIndex::CacheIndex()
   : mLock("CacheFile.mLock")
   , mState(INITIAL)
   , mShuttingDown(false)
   , mIndexNeedsUpdate(false)
+  , mRemovingAll(false)
   , mIndexOnDiskIsValid(false)
   , mDontMarkIndexClean(false)
   , mIndexTimeStamp(0)
+  , mUpdateEventPending(false)
   , mSkipEntries(0)
   , mProcessEntries(0)
   , mRWBuf(nullptr)
   , mRWBufSize(0)
   , mRWBufPos(0)
-  , mReadOpenCount(0)
-  , mReadFailed(false)
   , mJournalReadSuccessfully(false)
 {
   LOG(("CacheIndex::CacheIndex [this=%p]", this));
   MOZ_COUNT_CTOR(CacheIndex);
   MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!");
 }
 
 CacheIndex::~CacheIndex()
@@ -295,45 +362,36 @@ CacheIndex::Init(nsIFile *aCacheDirector
   MOZ_ASSERT(NS_IsMainThread());
 
   if (gInstance) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   nsRefPtr<CacheIndex> idx = new CacheIndex();
 
+  CacheIndexAutoLock lock(idx);
+
   nsresult rv = idx->InitInternal(aCacheDirectory);
   NS_ENSURE_SUCCESS(rv, rv);
 
   idx.swap(gInstance);
   return NS_OK;
 }
 
 nsresult
 CacheIndex::InitInternal(nsIFile *aCacheDirectory)
 {
   nsresult rv;
 
   rv = aCacheDirectory->Clone(getter_AddRefs(mCacheDirectory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  ChangeState(READING);
-
   mStartTime = TimeStamp::NowLoRes();
 
-  // dispatch an event since IO manager's path is not initialized yet
-  nsCOMPtr<nsIRunnable> event;
-  event = NS_NewRunnableMethod(this, &CacheIndex::ReadIndexFromDisk);
-
-  rv = NS_DispatchToCurrentThread(event);
-  if (NS_FAILED(rv)) {
-    ChangeState(INITIAL);
-    LOG(("CacheIndex::InitInternal() - Cannot dispatch event"));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  ReadIndexFromDisk();
 
   return NS_OK;
 }
 
 // static
 nsresult
 CacheIndex::PreShutdown()
 {
@@ -385,33 +443,31 @@ CacheIndex::PreShutdownInternal()
   CacheIndexAutoLock lock(this);
 
   LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
        "dontMarkIndexClean=%d]", mState, mIndexOnDiskIsValid,
        mDontMarkIndexClean));
 
   MOZ_ASSERT(mShuttingDown);
 
-  if (mTimer) {
-    mTimer = nullptr;
+  if (mUpdateTimer) {
+    mUpdateTimer = nullptr;
   }
 
   switch (mState) {
     case WRITING:
       FinishWrite(false);
       break;
     case READY:
       // nothing to do, write the journal in Shutdown()
       break;
     case READING:
       FinishRead(false);
       break;
     case BUILDING:
-      FinishBuild(false);
-      break;
     case UPDATING:
       FinishUpdate(false);
       break;
     default:
       MOZ_ASSERT(false, "Implement me!");
   }
 
   // We should end up in READY state
@@ -461,18 +517,16 @@ CacheIndex::Shutdown()
       } else {
         index->RemoveIndexFromDisk();
       }
       break;
     case READING:
       index->FinishRead(false);
       break;
     case BUILDING:
-      index->FinishBuild(false);
-      break;
     case UPDATING:
       index->FinishUpdate(false);
       break;
     default:
       MOZ_ASSERT(false, "Unexpected state!");
   }
 
   return NS_OK;
@@ -975,16 +1029,100 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
 
   index->WriteIndexToDiskIfNeeded();
 
   return NS_OK;
 }
 
 // static
 nsresult
+CacheIndex::RemoveAll()
+{
+  LOG(("CacheIndex::RemoveAll()"));
+
+  nsRefPtr<CacheIndex> index = gInstance;
+
+  if (!index) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  nsCOMPtr<nsIFile> file;
+
+  {
+    CacheIndexAutoLock lock(index);
+
+    MOZ_ASSERT(!index->mRemovingAll);
+
+    if (!index->IsIndexUsable()) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+
+    AutoRestore<bool> saveRemovingAll(index->mRemovingAll);
+    index->mRemovingAll = true;
+
+    // Doom index and journal handles but don't null them out since this will be
+    // done in FinishWrite/FinishRead methods.
+    if (index->mIndexHandle) {
+      CacheFileIOManager::DoomFile(index->mIndexHandle, nullptr);
+    } else {
+      // We don't have a handle to index file, so get the file here, but delete
+      // it outside the lock. Ignore the result since this is not fatal.
+      index->GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(file));
+    }
+
+    if (index->mJournalHandle) {
+      CacheFileIOManager::DoomFile(index->mJournalHandle, nullptr);
+    }
+
+    switch (index->mState) {
+      case WRITING:
+        index->FinishWrite(false);
+        break;
+      case READY:
+        // nothing to do
+        break;
+      case READING:
+        index->FinishRead(false);
+        break;
+      case BUILDING:
+      case UPDATING:
+        index->FinishUpdate(false);
+        break;
+      default:
+        MOZ_ASSERT(false, "Unexpected state!");
+    }
+
+    // We should end up in READY state
+    MOZ_ASSERT(index->mState == READY);
+
+    // There should not be any handle
+    MOZ_ASSERT(!index->mIndexHandle);
+    MOZ_ASSERT(!index->mJournalHandle);
+
+    index->mIndexOnDiskIsValid = false;
+    index->mIndexNeedsUpdate = false;
+
+    index->mIndexStats.Clear();
+    index->mFrecencyArray.Clear();
+    index->mExpirationArray.Clear();
+    index->mIndex.Clear();
+  }
+
+  if (file) {
+    // Ignore the result. The file might not exist and the failure is not fatal.
+    file->Remove(false);
+  }
+
+  return NS_OK;
+}
+
+// static
+nsresult
 CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
 {
   LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey).get()));
 
   nsRefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
@@ -1269,20 +1407,21 @@ CacheIndex::WriteIndexToDisk()
   MOZ_ASSERT(mState == READY);
   MOZ_ASSERT(!mRWBuf);
   MOZ_ASSERT(!mRWHash);
 
   ChangeState(WRITING);
 
   mProcessEntries = mIndexStats.ActiveEntriesCount();
 
+  mIndexFileOpener = new FileOpenHelper(this);
   rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
                                     CacheFileIOManager::SPECIAL_FILE |
                                     CacheFileIOManager::CREATE,
-                                    this);
+                                    mIndexFileOpener);
   if (NS_FAILED(rv)) {
     LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08x]", rv));
     FinishWrite(false);
     return;
   }
 
   // Write index header to a buffer, it will be written to disk together with
   // records in WriteRecords() once we open the file successfully.
@@ -1391,18 +1530,29 @@ CacheIndex::FinishWrite(bool aSucceeded)
 
   AssertOwnsLock();
 
   mIndexHandle = nullptr;
   mRWHash = nullptr;
   ReleaseBuffer();
 
   if (aSucceeded) {
+    // Opening of the file must not be in progress if writing succeeded.
+    MOZ_ASSERT(!mIndexFileOpener);
+
     mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
     mIndexOnDiskIsValid = true;
+  } else {
+    if (mIndexFileOpener) {
+      // If opening of the file is still in progress (e.g. WRITE process was
+      // canceled by RemoveAll()) then we need to cancel the opener to make sure
+      // that OnFileOpenedInternal() won't be called.
+      mIndexFileOpener->Cancel();
+      mIndexFileOpener = nullptr;
+    }
   }
 
   ProcessPendingOperations();
   mIndexStats.Log();
 
   if (mState == WRITING) {
     ChangeState(READY);
     mLastDumpTime = TimeStamp::NowLoRes();
@@ -1697,60 +1847,52 @@ CacheIndex::WriteEntryToLog(CacheIndexEn
 
 void
 CacheIndex::ReadIndexFromDisk()
 {
   LOG(("CacheIndex::ReadIndexFromDisk()"));
 
   nsresult rv;
 
-  CacheIndexAutoLock lock(this);
-
-  MOZ_ASSERT(mState == READING);
-
-  mReadFailed = false;
-  mReadOpenCount = 0;
-
+  AssertOwnsLock();
+  MOZ_ASSERT(mState == INITIAL);
+
+  ChangeState(READING);
+
+  mIndexFileOpener = new FileOpenHelper(this);
   rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kIndexName),
                                     CacheFileIOManager::SPECIAL_FILE |
                                     CacheFileIOManager::OPEN,
-                                    this);
+                                    mIndexFileOpener);
   if (NS_FAILED(rv)) {
     LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
          "failed [rv=0x%08x, file=%s]", rv, kIndexName));
-    mReadFailed = true;
-  } else {
-    mReadOpenCount++;
+    FinishRead(false);
+    return;
   }
 
+  mJournalFileOpener = new FileOpenHelper(this);
   rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kJournalName),
                                     CacheFileIOManager::SPECIAL_FILE |
                                     CacheFileIOManager::OPEN,
-                                    this);
+                                    mJournalFileOpener);
   if (NS_FAILED(rv)) {
     LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
          "failed [rv=0x%08x, file=%s]", rv, kJournalName));
-    mReadFailed = true;
-  } else {
-    mReadOpenCount++;
+    FinishRead(false);
   }
 
+  mTmpFileOpener = new FileOpenHelper(this);
   rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
                                     CacheFileIOManager::SPECIAL_FILE |
                                     CacheFileIOManager::OPEN,
-                                    this);
+                                    mTmpFileOpener);
   if (NS_FAILED(rv)) {
     LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
          "failed [rv=0x%08x, file=%s]", rv, kTempIndexName));
-    mReadFailed = true;
-  } else {
-    mReadOpenCount++;
-  }
-
-  if (mReadOpenCount == 0) {
     FinishRead(false);
   }
 }
 
 void
 CacheIndex::StartReadingIndex()
 {
   LOG(("CacheIndex::StartReadingIndex()"));
@@ -2128,118 +2270,135 @@ CacheIndex::FinishRead(bool aSucceeded)
       CacheFileIOManager::DoomFile(mIndexHandle, nullptr);
     }
 
     if (mJournalHandle) {
       CacheFileIOManager::DoomFile(mJournalHandle, nullptr);
     }
   }
 
+  if (mIndexFileOpener) {
+    mIndexFileOpener->Cancel();
+    mIndexFileOpener = nullptr;
+  }
+  if (mJournalFileOpener) {
+    mJournalFileOpener->Cancel();
+    mJournalFileOpener = nullptr;
+  }
+  if (mTmpFileOpener) {
+    mTmpFileOpener->Cancel();
+    mTmpFileOpener = nullptr;
+  }
+
   mIndexHandle = nullptr;
   mJournalHandle = nullptr;
   mRWHash = nullptr;
   ReleaseBuffer();
 
   if (mState == SHUTDOWN) {
     return;
   }
 
   if (!mIndexOnDiskIsValid) {
     MOZ_ASSERT(mTmpJournal.Count() == 0);
     EnsureNoFreshEntry();
     ProcessPendingOperations();
     // Remove all entries that we haven't seen during this session
     mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
-    StartBuildingIndex();
+    StartUpdatingIndex(true);
     return;
   }
 
   if (!mJournalReadSuccessfully) {
     mTmpJournal.Clear();
     EnsureNoFreshEntry();
     ProcessPendingOperations();
-    StartUpdatingIndex();
+    StartUpdatingIndex(false);
     return;
   }
 
   MergeJournal();
   EnsureNoFreshEntry();
   ProcessPendingOperations();
   mIndexStats.Log();
 
   ChangeState(READY);
   mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
 }
 
 // static
 void
-CacheIndex::DelayedBuildUpdate(nsITimer *aTimer, void *aClosure)
+CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure)
 {
-  LOG(("CacheIndex::DelayedBuildUpdate()"));
+  LOG(("CacheIndex::DelayedUpdate()"));
 
   nsresult rv;
   nsRefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return;
   }
 
   CacheIndexAutoLock lock(index);
 
-  index->mTimer = nullptr;
+  index->mUpdateTimer = nullptr;
 
   if (!index->IsIndexUsable()) {
     return;
   }
 
   if (index->mState == READY && index->mShuttingDown) {
     return;
   }
 
-  MOZ_ASSERT(index->mState == BUILDING || index->mState == UPDATING);
+  // mUpdateEventPending must be false here since StartUpdatingIndex() won't
+  // schedule timer if it is true.
+  MOZ_ASSERT(!index->mUpdateEventPending);
+  if (index->mState != BUILDING && index->mState != UPDATING) {
+    LOG(("CacheIndex::DelayedUpdate() - Update was canceled"));
+    return;
+  }
 
   // We need to redispatch to run with lower priority
   nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
   MOZ_ASSERT(ioThread);
 
-  rv = ioThread->Dispatch(index, CacheIOThread::BUILD_OR_UPDATE_INDEX);
+  index->mUpdateEventPending = true;
+  rv = ioThread->Dispatch(index, CacheIOThread::INDEX);
   if (NS_FAILED(rv)) {
-    NS_WARNING("CacheIndex::DelayedBuildUpdate() - Can't dispatch event");
-    LOG(("CacheIndex::DelayedBuildUpdate() - Can't dispatch event" ));
-    if (index->mState == BUILDING) {
-      index->FinishBuild(false);
-    } else {
-      index->FinishUpdate(false);
-    }
+    index->mUpdateEventPending = false;
+    NS_WARNING("CacheIndex::DelayedUpdate() - Can't dispatch event");
+    LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event" ));
+    index->FinishUpdate(false);
   }
 }
 
 nsresult
-CacheIndex::ScheduleBuildUpdateTimer(uint32_t aDelay)
+CacheIndex::ScheduleUpdateTimer(uint32_t aDelay)
 {
-  LOG(("CacheIndex::ScheduleBuildUpdateTimer() [delay=%u]", aDelay));
-
-  MOZ_ASSERT(!mTimer);
+  LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay));
+
+  MOZ_ASSERT(!mUpdateTimer);
 
   nsresult rv;
 
   nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
   MOZ_ASSERT(ioTarget);
 
   rv = timer->SetTarget(ioTarget);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = timer->InitWithFuncCallback(CacheIndex::DelayedBuildUpdate, nullptr,
+  rv = timer->InitWithFuncCallback(CacheIndex::DelayedUpdate, nullptr,
                                    aDelay, nsITimer::TYPE_ONE_SHOT);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mTimer.swap(timer);
+  mUpdateTimer.swap(timer);
   return NS_OK;
 }
 
 nsresult
 CacheIndex::SetupDirectoryEnumerator()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mDirEnumerator);
@@ -2294,53 +2453,26 @@ CacheIndex::InitEntryFromDiskData(CacheI
   aMetaData->GetFrecency(&frecency);
   aEntry->SetFrecency(frecency);
 
   aEntry->SetFileSize(static_cast<uint32_t>(
                         std::min(static_cast<int64_t>(PR_UINT32_MAX),
                                  (aFileSize + 0x3FF) >> 10)));
 }
 
-void
-CacheIndex::StartBuildingIndex()
+bool
+CacheIndex::IsUpdatePending()
 {
-  LOG(("CacheIndex::StartBuildingIndex()"));
-
-  nsresult rv;
-
-  ChangeState(BUILDING);
-  mDontMarkIndexClean = false;
-
-  if (mShuttingDown) {
-    FinishBuild(false);
-    return;
+  AssertOwnsLock();
+
+  if (mUpdateTimer || mUpdateEventPending) {
+    return true;
   }
 
-  uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
-  if (elapsed < kBuildIndexStartDelay) {
-    rv = ScheduleBuildUpdateTimer(kBuildIndexStartDelay - elapsed);
-    if (NS_SUCCEEDED(rv)) {
-      return;
-    }
-
-    LOG(("CacheIndex::StartBuildingIndex() - ScheduleBuildUpdateTimer() failed."
-         " Starting build immediately."));
-  }
-
-  nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
-  MOZ_ASSERT(ioThread);
-
-  // We need to dispatch an event even if we are on IO thread since we need to
-  // build the inde with the correct priority.
-  rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("CacheIndex::StartBuildingIndex() - Can't dispatch event");
-    LOG(("CacheIndex::StartBuildingIndex() - Can't dispatch event" ));
-    FinishBuild(false);
-  }
+  return false;
 }
 
 void
 CacheIndex::BuildIndex()
 {
   LOG(("CacheIndex::BuildIndex()"));
 
   AssertOwnsLock();
@@ -2351,44 +2483,45 @@ CacheIndex::BuildIndex()
 
   if (!mDirEnumerator) {
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = SetupDirectoryEnumerator();
     }
     if (mState == SHUTDOWN) {
-      // The index was shut down while we released the lock. FinishBuild() was
+      // The index was shut down while we released the lock. FinishUpdate() was
       // already called from Shutdown(), so just simply return here.
       return;
     }
 
     if (NS_FAILED(rv)) {
-      FinishBuild(false);
+      FinishUpdate(false);
       return;
     }
   }
 
   while (true) {
     if (CacheIOThread::YieldAndRerun()) {
       LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
+      mUpdateEventPending = true;
       return;
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
     }
     if (mState == SHUTDOWN) {
       return;
     }
     if (!file) {
-      FinishBuild(NS_SUCCEEDED(rv));
+      FinishUpdate(NS_SUCCEEDED(rv));
       return;
     }
 
     nsAutoCString leaf;
     rv = file->GetNativeLeafName(leaf);
     if (NS_FAILED(rv)) {
       LOG(("CacheIndex::BuildIndex() - GetNativeLeafName() failed! Skipping "
            "file."));
@@ -2470,105 +2603,77 @@ CacheIndex::BuildIndex()
            leaf.get()));
       entry->Log();
     }
   }
 
   NS_NOTREACHED("We should never get here");
 }
 
-void
-CacheIndex::FinishBuild(bool aSucceeded)
-{
-  LOG(("CacheIndex::FinishBuild() [succeeded=%d]", aSucceeded));
-
-  MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == BUILDING);
-
-  AssertOwnsLock();
-
-  if (mDirEnumerator) {
-    if (NS_IsMainThread()) {
-      LOG(("CacheIndex::FinishBuild() - posting of PreShutdownInternal failed? "
-           "Cannot safely release mDirEnumerator, leaking it!"));
-      NS_WARNING(("CacheIndex::FinishBuild() - Leaking mDirEnumerator!"));
-      // This can happen only in case dispatching event to IO thread failed in
-      // CacheIndex::PreShutdown().
-      mDirEnumerator.forget(); // Leak it since dir enumerator is not threadsafe
-    } else {
-      mDirEnumerator->Close();
-      mDirEnumerator = nullptr;
-    }
-  }
-
-  if (!aSucceeded) {
-    mDontMarkIndexClean = true;
-  }
-
-  if (mState == BUILDING) {
-    // Make sure we won't start update. Index should be up to date, if build
-    // was successful. If the build failed, there is no reason to believe that
-    // the update will succeed.
-    mIndexNeedsUpdate = false;
-
-    ChangeState(READY);
-    mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
-  }
-}
-
 bool
 CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState)
 {
   // Start updating process when we are in or we are switching to READY state
-  // and index needs update, but not during shutdown.
+  // and index needs update, but not during shutdown or when removing all
+  // entries.
   if ((mState == READY || aSwitchingToReadyState) && mIndexNeedsUpdate &&
-      !mShuttingDown) {
+      !mShuttingDown && !mRemovingAll) {
     LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process"));
     mIndexNeedsUpdate = false;
-    StartUpdatingIndex();
+    StartUpdatingIndex(false);
     return true;
   }
 
   return false;
 }
 
 void
-CacheIndex::StartUpdatingIndex()
+CacheIndex::StartUpdatingIndex(bool aRebuild)
 {
-  LOG(("CacheIndex::StartUpdatingIndex()"));
+  LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild));
+
+  AssertOwnsLock();
 
   nsresult rv;
 
   mIndexStats.Log();
 
-  ChangeState(UPDATING);
+  ChangeState(aRebuild ? BUILDING : UPDATING);
   mDontMarkIndexClean = false;
 
-  if (mShuttingDown) {
+  if (mShuttingDown || mRemovingAll) {
     FinishUpdate(false);
     return;
   }
 
+  if (IsUpdatePending()) {
+    LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending"));
+    return;
+  }
+
   uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
   if (elapsed < kUpdateIndexStartDelay) {
-    rv = ScheduleBuildUpdateTimer(kUpdateIndexStartDelay - elapsed);
+    rv = ScheduleUpdateTimer(kUpdateIndexStartDelay - elapsed);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
 
-    LOG(("CacheIndex::StartUpdatingIndex() - ScheduleBuildUpdateTimer() failed."
-         " Starting update immediately."));
+    LOG(("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. "
+         "Starting update immediately."));
   }
 
   nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
   MOZ_ASSERT(ioThread);
 
   // We need to dispatch an event even if we are on IO thread since we need to
   // update the index with the correct priority.
-  rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
+  mUpdateEventPending = true;
+  rv = ioThread->Dispatch(this, CacheIOThread::INDEX);
   if (NS_FAILED(rv)) {
+    mUpdateEventPending = false;
     NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event");
     LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event" ));
     FinishUpdate(false);
   }
 }
 
 void
 CacheIndex::UpdateIndex()
@@ -2583,31 +2688,32 @@ CacheIndex::UpdateIndex()
 
   if (!mDirEnumerator) {
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = SetupDirectoryEnumerator();
     }
     if (mState == SHUTDOWN) {
-      // The index was shut down while we released the lock. FinishBuild() was
+      // The index was shut down while we released the lock. FinishUpdate() was
       // already called from Shutdown(), so just simply return here.
       return;
     }
 
     if (NS_FAILED(rv)) {
       FinishUpdate(false);
       return;
     }
   }
 
   while (true) {
     if (CacheIOThread::YieldAndRerun()) {
       LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level "
            "events."));
+      mUpdateEventPending = true;
       return;
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
       CacheIndexAutoUnlock unlock(this);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
@@ -2742,17 +2848,18 @@ CacheIndex::UpdateIndex()
   NS_NOTREACHED("We should never get here");
 }
 
 void
 CacheIndex::FinishUpdate(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
 
-  MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == UPDATING);
+  MOZ_ASSERT(mState == UPDATING || mState == BUILDING ||
+             (!aSucceeded && mState == SHUTDOWN));
 
   AssertOwnsLock();
 
   if (mDirEnumerator) {
     if (NS_IsMainThread()) {
       LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
            " Cannot safely release mDirEnumerator, leaking it!"));
       NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
@@ -2764,31 +2871,33 @@ CacheIndex::FinishUpdate(bool aSucceeded
       mDirEnumerator = nullptr;
     }
   }
 
   if (!aSucceeded) {
     mDontMarkIndexClean = true;
   }
 
-  if (mState == UPDATING) {
-    if (aSucceeded) {
-      // If we've iterated over all entries successfully then all entries that
-      // really exist on the disk are now marked as fresh. All non-fresh entries
-      // don't exist anymore and must be removed from the index.
-      mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
-    }
-
-    // Make sure we won't start update again. If the update failed, there is no
-    // reason to believe that it will succeed next time.
-    mIndexNeedsUpdate = false;
-
-    ChangeState(READY);
-    mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
+  if (mState == SHUTDOWN) {
+    return;
   }
+
+  if (mState == UPDATING && aSucceeded) {
+    // If we've iterated over all entries successfully then all entries that
+    // really exist on the disk are now marked as fresh. All non-fresh entries
+    // don't exist anymore and must be removed from the index.
+    mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
+  }
+
+  // Make sure we won't start update. If the build or update failed, there is no
+  // reason to believe that it will succeed next time.
+  mIndexNeedsUpdate = false;
+
+  ChangeState(READY);
+  mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
 }
 
 // static
 PLDHashOperator
 CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure)
 {
   if (aEntry->IsFresh()) {
     return PL_DHASH_NEXT;
@@ -2840,18 +2949,19 @@ CacheIndex::ChangeState(EState aNewState
   MOZ_ASSERT(!mShuttingDown || mState != READY || aNewState == SHUTDOWN);
 
   // Start updating process when switching to READY state if needed
   if (aNewState == READY && StartUpdatingIndexIfNeeded(true)) {
     return;
   }
 
   // Try to evict entries over limit everytime we're leaving state READING,
-  // BUILDING or UPDATING, but not during shutdown.
-  if (!mShuttingDown && aNewState != SHUTDOWN &&
+  // BUILDING or UPDATING, but not during shutdown or when removing all
+  // entries.
+  if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN &&
       (mState == READING || mState == BUILDING || mState == UPDATING))  {
     CacheFileIOManager::EvictIfOverLimit();
   }
 
   mState = aNewState;
 }
 
 void
@@ -2971,137 +3081,143 @@ CacheIndex::Run()
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
 
+  mUpdateEventPending = false;
+
   switch (mState) {
     case BUILDING:
       BuildIndex();
       break;
     case UPDATING:
       UpdateIndex();
       break;
     default:
-      MOZ_ASSERT(false, "Unexpected state!");
+      LOG(("CacheIndex::Run() - Update/Build was canceled"));
   }
 
   return NS_OK;
 }
 
 nsresult
-CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
+CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener,
+                                 CacheFileHandle *aHandle, nsresult aResult)
 {
-  LOG(("CacheIndex::OnFileOpened() [handle=%p, result=0x%08x]", aHandle,
-       aResult));
+  LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
+       "result=0x%08x]", aOpener, aHandle, aResult));
 
   nsresult rv;
 
-  CacheIndexAutoLock lock(this);
+  AssertOwnsLock();
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
 
   switch (mState) {
     case WRITING:
+      MOZ_ASSERT(aOpener == mIndexFileOpener);
+      mIndexFileOpener = nullptr;
+
       if (NS_FAILED(aResult)) {
-        LOG(("CacheIndex::OnFileOpened() - Can't open index file for writing "
-             "[rv=0x%08x]", aResult));
+        LOG(("CacheIndex::OnFileOpenedInternal() - Can't open index file for "
+             "writing [rv=0x%08x]", aResult));
         FinishWrite(false);
       } else {
         mIndexHandle = aHandle;
         WriteRecords();
       }
       break;
     case READING:
-      mReadOpenCount--;
-
-      if (mReadFailed) {
+      if (aOpener == mIndexFileOpener) {
+        mIndexFileOpener = nullptr;
+
         if (NS_SUCCEEDED(aResult)) {
-          CacheFileIOManager::DoomFile(aHandle, nullptr);
-        }
-
-        if (mReadOpenCount == 0) {
+          if (aHandle->FileSize() == 0) {
+            FinishRead(false);
+            CacheFileIOManager::DoomFile(aHandle, nullptr);
+            break;
+          } else {
+            mIndexHandle = aHandle;
+          }
+        } else {
           FinishRead(false);
-        }
-
-        return NS_OK;
-      }
-
-      switch (mReadOpenCount) {
-        case 2: // kIndexName
-          if (NS_FAILED(aResult)) {
-            mReadFailed = true;
-          } else {
-            MOZ_ASSERT(aHandle->Key() == kIndexName);
-            if (aHandle->FileSize() == 0) {
-              mReadFailed = true;
-              CacheFileIOManager::DoomFile(aHandle, nullptr);
-            } else {
-              mIndexHandle = aHandle;
-            }
-          }
           break;
-        case 1: // kJournalName
-          if (NS_SUCCEEDED(aResult)) {
-            MOZ_ASSERT(aHandle->Key() == kJournalName);
-            if (aHandle->FileSize() == 0) {
-              CacheFileIOManager::DoomFile(aHandle, nullptr);
-            } else {
-              mJournalHandle = aHandle;
-            }
-          }
+        }
+      } else if (aOpener == mJournalFileOpener) {
+        mJournalFileOpener = nullptr;
+        mJournalHandle = aHandle;
+      } else if (aOpener == mTmpFileOpener) {
+        mTmpFileOpener = nullptr;
+        mTmpHandle = aHandle;
+      } else {
+        MOZ_ASSERT(false, "Unexpected state!");
+      }
+
+      if (mIndexFileOpener || mJournalFileOpener || mTmpFileOpener) {
+        // Some opener still didn't finish
+        break;
+      }
+
+      // We fail and cancel all other openers when we opening index file fails.
+      MOZ_ASSERT(mIndexHandle);
+
+      if (mTmpHandle) {
+        CacheFileIOManager::DoomFile(mTmpHandle, nullptr);
+        mTmpHandle = nullptr;
+
+        if (mJournalHandle) { // this shouldn't normally happen
+          LOG(("CacheIndex::OnFileOpenedInternal() - Unexpected state, all "
+               "files [%s, %s, %s] should never exist. Removing whole index.",
+               kIndexName, kJournalName, kTempIndexName));
+          FinishRead(false);
           break;
-        case 0: // kTempIndexName
-          if (NS_SUCCEEDED(aResult)) {
-            MOZ_ASSERT(aHandle->Key() == kTempIndexName);
-            CacheFileIOManager::DoomFile(aHandle, nullptr);
-
-            if (mJournalHandle) { // this should never happen
-              LOG(("CacheIndex::OnFileOpened() - Unexpected state, all files "
-                   "[%s, %s, %s] should never exist. Removing whole index.",
-                   kIndexName, kJournalName, kTempIndexName));
-              FinishRead(false);
-              break;
-            }
-          }
-
-          if (mJournalHandle) {
-            // Rename journal to make sure we update index on next start in case
-            // firefox crashes
-            rv = CacheFileIOManager::RenameFile(
-              mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this);
-            if (NS_FAILED(rv)) {
-              LOG(("CacheIndex::OnFileOpened() - CacheFileIOManager::RenameFile"
-                   "() failed synchronously [rv=0x%08x]", rv));
-              FinishRead(false);
-              break;
-            }
-            break;
-          }
-
-          StartReadingIndex();
+        }
       }
+
+      if (mJournalHandle) {
+        // Rename journal to make sure we update index on next start in case
+        // firefox crashes
+        rv = CacheFileIOManager::RenameFile(
+          mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this);
+        if (NS_FAILED(rv)) {
+          LOG(("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::"
+               "RenameFile() failed synchronously [rv=0x%08x]", rv));
+          FinishRead(false);
+          break;
+        }
+      } else {
+        StartReadingIndex();
+      }
+
       break;
     default:
       MOZ_ASSERT(false, "Unexpected state!");
   }
 
   return NS_OK;
 }
 
 nsresult
+CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
+{
+  MOZ_CRASH("CacheIndex::OnFileOpened should not be called!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
 CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                           nsresult aResult)
 {
   LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle,
        aResult));
 
   nsresult rv;
 
@@ -3112,16 +3228,22 @@ CacheIndex::OnDataWritten(CacheFileHandl
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
 
   switch (mState) {
     case WRITING:
+      if (mIndexHandle != aHandle) {
+        LOG(("CacheIndex::OnDataWritten() - ignoring notification since it "
+             "belongs to previously canceled operation [state=%d]", mState));
+        break;
+      }
+
       if (NS_FAILED(aResult)) {
         FinishWrite(false);
       } else {
         if (mSkipEntries == mProcessEntries) {
           rv = CacheFileIOManager::RenameFile(mIndexHandle,
                                               NS_LITERAL_CSTRING(kIndexName),
                                               this);
           if (NS_FAILED(rv)) {
@@ -3130,17 +3252,19 @@ CacheIndex::OnDataWritten(CacheFileHandl
             FinishWrite(false);
           }
         } else {
           WriteRecords();
         }
       }
       break;
     default:
-      MOZ_ASSERT(false, "Unexpected state!");
+      // Writing was canceled.
+      LOG(("CacheIndex::OnDataWritten() - ignoring notification since the "
+           "operation was previously canceled [state=%d]", mState));
   }
 
   return NS_OK;
 }
 
 nsresult
 CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
 {
@@ -3148,34 +3272,34 @@ CacheIndex::OnDataRead(CacheFileHandle *
        aResult));
 
   CacheIndexAutoLock lock(this);
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  if (mState == READY && mShuttingDown) {
-    return NS_OK;
-  }
-
   switch (mState) {
     case READING:
+      MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle);
+
       if (NS_FAILED(aResult)) {
         FinishRead(false);
       } else {
         if (!mIndexOnDiskIsValid) {
           ParseRecords();
         } else {
           ParseJournal();
         }
       }
       break;
     default:
-      MOZ_ASSERT(false, "Unexpected state!");
+      // Reading was canceled.
+      LOG(("CacheIndex::OnDataRead() - ignoring notification since the "
+           "operation was previously canceled [state=%d]", mState));
   }
 
   return NS_OK;
 }
 
 nsresult
 CacheIndex::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
 {
@@ -3206,30 +3330,46 @@ CacheIndex::OnFileRenamed(CacheFileHandl
     return NS_OK;
   }
 
   switch (mState) {
     case WRITING:
       // This is a result of renaming the new index written to tmpfile to index
       // file. This is the last step when writing the index and the whole
       // writing process is successful iff renaming was successful.
+
+      if (mIndexHandle != aHandle) {
+        LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
+             "belongs to previously canceled operation [state=%d]", mState));
+        break;
+      }
+
       FinishWrite(NS_SUCCEEDED(aResult));
       break;
     case READING:
       // This is a result of renaming journal file to tmpfile. It is renamed
       // before we start reading index and journal file and it should normally
       // succeed. If it fails give up reading of index.
+
+      if (mJournalHandle != aHandle) {
+        LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
+             "belongs to previously canceled operation [state=%d]", mState));
+        break;
+      }
+
       if (NS_FAILED(aResult)) {
         FinishRead(false);
       } else {
         StartReadingIndex();
       }
       break;
     default:
-      MOZ_ASSERT(false, "Unexpected state!");
+      // Reading/writing was canceled.
+      LOG(("CacheIndex::OnFileRenamed() - ignoring notification since the "
+           "operation was previously canceled [state=%d]", mState));
   }
 
   return NS_OK;
 }
 
 // Memory reporting
 
 namespace { // anon
@@ -3256,17 +3396,17 @@ CacheIndex::SizeOfExcludingThisInternal(
   // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
   // handles array.
 
   sizeOf = do_QueryInterface(mCacheDirectory);
   if (sizeOf) {
     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
   }
 
-  sizeOf = do_QueryInterface(mTimer);
+  sizeOf = do_QueryInterface(mUpdateTimer);
   if (sizeOf) {
     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
   }
 
   n += mallocSizeOf(mRWBuf);
   n += mallocSizeOf(mRWHash);
 
   n += mIndex.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -25,16 +25,17 @@ class nsITimer;
 #ifdef DEBUG
 #define DEBUG_STATS 1
 #endif
 
 namespace mozilla {
 namespace net {
 
 class CacheFileMetadata;
+class FileOpenHelper;
 
 typedef struct {
   // Version of the index. The index must be ignored and deleted when the file
   // on disk was written with a newer version.
   uint32_t mVersion;
 
   // Timestamp of time when the last successful write of the index started.
   // During update process we use this timestamp for a quick validation of entry
@@ -322,20 +323,32 @@ public:
 #ifdef DEBUG
   void DisableLogging() {
     mDisableLogging = true;
   }
 #endif
 
   void Log() {
     LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
-         "dirty=%u, fresh=%u, empty=%u, size=%lld]", mCount, mNotInitialized,
+         "dirty=%u, fresh=%u, empty=%u, size=%u]", mCount, mNotInitialized,
          mRemoved, mDirty, mFresh, mEmpty, mSize));
   }
 
+  void Clear() {
+    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Clear() - state logged!");
+
+    mCount = 0;
+    mNotInitialized = 0;
+    mRemoved = 0;
+    mDirty = 0;
+    mFresh = 0;
+    mEmpty = 0;
+    mSize = 0;
+  }
+
 #ifdef DEBUG
   bool StateLogged() {
     return mStateLogged;
   }
 #endif
 
   uint32_t Count() {
     MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Count() - state logged!");
@@ -507,16 +520,19 @@ public:
   // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
   // InitEntry() must precede the call to this method.
   // Pass nullptr if the value didn't change.
   static nsresult UpdateEntry(const SHA1Sum::Hash *aHash,
                               const uint32_t      *aFrecency,
                               const uint32_t      *aExpirationTime,
                               const uint32_t      *aSize);
 
+  // Remove all entries from the index. Called when clearing the whole cache.
+  static nsresult RemoveAll();
+
   enum EntryStatus {
     EXISTS         = 0,
     DOES_NOT_EXIST = 1,
     DO_NOT_KNOW    = 2
   };
 
   // Returns status of the entry in index for the given key. It can be called
   // on any thread.
@@ -533,20 +549,23 @@ public:
   // Memory reporting
   static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
   static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 private:
   friend class CacheIndexEntryAutoManage;
   friend class CacheIndexAutoLock;
   friend class CacheIndexAutoUnlock;
+  friend class FileOpenHelper;
 
   virtual ~CacheIndex();
 
   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
+  nsresult   OnFileOpenedInternal(FileOpenHelper *aOpener,
+                                  CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                            nsresult aResult);
   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult);
 
   void     Lock();
@@ -677,39 +696,38 @@ private:
   // Finalizes reading process.
   void FinishRead(bool aSucceeded);
 
   static PLDHashOperator ProcessJournalEntry(CacheIndexEntry *aEntry,
                                              void* aClosure);
 
   // Following methods perform updating and building of the index.
   // Timer callback that starts update or build process.
-  static void DelayedBuildUpdate(nsITimer *aTimer, void *aClosure);
+  static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
   // Posts timer event that start update or build process.
-  nsresult ScheduleBuildUpdateTimer(uint32_t aDelay);
+  nsresult ScheduleUpdateTimer(uint32_t aDelay);
   nsresult SetupDirectoryEnumerator();
   void InitEntryFromDiskData(CacheIndexEntry *aEntry,
                              CacheFileMetadata *aMetaData,
                              int64_t aFileSize);
-  // Starts build process or fires a timer when it is too early after startup.
-  void StartBuildingIndex();
+  // Returns true when either a timer is scheduled or event is posted.
+  bool IsUpdatePending();
   // Iterates through all files in entries directory that we didn't create/open
   // during this session, parses them and adds the entries to the index.
   void BuildIndex();
-  // Finalizes build process.
-  void FinishBuild(bool aSucceeded);
 
   bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
-  // Starts update process or fires a timer when it is too early after startup.
-  void StartUpdatingIndex();
+  // Starts update or build process or fires a timer when it is too early after
+  // startup.
+  void StartUpdatingIndex(bool aRebuild);
   // Iterates through all files in entries directory that we didn't create/open
   // during this session and theirs last modified time is newer than timestamp
   // in the index header. Parses the files and adds the entries to the index.
   void UpdateIndex();
-  // Finalizes update process.
+  // Finalizes update or build process.
   void FinishUpdate(bool aSucceeded);
 
   static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
                                                void* aClosure);
 
   enum EState {
     // Initial state in which the index is not usable
     // Possible transitions:
@@ -792,60 +810,68 @@ private:
   bool           mShuttingDown;
   // When set to true, update process should start as soon as possible. This
   // flag is set whenever we find some inconsistency which would be fixed by
   // update process. The flag is checked always when switching to READY state.
   // To make sure we start the update process as soon as possible, methods that
   // set this flag should also call StartUpdatingIndexIfNeeded() to cover the
   // case when we are currently in READY state.
   bool           mIndexNeedsUpdate;
+  // Set at the beginning of RemoveAll() which clears the whole index. When
+  // removing all entries we must stop any pending reading, writing, updating or
+  // building operation. This flag is checked at various places and it prevents
+  // we won't start another operation (e.g. canceling reading of the index would
+  // normally start update or build process)
+  bool           mRemovingAll;
   // Whether the index file on disk exists and is valid.
   bool           mIndexOnDiskIsValid;
   // When something goes wrong during updating or building process, we don't
   // mark index clean (and also don't write journal) to ensure that update or
   // build will be initiated on the next start.
   bool           mDontMarkIndexClean;
   // Timestamp value from index file. It is used during update process to skip
   // entries that were last modified before this timestamp.
   uint32_t       mIndexTimeStamp;
   // Timestamp of last time the index was dumped to disk.
   // NOTE: The index might not be necessarily dumped at this time. The value
   // is used to schedule next dump of the index.
   TimeStamp      mLastDumpTime;
 
   // Timer of delayed update/build.
-  nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsITimer> mUpdateTimer;
+  // True when build or update event is posted
+  bool               mUpdateEventPending;
 
   // Helper members used when reading/writing index from/to disk.
   // Contains number of entries that should be skipped:
   //  - in hashtable when writing index because they were already written
   //  - in index file when reading index because they were already read
   uint32_t                  mSkipEntries;
   // Number of entries that should be written to disk. This is number of entries
   // in hashtable that are initialized and are not marked as removed when writing
   // begins.
   uint32_t                  mProcessEntries;
   char                     *mRWBuf;
   uint32_t                  mRWBufSize;
   uint32_t                  mRWBufPos;
   nsRefPtr<CacheHash>       mRWHash;
 
-  // When reading index from disk, we open index, journal and tmpindex files at
-  // the same time. This value tell us how many times CacheIndex::OnFileOpened()
-  // will be called and identifies the handle.
-  uint32_t                  mReadOpenCount;
-  // Reading of index failed completely if true.
-  bool                      mReadFailed;
   // Reading of journal succeeded if true.
   bool                      mJournalReadSuccessfully;
 
   // Handle used for writing and reading index file.
   nsRefPtr<CacheFileHandle> mIndexHandle;
   // Handle used for reading journal file.
   nsRefPtr<CacheFileHandle> mJournalHandle;
+  // Used to check the existence of the file during reading process.
+  nsRefPtr<CacheFileHandle> mTmpHandle;
+
+  nsRefPtr<FileOpenHelper>  mIndexFileOpener;
+  nsRefPtr<FileOpenHelper>  mJournalFileOpener;
+  nsRefPtr<FileOpenHelper>  mTmpFileOpener;
 
   // Directory enumerator used when building and updating index.
   nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
 
   // Main index hashtable.
   nsTHashtable<CacheIndexEntry> mIndex;
 
   // We cannot add, remove or change any entry in mIndex in states READING and
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -484,233 +484,16 @@ NS_IMETHODIMP CacheStorageService::AppCa
   else {
     storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
   }
 
   storage.forget(_retval);
   return NS_OK;
 }
 
-namespace { // anon
-
-class CacheFilesDeletor : public nsRunnable
-                        , public CacheEntriesEnumeratorCallback
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  CacheFilesDeletor(nsICacheEntryDoomCallback* aCallback);
-  ~CacheFilesDeletor();
-
-  nsresult DeleteAll();
-  nsresult DeleteDoomed();
-
-private:
-  nsresult Init(CacheFileIOManager::EEnumerateMode aMode);
-  NS_IMETHOD Run();
-  NS_IMETHOD Execute();
-  void Callback();
-  virtual void OnFile(CacheFile* aFile);
-
-  nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
-  nsAutoPtr<CacheEntriesEnumerator> mEnumerator;
-  nsRefPtr<CacheIOThread> mIOThread;
-
-  uint32_t mRunning;
-  enum {
-    ALL,
-    DOOMED
-  } mMode;
-  nsresult mRv;
-};
-
-NS_IMPL_ISUPPORTS_INHERITED0(CacheFilesDeletor, nsRunnable);
-
-CacheFilesDeletor::CacheFilesDeletor(nsICacheEntryDoomCallback* aCallback)
-: mCallback(aCallback)
-, mRunning(0)
-, mRv(NS_OK)
-{
-  MOZ_COUNT_CTOR(CacheFilesDeletor);
-  MOZ_EVENT_TRACER_WAIT(static_cast<nsRunnable*>(this), "net::cache::deletor");
-}
-
-CacheFilesDeletor::~CacheFilesDeletor()
-{
-  MOZ_COUNT_DTOR(CacheFilesDeletor);
-  MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor");
-
-  if (mMode == ALL) {
-    // Now delete the doomed entries if some left.
-    nsRefPtr<CacheFilesDeletor> deletor = new CacheFilesDeletor(mCallback);
-
-    nsRefPtr<nsRunnableMethod<CacheFilesDeletor, nsresult> > event =
-      NS_NewRunnableMethod(deletor.get(), &CacheFilesDeletor::DeleteDoomed);
-    NS_DispatchToMainThread(event);
-  }
-}
-
-nsresult CacheFilesDeletor::DeleteAll()
-{
-  mMode = ALL;
-  return Init(CacheFileIOManager::ENTRIES);
-}
-
-nsresult CacheFilesDeletor::DeleteDoomed()
-{
-  mMode = DOOMED;
-  return Init(CacheFileIOManager::DOOMED);
-}
-
-nsresult CacheFilesDeletor::Init(CacheFileIOManager::EEnumerateMode aMode)
-{
-  nsresult rv;
-
-  rv = CacheFileIOManager::EnumerateEntryFiles(
-    aMode, getter_Transfers(mEnumerator));
-
-  if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
-    rv = NS_OK;
-  }
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mIOThread = CacheFileIOManager::IOThread();
-  NS_ENSURE_TRUE(mIOThread, NS_ERROR_NOT_INITIALIZED);
-
-  rv = mIOThread->Dispatch(this, CacheIOThread::EVICT);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-void CacheFilesDeletor::Callback()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
-  if (obsSvc) {
-    obsSvc->NotifyObservers(CacheStorageService::SelfISupports(),
-                            "cacheservice:empty-cache",
-                            nullptr);
-  }
-
-  if (!mCallback)
-    return;
-
-  nsCOMPtr<nsICacheEntryDoomCallback> callback;
-  callback.swap(mCallback);
-  callback->OnCacheEntryDoomed(mRv);
-}
-
-NS_IMETHODIMP CacheFilesDeletor::Run()
-{
-  if (!mRunning) {
-    MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor");
-  }
-
-  MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor::exec");
-
-  nsresult rv = Execute();
-  if (NS_SUCCEEDED(mRv))
-    mRv = rv;
-
-  if (!mEnumerator || !mEnumerator->HasMore()) {
-    // No enumerator or no more elements means the job is done.
-    mEnumerator = nullptr;
-
-    if (mMode != ALL) {
-      nsRefPtr<nsRunnableMethod<CacheFilesDeletor> > event =
-        NS_NewRunnableMethod(this, &CacheFilesDeletor::Callback);
-      NS_DispatchToMainThread(event);
-    }
-  }
-
-  MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor::exec");
-
-  return NS_OK;
-}
-
-nsresult CacheFilesDeletor::Execute()
-{
-  LOG(("CacheFilesDeletor::Execute [this=%p]", this));
-
-  if (!mEnumerator) {
-    // No enumerator means the job is done.
-    return NS_OK;
-  }
-
-  nsresult rv;
-
-  switch (mMode) {
-  case ALL:
-  case DOOMED:
-    // Simply delete all files, don't doom then though the backend
-
-    while (mEnumerator->HasMore()) {
-      nsCOMPtr<nsIFile> file;
-      rv = mEnumerator->GetNextFile(getter_AddRefs(file));
-      if (NS_FAILED(rv))
-        return rv;
-
-#ifdef PR_LOG
-      nsAutoCString key;
-      file->GetNativeLeafName(key);
-      LOG(("  deleting file with key=%s", key.get()));
-#endif
-
-      rv = file->Remove(false);
-      if (NS_FAILED(rv)) {
-        LOG(("  could not remove the file, probably doomed, rv=0x%08x", rv));
-      }
-
-      ++mRunning;
-
-      if (CacheIOThread::YieldAndRerun()) {
-        LOG(("  deleted %u files, breaking loop for higher level events."));
-        return NS_OK;
-      }
-    }
-
-    break;
-
-  default:
-    MOZ_ASSERT(false);
-  }
-
-  return NS_OK;
-}
-
-void CacheFilesDeletor::OnFile(CacheFile* aFile)
-{
-  LOG(("CacheFilesDeletor::OnFile [this=%p, file=%p]", this, aFile));
-
-  if (!aFile)
-    return;
-
-  MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor::file");
-
-#ifdef PR_LOG
-  nsAutoCString key;
-  aFile->Key(key);
-#endif
-
-  switch (mMode) {
-  case ALL:
-  case DOOMED:
-    LOG(("  dooming file with key=%s", key.get()));
-    // Uncompromisely delete the file!
-    aFile->Doom(nullptr);
-    break;
-  }
-
-  MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor::file");
-}
-
-} // anon
-
 NS_IMETHODIMP CacheStorageService::Clear()
 {
   nsresult rv;
 
   if (CacheObserver::UseNewCache()) {
     {
       mozilla::MutexAutoLock lock(mLock);
 
@@ -718,22 +501,19 @@ NS_IMETHODIMP CacheStorageService::Clear
 
       nsTArray<nsCString> keys;
       sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys);
 
       for (uint32_t i = 0; i < keys.Length(); ++i)
         DoomStorageEntries(keys[i], true, nullptr);
     }
 
-    // TODO - Callback can be provided!
-    nsRefPtr<CacheFilesDeletor> deletor = new CacheFilesDeletor(nullptr);
-    rv = deletor->DeleteAll();
+    rv = CacheFileIOManager::EvictAll();
     NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
+  } else {
     nsCOMPtr<nsICacheService> serv =
         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = serv->EvictEntries(nsICache::STORE_ANYWHERE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
--- a/netwerk/cache2/moz.build
+++ b/netwerk/cache2/moz.build
@@ -16,17 +16,16 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'necko_cache2'
 
 EXPORTS += [
     'CacheObserver.h',
     'CacheStorageService.h',
 ]
 
 UNIFIED_SOURCES += [
-    'CacheEntriesEnumerator.cpp',
     'CacheHashUtils.cpp',
     'CacheIOThread.cpp',
     'CacheObserver.cpp',
 ]
 
 # AppCacheStorage.cpp cannot be built in unified mode because it uses plarena.h.
 # The rest of these files cannot be built in unified mode because they rely oni
 # including prlog.h after nsCache.h.