netwerk/cache2/CacheFileContextEvictor.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 "CacheFileContextEvictor.h"
     7 #include "CacheFileIOManager.h"
     8 #include "CacheIndex.h"
     9 #include "CacheIndexIterator.h"
    10 #include "CacheFileUtils.h"
    11 #include "nsIFile.h"
    12 #include "LoadContextInfo.h"
    13 #include "nsThreadUtils.h"
    14 #include "nsString.h"
    15 #include "nsISimpleEnumerator.h"
    16 #include "nsIDirectoryEnumerator.h"
    17 #include "mozilla/Base64.h"
    20 namespace mozilla {
    21 namespace net {
    23 const char kContextEvictionPrefix[] = "ce_";
    24 const uint32_t kContextEvictionPrefixLength =
    25   sizeof(kContextEvictionPrefix) - 1;
    27 bool CacheFileContextEvictor::sDiskAlreadySearched = false;
    29 CacheFileContextEvictor::CacheFileContextEvictor()
    30   : mEvicting(false)
    31   , mIndexIsUpToDate(false)
    32 {
    33   LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this));
    34 }
    36 CacheFileContextEvictor::~CacheFileContextEvictor()
    37 {
    38   LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this));
    39 }
    41 nsresult
    42 CacheFileContextEvictor::Init(nsIFile *aCacheDirectory)
    43 {
    44   LOG(("CacheFileContextEvictor::Init()"));
    46   nsresult rv;
    48   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    50   CacheIndex::IsUpToDate(&mIndexIsUpToDate);
    52   mCacheDirectory = aCacheDirectory;
    54   rv = aCacheDirectory->Clone(getter_AddRefs(mEntriesDir));
    55   if (NS_WARN_IF(NS_FAILED(rv))) {
    56     return rv;
    57   }
    59   rv = mEntriesDir->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
    60   if (NS_WARN_IF(NS_FAILED(rv))) {
    61     return rv;
    62   }
    64   if (!sDiskAlreadySearched) {
    65     LoadEvictInfoFromDisk();
    66     if ((mEntries.Length() != 0) && mIndexIsUpToDate) {
    67       CreateIterators();
    68       StartEvicting();
    69     }
    70   }
    72   return NS_OK;
    73 }
    75 uint32_t
    76 CacheFileContextEvictor::ContextsCount()
    77 {
    78   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    80   return mEntries.Length();
    81 }
    83 nsresult
    84 CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo)
    85 {
    86   LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p]",
    87        this, aLoadContextInfo));
    89   nsresult rv;
    91   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    93   CacheFileContextEvictorEntry *entry = nullptr;
    94   for (uint32_t i = 0; i < mEntries.Length(); ++i) {
    95     if (mEntries[i]->mInfo->Equals(aLoadContextInfo)) {
    96       entry = mEntries[i];
    97       break;
    98     }
    99   }
   101   if (!entry) {
   102     entry = new CacheFileContextEvictorEntry();
   103     entry->mInfo = aLoadContextInfo;
   104     mEntries.AppendElement(entry);
   105   }
   107   entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
   109   PersistEvictionInfoToDisk(aLoadContextInfo);
   111   if (mIndexIsUpToDate) {
   112     // Already existing context could be added again, in this case the iterator
   113     // would be recreated. Close the old iterator explicitely.
   114     if (entry->mIterator) {
   115       entry->mIterator->Close();
   116       entry->mIterator = nullptr;
   117     }
   119     rv = CacheIndex::GetIterator(aLoadContextInfo, false,
   120                                  getter_AddRefs(entry->mIterator));
   121     if (NS_FAILED(rv)) {
   122       // This could probably happen during shutdown. Remove the entry from
   123       // the array, but leave the info on the disk. No entry can be opened
   124       // during shutdown and we'll load the eviction info on next start.
   125       LOG(("CacheFileContextEvictor::AddContext() - Cannot get an iterator. "
   126            "[rv=0x%08x]", rv));
   127       mEntries.RemoveElement(entry);
   128       return rv;
   129     }
   131     StartEvicting();
   132   }
   134   return NS_OK;
   135 }
   137 nsresult
   138 CacheFileContextEvictor::CacheIndexStateChanged()
   139 {
   140   LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this));
   142   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   144   bool isUpToDate = false;
   145   CacheIndex::IsUpToDate(&isUpToDate);
   146   if (mEntries.Length() == 0) {
   147     // Just save the state and exit, since there is nothing to do
   148     mIndexIsUpToDate = isUpToDate;
   149     return NS_OK;
   150   }
   152   if (!isUpToDate && !mIndexIsUpToDate) {
   153     // Index is outdated and status has not changed, nothing to do.
   154     return NS_OK;
   155   }
   157   if (isUpToDate && mIndexIsUpToDate) {
   158     // Status has not changed, but make sure the eviction is running.
   159     if (mEvicting) {
   160       return NS_OK;
   161     }
   163     // We're not evicting, but we should be evicting?!
   164     LOG(("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to "
   165          "date, we have some context to evict but eviction is not running! "
   166          "Starting now."));
   167   }
   169   mIndexIsUpToDate = isUpToDate;
   171   if (mIndexIsUpToDate) {
   172     CreateIterators();
   173     StartEvicting();
   174   } else {
   175     CloseIterators();
   176   }
   178   return NS_OK;
   179 }
   181 nsresult
   182 CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile,
   183                                     bool *_retval)
   184 {
   185   LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
   186        PromiseFlatCString(aKey).get()));
   188   nsresult rv;
   190   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   192   nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
   193   MOZ_ASSERT(info);
   194   if (!info) {
   195     LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
   196     *_retval = false;
   197     return NS_OK;
   198   }
   200   CacheFileContextEvictorEntry *entry = nullptr;
   201   for (uint32_t i = 0; i < mEntries.Length(); ++i) {
   202     if (info->Equals(mEntries[i]->mInfo)) {
   203       entry = mEntries[i];
   204       break;
   205     }
   206   }
   208   if (!entry) {
   209     LOG(("CacheFileContextEvictor::WasEvicted() - Didn't find equal context, "
   210          "returning false."));
   211     *_retval = false;
   212     return NS_OK;
   213   }
   215   PRTime lastModifiedTime;
   216   rv = aFile->GetLastModifiedTime(&lastModifiedTime);
   217   if (NS_FAILED(rv)) {
   218     LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
   219          ", returning false."));
   220     *_retval = false;
   221     return NS_OK;
   222   }
   224   *_retval = !(lastModifiedTime > entry->mTimeStamp);
   225   LOG(("CacheFileContextEvictor::WasEvicted() - returning %s. [mTimeStamp=%lld,"
   226        " lastModifiedTime=%lld]", *_retval ? "true" : "false",
   227        mEntries[0]->mTimeStamp, lastModifiedTime));
   229   return NS_OK;
   230 }
   232 nsresult
   233 CacheFileContextEvictor::PersistEvictionInfoToDisk(
   234   nsILoadContextInfo *aLoadContextInfo)
   235 {
   236   LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
   237        "loadContextInfo=%p]", this, aLoadContextInfo));
   239   nsresult rv;
   241   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   243   nsCOMPtr<nsIFile> file;
   244   rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
   245   if (NS_WARN_IF(NS_FAILED(rv))) {
   246     return rv;
   247   }
   249 #ifdef PR_LOGGING
   250   nsAutoCString path;
   251   file->GetNativePath(path);
   252 #endif
   254   PRFileDesc *fd;
   255   rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600,
   256                               &fd);
   257   if (NS_WARN_IF(NS_FAILED(rv))) {
   258     LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file "
   259          "failed! [path=%s, rv=0x%08x]", path.get(), rv));
   260     return rv;
   261   }
   263   PR_Close(fd);
   265   LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully "
   266        "created file. [path=%s]", path.get()));
   268   return NS_OK;
   269 }
   271 nsresult
   272 CacheFileContextEvictor::RemoveEvictInfoFromDisk(
   273   nsILoadContextInfo *aLoadContextInfo)
   274 {
   275   LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
   276        "loadContextInfo=%p]", this, aLoadContextInfo));
   278   nsresult rv;
   280   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   282   nsCOMPtr<nsIFile> file;
   283   rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
   284   if (NS_WARN_IF(NS_FAILED(rv))) {
   285     return rv;
   286   }
   288 #ifdef PR_LOGGING
   289   nsAutoCString path;
   290   file->GetNativePath(path);
   291 #endif
   293   rv = file->Remove(false);
   294   if (NS_WARN_IF(NS_FAILED(rv))) {
   295     LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file"
   296          " failed! [path=%s, rv=0x%08x]", path.get(), rv));
   297     return rv;
   298   }
   300   LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully "
   301        "removed file. [path=%s]", path.get()));
   303   return NS_OK;
   304 }
   306 nsresult
   307 CacheFileContextEvictor::LoadEvictInfoFromDisk()
   308 {
   309   LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this));
   311   nsresult rv;
   313   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   315   sDiskAlreadySearched = true;
   317   nsCOMPtr<nsISimpleEnumerator> enumerator;
   318   rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(enumerator));
   319   if (NS_WARN_IF(NS_FAILED(rv))) {
   320     return rv;
   321   }
   323   nsCOMPtr<nsIDirectoryEnumerator> dirEnum = do_QueryInterface(enumerator, &rv);
   324   if (NS_WARN_IF(NS_FAILED(rv))) {
   325     return rv;
   326   }
   328   while (true) {
   329     nsCOMPtr<nsIFile> file;
   330     rv = dirEnum->GetNextFile(getter_AddRefs(file));
   331     if (!file) {
   332       break;
   333     }
   335     bool isDir = false;
   336     file->IsDirectory(&isDir);
   337     if (isDir) {
   338       continue;
   339     }
   341     nsAutoCString leaf;
   342     rv = file->GetNativeLeafName(leaf);
   343     if (NS_FAILED(rv)) {
   344       LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - "
   345            "GetNativeLeafName() failed! Skipping file."));
   346       continue;
   347     }
   349     if (leaf.Length() < kContextEvictionPrefixLength) {
   350       continue;
   351     }
   353     if (!StringBeginsWith(leaf, NS_LITERAL_CSTRING(kContextEvictionPrefix))) {
   354       continue;
   355     }
   357     nsAutoCString encoded;
   358     encoded = Substring(leaf, kContextEvictionPrefixLength);
   359     encoded.ReplaceChar('-', '/');
   361     nsAutoCString decoded;
   362     rv = Base64Decode(encoded, decoded);
   363     if (NS_FAILED(rv)) {
   364       LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding "
   365            "failed. Removing the file. [file=%s]", leaf.get()));
   366       file->Remove(false);
   367       continue;
   368     }
   370     nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(decoded);
   372     if (!info) {
   373       LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
   374            "context key, removing file. [contextKey=%s, file=%s]",
   375            decoded.get(), leaf.get()));
   376       file->Remove(false);
   377       continue;
   378     }
   380     PRTime lastModifiedTime;
   381     rv = file->GetLastModifiedTime(&lastModifiedTime);
   382     if (NS_FAILED(rv)) {
   383       continue;
   384     }
   386     CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry();
   387     entry->mInfo = info;
   388     entry->mTimeStamp = lastModifiedTime;
   389     mEntries.AppendElement(entry);
   390   }
   392   return NS_OK;
   393 }
   395 nsresult
   396 CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
   397                                         nsIFile **_retval)
   398 {
   399   nsresult rv;
   401   nsAutoCString leafName;
   402   leafName.Assign(NS_LITERAL_CSTRING(kContextEvictionPrefix));
   404   nsAutoCString keyPrefix;
   405   CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
   407   // TODO: This hack is needed because current CacheFileUtils::ParseKey() can
   408   // parse only the whole key and not just the key prefix generated by
   409   // CacheFileUtils::CreateKeyPrefix(). This should be removed once bug #968593
   410   // is fixed.
   411   keyPrefix.Append(":foo");
   413   nsAutoCString data64;
   414   rv = Base64Encode(keyPrefix, data64);
   415   if (NS_WARN_IF(NS_FAILED(rv))) {
   416     return rv;
   417   }
   419   // Replace '/' with '-' since '/' cannot be part of the filename.
   420   data64.ReplaceChar('/', '-');
   422   leafName.Append(data64);
   424   nsCOMPtr<nsIFile> file;
   425   rv = mCacheDirectory->Clone(getter_AddRefs(file));
   426   if (NS_WARN_IF(NS_FAILED(rv))) {
   427     return rv;
   428   }
   430   rv = file->AppendNative(leafName);
   431   if (NS_WARN_IF(NS_FAILED(rv))) {
   432     return rv;
   433   }
   435   file.swap(*_retval);
   436   return NS_OK;
   437 }
   439 void
   440 CacheFileContextEvictor::CreateIterators()
   441 {
   442   LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this));
   444   CloseIterators();
   446   nsresult rv;
   448   for (uint32_t i = 0; i < mEntries.Length(); ) {
   449     rv = CacheIndex::GetIterator(mEntries[i]->mInfo, false,
   450                                  getter_AddRefs(mEntries[i]->mIterator));
   451     if (NS_FAILED(rv)) {
   452       LOG(("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator"
   453            ". [rv=0x%08x]", rv));
   454       mEntries.RemoveElementAt(i);
   455       continue;
   456     }
   458     ++i;
   459   }
   460 }
   462 void
   463 CacheFileContextEvictor::CloseIterators()
   464 {
   465   LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this));
   467   for (uint32_t i = 0; i < mEntries.Length(); ++i) {
   468     if (mEntries[i]->mIterator) {
   469       mEntries[i]->mIterator->Close();
   470       mEntries[i]->mIterator = nullptr;
   471     }
   472   }
   473 }
   475 void
   476 CacheFileContextEvictor::StartEvicting()
   477 {
   478   LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this));
   480   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   482   if (mEvicting) {
   483     LOG(("CacheFileContextEvictor::StartEvicting() - already evicintg."));
   484     return;
   485   }
   487   if (mEntries.Length() == 0) {
   488     LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict."));
   489     return;
   490   }
   492   nsCOMPtr<nsIRunnable> ev;
   493   ev = NS_NewRunnableMethod(this, &CacheFileContextEvictor::EvictEntries);
   495   nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
   497   nsresult rv = ioThread->Dispatch(ev, CacheIOThread::EVICT);
   498   if (NS_FAILED(rv)) {
   499     LOG(("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to "
   500          "IO thread. [rv=0x%08x]", rv));
   501   }
   503   mEvicting = true;
   504 }
   506 nsresult
   507 CacheFileContextEvictor::EvictEntries()
   508 {
   509   LOG(("CacheFileContextEvictor::EvictEntries()"));
   511   nsresult rv;
   513   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
   515   mEvicting = false;
   517   if (!mIndexIsUpToDate) {
   518     LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
   519          "outdated index."));
   520     return NS_OK;
   521   }
   523   while (true) {
   524     if (CacheIOThread::YieldAndRerun()) {
   525       LOG(("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher "
   526            "level events."));
   527       mEvicting = true;
   528       return NS_OK;
   529     }
   531     if (mEntries.Length() == 0) {
   532       LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there "
   533            "is no context to evict."));
   534       return NS_OK;
   535     }
   537     SHA1Sum::Hash hash;
   538     rv = mEntries[0]->mIterator->GetNextHash(&hash);
   539     if (rv == NS_ERROR_NOT_AVAILABLE) {
   540       LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
   541            "iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
   542            mEntries[0]->mInfo.get()));
   543       RemoveEvictInfoFromDisk(mEntries[0]->mInfo);
   544       mEntries.RemoveElementAt(0);
   545       continue;
   546     } else if (NS_FAILED(rv)) {
   547       LOG(("CacheFileContextEvictor::EvictEntries() - Iterator failed to "
   548            "provide next hash (shutdown?), keeping eviction info on disk."
   549            " [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
   550            mEntries[0]->mInfo.get()));
   551       mEntries.RemoveElementAt(0);
   552       continue;
   553     }
   555     LOG(("CacheFileContextEvictor::EvictEntries() - Processing hash. "
   556          "[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]", LOGSHA1(&hash),
   557          mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
   559     nsRefPtr<CacheFileHandle> handle;
   560     CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
   561                                                       getter_AddRefs(handle));
   562     if (handle) {
   563       // We doom any active handle in CacheFileIOManager::EvictByContext(), so
   564       // this must be a new one. Skip it.
   565       LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
   566            "found an active handle. [handle=%p]", handle.get()));
   567       continue;
   568     }
   570     nsAutoCString leafName;
   571     CacheFileIOManager::HashToStr(&hash, leafName);
   573     PRTime lastModifiedTime;
   574     nsCOMPtr<nsIFile> file;
   575     rv = mEntriesDir->Clone(getter_AddRefs(file));
   576     if (NS_SUCCEEDED(rv)) {
   577       rv = file->AppendNative(leafName);
   578     }
   579     if (NS_SUCCEEDED(rv)) {
   580       rv = file->GetLastModifiedTime(&lastModifiedTime);
   581     }
   582     if (NS_FAILED(rv)) {
   583       LOG(("CacheFileContextEvictor::EvictEntries() - Cannot get last modified "
   584            "time, skipping entry."));
   585       continue;
   586     }
   588     if (lastModifiedTime > mEntries[0]->mTimeStamp) {
   589       LOG(("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. "
   590            "[mTimeStamp=%lld, lastModifiedTime=%lld]", mEntries[0]->mTimeStamp,
   591            lastModifiedTime));
   592       continue;
   593     }
   595     LOG(("CacheFileContextEvictor::EvictEntries - Removing entry."));
   596     file->Remove(false);
   597     CacheIndex::RemoveEntry(&hash);
   598   }
   600   NS_NOTREACHED("We should never get here");
   601   return NS_OK;
   602 }
   604 } // net
   605 } // mozilla

mercurial