netwerk/cache2/CacheStorageService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 "CacheStorageService.h"
     7 #include "CacheFileIOManager.h"
     8 #include "CacheObserver.h"
     9 #include "CacheIndex.h"
    11 #include "nsICacheStorageVisitor.h"
    12 #include "nsIObserverService.h"
    13 #include "CacheStorage.h"
    14 #include "AppCacheStorage.h"
    15 #include "CacheEntry.h"
    16 #include "CacheFileUtils.h"
    18 #include "OldWrappers.h"
    19 #include "nsCacheService.h"
    20 #include "nsDeleteDir.h"
    22 #include "nsIFile.h"
    23 #include "nsIURI.h"
    24 #include "nsCOMPtr.h"
    25 #include "nsAutoPtr.h"
    26 #include "nsNetCID.h"
    27 #include "nsServiceManagerUtils.h"
    28 #include "mozilla/TimeStamp.h"
    29 #include "mozilla/DebugOnly.h"
    30 #include "mozilla/VisualEventTracer.h"
    31 #include "mozilla/Services.h"
    33 namespace mozilla {
    34 namespace net {
    36 namespace {
    38 void AppendMemoryStorageID(nsAutoCString &key)
    39 {
    40   key.Append('/');
    41   key.Append('M');
    42 }
    44 }
    46 // Not defining as static or class member of CacheStorageService since
    47 // it would otherwise need to include CacheEntry.h and that then would
    48 // need to be exported to make nsNetModule.cpp compilable.
    49 typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable>
    50         GlobalEntryTables;
    52 /**
    53  * Keeps tables of entries.  There is one entries table for each distinct load
    54  * context type.  The distinction is based on following load context info states:
    55  * <isPrivate|isAnon|appId|inBrowser> which builds a mapping key.
    56  *
    57  * Thread-safe to access, protected by the service mutex.
    58  */
    59 static GlobalEntryTables* sGlobalEntryTables;
    61 CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags)
    62 : mReportedMemoryConsumption(0)
    63 , mFlags(aFlags)
    64 {
    65 }
    67 void
    68 CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
    69 {
    70   if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) {
    71     CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
    72   }
    73 }
    75 CacheStorageService::MemoryPool::MemoryPool(EType aType)
    76 : mType(aType)
    77 , mMemorySize(0)
    78 {
    79 }
    81 CacheStorageService::MemoryPool::~MemoryPool()
    82 {
    83   if (mMemorySize != 0) {
    84     NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?");
    85   }
    86 }
    88 uint32_t const
    89 CacheStorageService::MemoryPool::Limit() const
    90 {
    91   switch (mType) {
    92   case DISK:
    93     return CacheObserver::MetadataMemoryLimit();
    94   case MEMORY:
    95     return CacheObserver::MemoryCacheCapacity();
    96   }
    98   MOZ_CRASH("Bad pool type");
    99   return 0;
   100 }
   102 NS_IMPL_ISUPPORTS(CacheStorageService,
   103                   nsICacheStorageService,
   104                   nsIMemoryReporter,
   105                   nsITimerCallback)
   107 CacheStorageService* CacheStorageService::sSelf = nullptr;
   109 CacheStorageService::CacheStorageService()
   110 : mLock("CacheStorageService")
   111 , mShutdown(false)
   112 , mDiskPool(MemoryPool::DISK)
   113 , mMemoryPool(MemoryPool::MEMORY)
   114 {
   115   CacheFileIOManager::Init();
   117   MOZ_ASSERT(!sSelf);
   119   sSelf = this;
   120   sGlobalEntryTables = new GlobalEntryTables();
   122   RegisterStrongMemoryReporter(this);
   123 }
   125 CacheStorageService::~CacheStorageService()
   126 {
   127   LOG(("CacheStorageService::~CacheStorageService"));
   128   sSelf = nullptr;
   129 }
   131 void CacheStorageService::Shutdown()
   132 {
   133   if (mShutdown)
   134     return;
   136   LOG(("CacheStorageService::Shutdown - start"));
   138   mShutdown = true;
   140   nsCOMPtr<nsIRunnable> event =
   141     NS_NewRunnableMethod(this, &CacheStorageService::ShutdownBackground);
   142   Dispatch(event);
   144   mozilla::MutexAutoLock lock(mLock);
   145   sGlobalEntryTables->Clear();
   146   delete sGlobalEntryTables;
   147   sGlobalEntryTables = nullptr;
   149   LOG(("CacheStorageService::Shutdown - done"));
   150 }
   152 void CacheStorageService::ShutdownBackground()
   153 {
   154   MOZ_ASSERT(IsOnManagementThread());
   156   Pool(false).mFrecencyArray.Clear();
   157   Pool(false).mExpirationArray.Clear();
   158   Pool(true).mFrecencyArray.Clear();
   159   Pool(true).mExpirationArray.Clear();
   160 }
   162 // Internal management methods
   164 namespace { // anon
   166 // WalkRunnable
   167 // Responsible to visit the storage and walk all entries on it asynchronously
   169 class WalkRunnable : public nsRunnable
   170 {
   171 public:
   172   WalkRunnable(nsCSubstring const & aContextKey, bool aVisitEntries,
   173                bool aUsingDisk,
   174                nsICacheStorageVisitor* aVisitor)
   175     : mContextKey(aContextKey)
   176     , mCallback(aVisitor)
   177     , mSize(0)
   178     , mNotifyStorage(true)
   179     , mVisitEntries(aVisitEntries)
   180     , mUsingDisk(aUsingDisk)
   181   {
   182     MOZ_ASSERT(NS_IsMainThread());
   183   }
   185 private:
   186   NS_IMETHODIMP Run()
   187   {
   188     if (CacheStorageService::IsOnManagementThread()) {
   189       LOG(("WalkRunnable::Run - collecting [this=%p, disk=%d]", this, (bool)mUsingDisk));
   190       // First, walk, count and grab all entries from the storage
   191       // TODO
   192       // - walk files on disk, when the storage is not private
   193       //    - should create representative entries only for the time
   194       //      of need
   196       mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock());
   198       if (!CacheStorageService::IsRunning())
   199         return NS_ERROR_NOT_INITIALIZED;
   201       CacheEntryTable* entries;
   202       if (sGlobalEntryTables->Get(mContextKey, &entries))
   203         entries->EnumerateRead(&WalkRunnable::WalkStorage, this);
   205       // Next, we dispatch to the main thread
   206     }
   207     else if (NS_IsMainThread()) {
   208       LOG(("WalkRunnable::Run - notifying [this=%p, disk=%d]", this, (bool)mUsingDisk));
   209       if (mNotifyStorage) {
   210         LOG(("  storage"));
   211         // Second, notify overall storage info
   212         mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize);
   213         if (!mVisitEntries)
   214           return NS_OK; // done
   216         mNotifyStorage = false;
   217       }
   218       else {
   219         LOG(("  entry [left=%d]", mEntryArray.Length()));
   220         // Third, notify each entry until depleted.
   221         if (!mEntryArray.Length()) {
   222           mCallback->OnCacheEntryVisitCompleted();
   223           return NS_OK; // done
   224         }
   226         mCallback->OnCacheEntryInfo(mEntryArray[0]);
   227         mEntryArray.RemoveElementAt(0);
   229         // Dispatch to the main thread again
   230       }
   231     }
   232     else {
   233       MOZ_ASSERT(false);
   234       return NS_ERROR_FAILURE;
   235     }
   237     NS_DispatchToMainThread(this);
   238     return NS_OK;
   239   }
   241   virtual ~WalkRunnable()
   242   {
   243     if (mCallback)
   244       ProxyReleaseMainThread(mCallback);
   245   }
   247   static PLDHashOperator
   248   WalkStorage(const nsACString& aKey,
   249               CacheEntry* aEntry,
   250               void* aClosure)
   251   {
   252     WalkRunnable* walker = static_cast<WalkRunnable*>(aClosure);
   254     if (!walker->mUsingDisk && aEntry->IsUsingDiskLocked())
   255       return PL_DHASH_NEXT;
   257     walker->mSize += aEntry->GetMetadataMemoryConsumption();
   259     int64_t size;
   260     if (NS_SUCCEEDED(aEntry->GetDataSize(&size)))
   261       walker->mSize += size;
   263     walker->mEntryArray.AppendElement(aEntry);
   264     return PL_DHASH_NEXT;
   265   }
   267   nsCString mContextKey;
   268   nsCOMPtr<nsICacheStorageVisitor> mCallback;
   269   nsTArray<nsRefPtr<CacheEntry> > mEntryArray;
   271   uint64_t mSize;
   273   bool mNotifyStorage : 1;
   274   bool mVisitEntries : 1;
   275   bool mUsingDisk : 1;
   276 };
   278 PLDHashOperator CollectPrivateContexts(const nsACString& aKey,
   279                                        CacheEntryTable* aTable,
   280                                        void* aClosure)
   281 {
   282   nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
   283   if (info && info->IsPrivate()) {
   284     nsTArray<nsCString>* keys = static_cast<nsTArray<nsCString>*>(aClosure);
   285     keys->AppendElement(aKey);
   286   }
   288   return PL_DHASH_NEXT;
   289 }
   291 PLDHashOperator CollectContexts(const nsACString& aKey,
   292                                        CacheEntryTable* aTable,
   293                                        void* aClosure)
   294 {
   295   nsTArray<nsCString>* keys = static_cast<nsTArray<nsCString>*>(aClosure);
   296   keys->AppendElement(aKey);
   298   return PL_DHASH_NEXT;
   299 }
   301 } // anon
   303 void CacheStorageService::DropPrivateBrowsingEntries()
   304 {
   305   mozilla::MutexAutoLock lock(mLock);
   307   if (mShutdown)
   308     return;
   310   nsTArray<nsCString> keys;
   311   sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys);
   313   for (uint32_t i = 0; i < keys.Length(); ++i)
   314     DoomStorageEntries(keys[i], nullptr, true, nullptr);
   315 }
   317 // static
   318 void CacheStorageService::WipeCacheDirectory(uint32_t aVersion)
   319 {
   320   nsCOMPtr<nsIFile> cacheDir;
   321   switch (aVersion) {
   322   case 0:
   323     nsCacheService::GetDiskCacheDirectory(getter_AddRefs(cacheDir));
   324     break;
   325   case 1:
   326     CacheFileIOManager::GetCacheDirectory(getter_AddRefs(cacheDir));
   327     break;
   328   }
   330   if (!cacheDir)
   331     return;
   333   nsDeleteDir::DeleteDir(cacheDir, true, 30000);
   334 }
   336 // Helper methods
   338 // static
   339 bool CacheStorageService::IsOnManagementThread()
   340 {
   341   nsRefPtr<CacheStorageService> service = Self();
   342   if (!service)
   343     return false;
   345   nsCOMPtr<nsIEventTarget> target = service->Thread();
   346   if (!target)
   347     return false;
   349   bool currentThread;
   350   nsresult rv = target->IsOnCurrentThread(&currentThread);
   351   return NS_SUCCEEDED(rv) && currentThread;
   352 }
   354 already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const
   355 {
   356   return CacheFileIOManager::IOTarget();
   357 }
   359 nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent)
   360 {
   361   nsRefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread();
   362   if (!cacheIOThread)
   363     return NS_ERROR_NOT_AVAILABLE;
   365   return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT);
   366 }
   368 // nsICacheStorageService
   370 NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo,
   371                                                       nsICacheStorage * *_retval)
   372 {
   373   NS_ENSURE_ARG(aLoadContextInfo);
   374   NS_ENSURE_ARG(_retval);
   376   nsCOMPtr<nsICacheStorage> storage;
   377   if (CacheObserver::UseNewCache()) {
   378     storage = new CacheStorage(aLoadContextInfo, false, false);
   379   }
   380   else {
   381     storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
   382   }
   384   storage.forget(_retval);
   385   return NS_OK;
   386 }
   388 NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo,
   389                                                     bool aLookupAppCache,
   390                                                     nsICacheStorage * *_retval)
   391 {
   392   NS_ENSURE_ARG(aLoadContextInfo);
   393   NS_ENSURE_ARG(_retval);
   395   // TODO save some heap granularity - cache commonly used storages.
   397   // When disk cache is disabled, still provide a storage, but just keep stuff
   398   // in memory.
   399   bool useDisk = CacheObserver::UseDiskCache();
   401   nsCOMPtr<nsICacheStorage> storage;
   402   if (CacheObserver::UseNewCache()) {
   403     storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache);
   404   }
   405   else {
   406     storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr);
   407   }
   409   storage.forget(_retval);
   410   return NS_OK;
   411 }
   413 NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
   414                                                    nsIApplicationCache *aApplicationCache,
   415                                                    nsICacheStorage * *_retval)
   416 {
   417   NS_ENSURE_ARG(aLoadContextInfo);
   418   NS_ENSURE_ARG(_retval);
   420   nsCOMPtr<nsICacheStorage> storage;
   421   if (CacheObserver::UseNewCache()) {
   422     // Using classification since cl believes we want to instantiate this method
   423     // having the same name as the desired class...
   424     storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache);
   425   }
   426   else {
   427     storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
   428   }
   430   storage.forget(_retval);
   431   return NS_OK;
   432 }
   434 NS_IMETHODIMP CacheStorageService::Clear()
   435 {
   436   nsresult rv;
   438   if (CacheObserver::UseNewCache()) {
   439     {
   440       mozilla::MutexAutoLock lock(mLock);
   442       NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
   444       nsTArray<nsCString> keys;
   445       sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys);
   447       for (uint32_t i = 0; i < keys.Length(); ++i)
   448         DoomStorageEntries(keys[i], nullptr, true, nullptr);
   449     }
   451     rv = CacheFileIOManager::EvictAll();
   452     NS_ENSURE_SUCCESS(rv, rv);
   453   } else {
   454     nsCOMPtr<nsICacheService> serv =
   455         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
   456     NS_ENSURE_SUCCESS(rv, rv);
   458     rv = serv->EvictEntries(nsICache::STORE_ANYWHERE);
   459     NS_ENSURE_SUCCESS(rv, rv);
   460   }
   462   return NS_OK;
   463 }
   465 NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
   466 {
   467   uint32_t what;
   469   switch (aWhat) {
   470   case PURGE_DISK_DATA_ONLY:
   471     what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED;
   472     break;
   474   case PURGE_DISK_ALL:
   475     what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED;
   476     break;
   478   case PURGE_EVERYTHING:
   479     what = CacheEntry::PURGE_WHOLE;
   480     break;
   482   default:
   483     return NS_ERROR_INVALID_ARG;
   484   }
   486   nsCOMPtr<nsIRunnable> event =
   487     new PurgeFromMemoryRunnable(this, what);
   489   return Dispatch(event);
   490 }
   492 NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
   493   nsICacheStorageConsumptionObserver* aObserver)
   494 {
   495   NS_ENSURE_ARG(aObserver);
   497   nsresult rv;
   499   if (CacheObserver::UseNewCache()) {
   500     rv = CacheIndex::AsyncGetDiskConsumption(aObserver);
   501     NS_ENSURE_SUCCESS(rv, rv);
   502   } else {
   503     rv = _OldGetDiskConsumption::Get(aObserver);
   504     NS_ENSURE_SUCCESS(rv, rv);
   505   }
   507   return NS_OK;
   508 }
   510 NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget)
   511 {
   512   NS_ENSURE_ARG(aEventTarget);
   514   if (CacheObserver::UseNewCache()) {
   515     nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
   516     ioTarget.forget(aEventTarget);
   517   }
   518   else {
   519     nsresult rv;
   521     nsCOMPtr<nsICacheService> serv =
   522         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
   523     NS_ENSURE_SUCCESS(rv, rv);
   525     rv = serv->GetCacheIOTarget(aEventTarget);
   526     NS_ENSURE_SUCCESS(rv, rv);
   527   }
   529   return NS_OK;
   530 }
   532 // Methods used by CacheEntry for management of in-memory structures.
   534 namespace { // anon
   536 class FrecencyComparator
   537 {
   538 public:
   539   bool Equals(CacheEntry* a, CacheEntry* b) const {
   540     return a->GetFrecency() == b->GetFrecency();
   541   }
   542   bool LessThan(CacheEntry* a, CacheEntry* b) const {
   543     return a->GetFrecency() < b->GetFrecency();
   544   }
   545 };
   547 class ExpirationComparator
   548 {
   549 public:
   550   bool Equals(CacheEntry* a, CacheEntry* b) const {
   551     return a->GetExpirationTime() == b->GetExpirationTime();
   552   }
   553   bool LessThan(CacheEntry* a, CacheEntry* b) const {
   554     return a->GetExpirationTime() < b->GetExpirationTime();
   555   }
   556 };
   558 } // anon
   560 void
   561 CacheStorageService::RegisterEntry(CacheEntry* aEntry)
   562 {
   563   MOZ_ASSERT(IsOnManagementThread());
   565   if (mShutdown || !aEntry->CanRegister())
   566     return;
   568   LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry));
   570   MemoryPool& pool = Pool(aEntry->IsUsingDisk());
   571   pool.mFrecencyArray.InsertElementSorted(aEntry, FrecencyComparator());
   572   pool.mExpirationArray.InsertElementSorted(aEntry, ExpirationComparator());
   574   aEntry->SetRegistered(true);
   575 }
   577 void
   578 CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
   579 {
   580   MOZ_ASSERT(IsOnManagementThread());
   582   if (!aEntry->IsRegistered())
   583     return;
   585   LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry));
   587   MemoryPool& pool = Pool(aEntry->IsUsingDisk());
   588   mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry);
   589   mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry);
   591   MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration));
   593   // Note: aEntry->CanRegister() since now returns false
   594   aEntry->SetRegistered(false);
   595 }
   597 static bool
   598 AddExactEntry(CacheEntryTable* aEntries,
   599               nsCString const& aKey,
   600               CacheEntry* aEntry,
   601               bool aOverwrite)
   602 {
   603   nsRefPtr<CacheEntry> existingEntry;
   604   if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
   605     bool equals = existingEntry == aEntry;
   606     LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals));
   607     return equals; // Already there...
   608   }
   610   LOG(("AddExactEntry [entry=%p put]", aEntry));
   611   aEntries->Put(aKey, aEntry);
   612   return true;
   613 }
   615 static bool
   616 RemoveExactEntry(CacheEntryTable* aEntries,
   617                  nsCString const& aKey,
   618                  CacheEntry* aEntry,
   619                  bool aOverwrite)
   620 {
   621   nsRefPtr<CacheEntry> existingEntry;
   622   if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
   623     LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
   624     return false; // Already removed...
   625   }
   627   if (!aOverwrite && existingEntry != aEntry) {
   628     LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
   629     return false; // Already replaced...
   630   }
   632   LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
   633   aEntries->Remove(aKey);
   634   return true;
   635 }
   637 bool
   638 CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
   639 {
   640   LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry));
   642   nsAutoCString entryKey;
   643   nsresult rv = aEntry->HashingKey(entryKey);
   644   if (NS_FAILED(rv)) {
   645     NS_ERROR("aEntry->HashingKey() failed?");
   646     return false;
   647   }
   649   mozilla::MutexAutoLock lock(mLock);
   651   if (mShutdown) {
   652     LOG(("  after shutdown"));
   653     return false;
   654   }
   656   if (aOnlyUnreferenced && aEntry->IsReferenced()) {
   657     LOG(("  still referenced, not removing"));
   658     return false;
   659   }
   661   CacheEntryTable* entries;
   662   if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
   663     RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
   665   nsAutoCString memoryStorageID(aEntry->GetStorageID());
   666   AppendMemoryStorageID(memoryStorageID);
   668   if (sGlobalEntryTables->Get(memoryStorageID, &entries))
   669     RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
   671   return true;
   672 }
   674 void
   675 CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry,
   676                                            bool aOnlyInMemory,
   677                                            bool aOverwrite)
   678 {
   679   LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]",
   680     aEntry, aOnlyInMemory, aOverwrite));
   681   // This method is responsible to put this entry to a special record hashtable
   682   // that contains only entries that are stored in memory.
   683   // Keep in mind that every entry, regardless of whether is in-memory-only or not
   684   // is always recorded in the storage master hash table, the one identified by
   685   // CacheEntry.StorageID().
   687   mLock.AssertCurrentThreadOwns();
   689   if (mShutdown) {
   690     LOG(("  after shutdown"));
   691     return;
   692   }
   694   nsresult rv;
   696   nsAutoCString entryKey;
   697   rv = aEntry->HashingKey(entryKey);
   698   if (NS_FAILED(rv)) {
   699     NS_ERROR("aEntry->HashingKey() failed?");
   700     return;
   701   }
   703   CacheEntryTable* entries = nullptr;
   704   nsAutoCString memoryStorageID(aEntry->GetStorageID());
   705   AppendMemoryStorageID(memoryStorageID);
   707   if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
   708     if (!aOnlyInMemory) {
   709       LOG(("  not recorded as memory only"));
   710       return;
   711     }
   713     entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
   714     sGlobalEntryTables->Put(memoryStorageID, entries);
   715     LOG(("  new memory-only storage table for %s", memoryStorageID.get()));
   716   }
   718   if (aOnlyInMemory) {
   719     AddExactEntry(entries, entryKey, aEntry, aOverwrite);
   720   }
   721   else {
   722     RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
   723   }
   724 }
   726 void
   727 CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer,
   728                                                uint32_t aCurrentMemoryConsumption)
   729 {
   730   LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]",
   731     aConsumer, aCurrentMemoryConsumption));
   733   uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption;
   734   if (savedMemorySize == aCurrentMemoryConsumption)
   735     return;
   737   // Exchange saved size with current one.
   738   aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption;
   740   bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY);
   741   bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange(
   742     savedMemorySize, aCurrentMemoryConsumption);
   744   if (!overLimit)
   745     return;
   747   // It's likely the timer has already been set when we get here,
   748   // check outside the lock to save resources.
   749   if (mPurgeTimer)
   750     return;
   752   // We don't know if this is called under the service lock or not,
   753   // hence rather dispatch.
   754   nsRefPtr<nsIEventTarget> cacheIOTarget = Thread();
   755   if (!cacheIOTarget)
   756     return;
   758   // Dispatch as a priority task, we want to set the purge timer
   759   // ASAP to prevent vain redispatch of this event.
   760   nsCOMPtr<nsIRunnable> event =
   761     NS_NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit);
   762   cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   763 }
   765 bool
   766 CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize,
   767                                                            uint32_t aCurrentMemoryConsumption)
   768 {
   769   mMemorySize -= aSavedMemorySize;
   770   mMemorySize += aCurrentMemoryConsumption;
   772   LOG(("  mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize));
   774   // Bypass purging when memory has not grew up significantly
   775   if (aCurrentMemoryConsumption <= aSavedMemorySize)
   776     return false;
   778   return mMemorySize > Limit();
   779 }
   781 void
   782 CacheStorageService::SchedulePurgeOverMemoryLimit()
   783 {
   784   mozilla::MutexAutoLock lock(mLock);
   786   if (mPurgeTimer)
   787     return;
   789   mPurgeTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   790   if (mPurgeTimer)
   791     mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
   792 }
   794 NS_IMETHODIMP
   795 CacheStorageService::Notify(nsITimer* aTimer)
   796 {
   797   if (aTimer == mPurgeTimer) {
   798     mPurgeTimer = nullptr;
   800     nsCOMPtr<nsIRunnable> event =
   801       NS_NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit);
   802     Dispatch(event);
   803   }
   805   return NS_OK;
   806 }
   808 void
   809 CacheStorageService::PurgeOverMemoryLimit()
   810 {
   811   MOZ_ASSERT(IsOnManagementThread());
   813   LOG(("CacheStorageService::PurgeOverMemoryLimit"));
   815   Pool(true).PurgeOverMemoryLimit();
   816   Pool(false).PurgeOverMemoryLimit();
   817 }
   819 void
   820 CacheStorageService::MemoryPool::PurgeOverMemoryLimit()
   821 {
   822 #ifdef PR_LOGGING
   823   TimeStamp start(TimeStamp::Now());
   824 #endif
   826   uint32_t const memoryLimit = Limit();
   827   if (mMemorySize > memoryLimit) {
   828     LOG(("  memory data consumption over the limit, abandon expired entries"));
   829     PurgeExpired();
   830   }
   832   bool frecencyNeedsSort = true;
   834   // No longer makes sense since:
   835   // Memory entries are never purged partially, only as a whole when the memory
   836   // cache limit is overreached.
   837   // Disk entries throw the data away ASAP so that only metadata are kept.
   838   // TODO when this concept of two separate pools is found working, the code should
   839   // clean up.
   840 #if 0
   841   if (mMemorySize > memoryLimit) {
   842     LOG(("  memory data consumption over the limit, abandon disk backed data"));
   843     PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED);
   844   }
   846   if (mMemorySize > memoryLimit) {
   847     LOG(("  metadata consumtion over the limit, abandon disk backed entries"));
   848     PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED);
   849   }
   850 #endif
   852   if (mMemorySize > memoryLimit) {
   853     LOG(("  memory data consumption over the limit, abandon any entry"));
   854     PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
   855   }
   857   LOG(("  purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
   858 }
   860 void
   861 CacheStorageService::MemoryPool::PurgeExpired()
   862 {
   863   MOZ_ASSERT(IsOnManagementThread());
   865   mExpirationArray.Sort(ExpirationComparator());
   866   uint32_t now = NowInSeconds();
   868   uint32_t const memoryLimit = Limit();
   870   for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
   871     if (CacheIOThread::YieldAndRerun())
   872       return;
   874     nsRefPtr<CacheEntry> entry = mExpirationArray[i];
   876     uint32_t expirationTime = entry->GetExpirationTime();
   877     if (expirationTime > 0 && expirationTime <= now) {
   878       LOG(("  dooming expired entry=%p, exptime=%u (now=%u)",
   879         entry.get(), entry->GetExpirationTime(), now));
   881       entry->PurgeAndDoom();
   882       continue;
   883     }
   885     // not purged, move to the next one
   886     ++i;
   887   }
   888 }
   890 void
   891 CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat)
   892 {
   893   MOZ_ASSERT(IsOnManagementThread());
   895   if (aFrecencyNeedsSort) {
   896     mFrecencyArray.Sort(FrecencyComparator());
   897     aFrecencyNeedsSort = false;
   898   }
   900   uint32_t const memoryLimit = Limit();
   902   for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
   903     if (CacheIOThread::YieldAndRerun())
   904       return;
   906     nsRefPtr<CacheEntry> entry = mFrecencyArray[i];
   908     if (entry->Purge(aWhat)) {
   909       LOG(("  abandoned (%d), entry=%p, frecency=%1.10f",
   910         aWhat, entry.get(), entry->GetFrecency()));
   911       continue;
   912     }
   914     // not purged, move to the next one
   915     ++i;
   916   }
   917 }
   919 void
   920 CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat)
   921 {
   922   LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat));
   923   MOZ_ASSERT(IsOnManagementThread());
   925   for (uint32_t i = 0; i < mFrecencyArray.Length();) {
   926     if (CacheIOThread::YieldAndRerun())
   927       return;
   929     nsRefPtr<CacheEntry> entry = mFrecencyArray[i];
   931     if (entry->Purge(aWhat)) {
   932       LOG(("  abandoned entry=%p", entry.get()));
   933       continue;
   934     }
   936     // not purged, move to the next one
   937     ++i;
   938   }
   939 }
   941 // Methods exposed to and used by CacheStorage.
   943 nsresult
   944 CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
   945                                      nsIURI* aURI,
   946                                      const nsACString & aIdExtension,
   947                                      bool aCreateIfNotExist,
   948                                      bool aReplace,
   949                                      CacheEntryHandle** aResult)
   950 {
   951   NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
   953   NS_ENSURE_ARG(aStorage);
   955   nsAutoCString contextKey;
   956   CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
   958   return AddStorageEntry(contextKey, aURI, aIdExtension,
   959                          aStorage->WriteToDisk(), aCreateIfNotExist, aReplace,
   960                          aResult);
   961 }
   963 nsresult
   964 CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
   965                                      nsIURI* aURI,
   966                                      const nsACString & aIdExtension,
   967                                      bool aWriteToDisk,
   968                                      bool aCreateIfNotExist,
   969                                      bool aReplace,
   970                                      CacheEntryHandle** aResult)
   971 {
   972   NS_ENSURE_ARG(aURI);
   974   nsresult rv;
   976   nsAutoCString entryKey;
   977   rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
   978   NS_ENSURE_SUCCESS(rv, rv);
   980   LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
   981     entryKey.get(), aContextKey.BeginReading()));
   983   nsRefPtr<CacheEntry> entry;
   984   nsRefPtr<CacheEntryHandle> handle;
   986   {
   987     mozilla::MutexAutoLock lock(mLock);
   989     NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
   991     // Ensure storage table
   992     CacheEntryTable* entries;
   993     if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
   994       entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
   995       sGlobalEntryTables->Put(aContextKey, entries);
   996       LOG(("  new storage entries table for context %s", aContextKey.BeginReading()));
   997     }
   999     bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
  1001     // check whether the file is already doomed
  1002     if (entryExists && entry->IsFileDoomed() && !aReplace) {
  1003       LOG(("  file already doomed, replacing the entry"));
  1004       aReplace = true;
  1007     // If truncate is demanded, delete and doom the current entry
  1008     if (entryExists && aReplace) {
  1009       entries->Remove(entryKey);
  1011       LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
  1012       // On purpose called under the lock to prevent races of doom and open on I/O thread
  1013       // No need to remove from both memory-only and all-entries tables.  The new entry
  1014       // will overwrite the shadow entry in its ctor.
  1015       entry->DoomAlreadyRemoved();
  1017       entry = nullptr;
  1018       entryExists = false;
  1021     if (entryExists && entry->SetUsingDisk(aWriteToDisk)) {
  1022       RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */);
  1025     // Ensure entry for the particular URL, if not read/only
  1026     if (!entryExists && (aCreateIfNotExist || aReplace)) {
  1027       // Entry is not in the hashtable or has just been truncated...
  1028       entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk);
  1029       entries->Put(entryKey, entry);
  1030       LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
  1033     if (entry) {
  1034       // Here, if this entry was not for a long time referenced by any consumer,
  1035       // gets again first 'handles count' reference.
  1036       handle = entry->NewHandle();
  1040   handle.forget(aResult);
  1041   return NS_OK;
  1044 namespace { // anon
  1046 class CacheEntryDoomByKeyCallback : public CacheFileIOListener
  1048 public:
  1049   NS_DECL_THREADSAFE_ISUPPORTS
  1051   CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback)
  1052     : mCallback(aCallback) { }
  1053   virtual ~CacheEntryDoomByKeyCallback();
  1055 private:
  1056   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; }
  1057   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) { return NS_OK; }
  1058   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { return NS_OK; }
  1059   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
  1060   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; }
  1061   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; }
  1063   nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  1064 };
  1066 CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback()
  1068   if (mCallback)
  1069     ProxyReleaseMainThread(mCallback);
  1072 NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
  1073                                                         nsresult aResult)
  1075   if (!mCallback)
  1076     return NS_OK;
  1078   mCallback->OnCacheEntryDoomed(aResult);
  1079   return NS_OK;
  1082 NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener);
  1084 } // anon
  1086 nsresult
  1087 CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage,
  1088                                       nsIURI *aURI,
  1089                                       const nsACString & aIdExtension,
  1090                                       nsICacheEntryDoomCallback* aCallback)
  1092   LOG(("CacheStorageService::DoomStorageEntry"));
  1094   NS_ENSURE_ARG(aStorage);
  1095   NS_ENSURE_ARG(aURI);
  1097   nsAutoCString contextKey;
  1098   CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1100   nsAutoCString entryKey;
  1101   nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  1102   NS_ENSURE_SUCCESS(rv, rv);
  1104   nsRefPtr<CacheEntry> entry;
  1106     mozilla::MutexAutoLock lock(mLock);
  1108     NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1110     CacheEntryTable* entries;
  1111     if (sGlobalEntryTables->Get(contextKey, &entries)) {
  1112       if (entries->Get(entryKey, getter_AddRefs(entry))) {
  1113         if (aStorage->WriteToDisk() || !entry->IsUsingDiskLocked()) {
  1114           // When evicting from disk storage, purge
  1115           // When evicting from memory storage and the entry is memory-only, purge
  1116           LOG(("  purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
  1117             entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked()));
  1118           entries->Remove(entryKey);
  1120         else {
  1121           // Otherwise, leave it
  1122           LOG(("  leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
  1123             entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked()));
  1124           entry = nullptr;
  1130   if (entry) {
  1131     LOG(("  dooming entry %p for %s", entry.get(), entryKey.get()));
  1132     return entry->AsyncDoom(aCallback);
  1135   LOG(("  no entry loaded for %s", entryKey.get()));
  1137   if (aStorage->WriteToDisk()) {
  1138     nsAutoCString contextKey;
  1139     CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1141     rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey);
  1142     NS_ENSURE_SUCCESS(rv, rv);
  1144     LOG(("  dooming file only for %s", entryKey.get()));
  1146     nsRefPtr<CacheEntryDoomByKeyCallback> callback(
  1147       new CacheEntryDoomByKeyCallback(aCallback));
  1148     rv = CacheFileIOManager::DoomFileByKey(entryKey, callback);
  1149     NS_ENSURE_SUCCESS(rv, rv);
  1151     return NS_OK;
  1154   if (aCallback)
  1155     aCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
  1157   return NS_OK;
  1160 nsresult
  1161 CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
  1162                                         nsICacheEntryDoomCallback* aCallback)
  1164   LOG(("CacheStorageService::DoomStorageEntries"));
  1166   NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1167   NS_ENSURE_ARG(aStorage);
  1169   nsAutoCString contextKey;
  1170   CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1172   mozilla::MutexAutoLock lock(mLock);
  1174   return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
  1175                             aStorage->WriteToDisk(), aCallback);
  1178 nsresult
  1179 CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
  1180                                         nsILoadContextInfo* aContext,
  1181                                         bool aDiskStorage,
  1182                                         nsICacheEntryDoomCallback* aCallback)
  1184   mLock.AssertCurrentThreadOwns();
  1186   NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
  1188   nsAutoCString memoryStorageID(aContextKey);
  1189   AppendMemoryStorageID(memoryStorageID);
  1191   if (aDiskStorage) {
  1192     LOG(("  dooming disk+memory storage of %s", aContextKey.BeginReading()));
  1194     // Just remove all entries, CacheFileIOManager will take care of the files.
  1195     sGlobalEntryTables->Remove(aContextKey);
  1196     sGlobalEntryTables->Remove(memoryStorageID);
  1198     if (aContext && !aContext->IsPrivate()) {
  1199       LOG(("  dooming disk entries"));
  1200       CacheFileIOManager::EvictByContext(aContext);
  1202   } else {
  1203     LOG(("  dooming memory-only storage of %s", aContextKey.BeginReading()));
  1205     class MemoryEntriesRemoval {
  1206     public:
  1207       static PLDHashOperator EvictEntry(const nsACString& aKey,
  1208                                         CacheEntry* aEntry,
  1209                                         void* aClosure)
  1211         CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure);
  1212         nsCString key(aKey);
  1213         RemoveExactEntry(entries, key, aEntry, false);
  1214         return PL_DHASH_NEXT;
  1216     };
  1218     // Remove the memory entries table from the global tables.
  1219     // Since we store memory entries also in the disk entries table
  1220     // we need to remove the memory entries from the disk table one
  1221     // by one manually.
  1222     nsAutoPtr<CacheEntryTable> memoryEntries;
  1223     sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);
  1225     CacheEntryTable* entries;
  1226     sGlobalEntryTables->Get(aContextKey, &entries);
  1227     if (memoryEntries && entries)
  1228       memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries);
  1231   // An artificial callback.  This is a candidate for removal tho.  In the new
  1232   // cache any 'doom' or 'evict' function ensures that the entry or entries
  1233   // being doomed is/are not accessible after the function returns.  So there is
  1234   // probably no need for a callback - has no meaning.  But for compatibility
  1235   // with the old cache that is still in the tree we keep the API similar to be
  1236   // able to make tests as well as other consumers work for now.
  1237   class Callback : public nsRunnable
  1239   public:
  1240     Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
  1241     NS_IMETHODIMP Run()
  1243       mCallback->OnCacheEntryDoomed(NS_OK);
  1244       return NS_OK;
  1246     nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  1247   };
  1249   if (aCallback) {
  1250     nsRefPtr<nsRunnable> callback = new Callback(aCallback);
  1251     return NS_DispatchToCurrentThread(callback);
  1254   return NS_OK;
  1257 nsresult
  1258 CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
  1259                                         bool aVisitEntries,
  1260                                         nsICacheStorageVisitor* aVisitor)
  1262   LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries));
  1263   NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1265   NS_ENSURE_ARG(aStorage);
  1267   nsAutoCString contextKey;
  1268   CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1270   nsRefPtr<WalkRunnable> event = new WalkRunnable(
  1271     contextKey, aVisitEntries, aStorage->WriteToDisk(), aVisitor);
  1272   return Dispatch(event);
  1275 void
  1276 CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
  1277                                      const nsACString & aIdExtension,
  1278                                      const nsACString & aURISpec)
  1280   nsAutoCString contextKey;
  1281   CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
  1283   nsAutoCString entryKey;
  1284   CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
  1286   mozilla::MutexAutoLock lock(mLock);
  1288   if (mShutdown)
  1289     return;
  1291   CacheEntryTable* entries;
  1292   if (!sGlobalEntryTables->Get(contextKey, &entries))
  1293     return;
  1295   nsRefPtr<CacheEntry> entry;
  1296   if (!entries->Get(entryKey, getter_AddRefs(entry)))
  1297     return;
  1299   if (!entry->IsFileDoomed())
  1300     return;
  1302   if (entry->IsReferenced())
  1303     return;
  1305   // Need to remove under the lock to avoid possible race leading
  1306   // to duplication of the entry per its key.
  1307   RemoveExactEntry(entries, entryKey, entry, false);
  1308   entry->DoomAlreadyRemoved();
  1311 // nsIMemoryReporter
  1313 size_t
  1314 CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1316   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
  1318   size_t n = 0;
  1319   // The elemets are referenced by sGlobalEntryTables and are reported from there
  1320   n += Pool(true).mFrecencyArray.SizeOfExcludingThis(mallocSizeOf);
  1321   n += Pool(true).mExpirationArray.SizeOfExcludingThis(mallocSizeOf);
  1322   n += Pool(false).mFrecencyArray.SizeOfExcludingThis(mallocSizeOf);
  1323   n += Pool(false).mExpirationArray.SizeOfExcludingThis(mallocSizeOf);
  1324   // Entries reported manually in CacheStorageService::CollectReports callback
  1325   if (sGlobalEntryTables) {
  1326     n += sGlobalEntryTables->SizeOfIncludingThis(nullptr, mallocSizeOf);
  1329   return n;
  1332 size_t
  1333 CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1335   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
  1338 namespace { // anon
  1340 class ReportStorageMemoryData
  1342 public:
  1343   nsIMemoryReporterCallback *mHandleReport;
  1344   nsISupports *mData;
  1345 };
  1347 size_t CollectEntryMemory(nsACString const & aKey,
  1348                           nsRefPtr<mozilla::net::CacheEntry> const & aEntry,
  1349                           mozilla::MallocSizeOf mallocSizeOf,
  1350                           void * aClosure)
  1352   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
  1354   CacheEntryTable* aTable = static_cast<CacheEntryTable*>(aClosure);
  1356   size_t n = 0;
  1357   n += aKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1359   // Bypass memory-only entries, those will be reported when iterating
  1360   // the memory only table. Memory-only entries are stored in both ALL_ENTRIES
  1361   // and MEMORY_ONLY hashtables.
  1362   if (aTable->Type() == CacheEntryTable::MEMORY_ONLY || aEntry->IsUsingDiskLocked())
  1363     n += aEntry->SizeOfIncludingThis(mallocSizeOf);
  1365   return n;
  1368 PLDHashOperator ReportStorageMemory(const nsACString& aKey,
  1369                                     CacheEntryTable* aTable,
  1370                                     void* aClosure)
  1372   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
  1374   size_t size = aTable->SizeOfIncludingThis(&CollectEntryMemory,
  1375                                             CacheStorageService::MallocSizeOf,
  1376                                             aTable);
  1378   ReportStorageMemoryData& data = *static_cast<ReportStorageMemoryData*>(aClosure);
  1379   data.mHandleReport->Callback(
  1380     EmptyCString(),
  1381     nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
  1382       aTable->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
  1383       aKey.BeginReading()),
  1384     nsIMemoryReporter::KIND_HEAP,
  1385     nsIMemoryReporter::UNITS_BYTES,
  1386     size,
  1387     NS_LITERAL_CSTRING("Memory used by the cache storage."),
  1388     data.mData);
  1390   return PL_DHASH_NEXT;
  1393 } // anon
  1395 NS_IMETHODIMP
  1396 CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport, nsISupports* aData)
  1398   nsresult rv;
  1400   rv = MOZ_COLLECT_REPORT(
  1401     "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
  1402     CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
  1403     "Memory used by the cache IO manager.");
  1404   if (NS_WARN_IF(NS_FAILED(rv)))
  1405     return rv;
  1407   rv = MOZ_COLLECT_REPORT(
  1408     "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
  1409     CacheIndex::SizeOfIncludingThis(MallocSizeOf),
  1410     "Memory used by the cache index.");
  1411   if (NS_WARN_IF(NS_FAILED(rv)))
  1412     return rv;
  1414   MutexAutoLock lock(mLock);
  1416   // Report the service instance, this doesn't report entries, done lower
  1417   rv = MOZ_COLLECT_REPORT(
  1418     "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
  1419     SizeOfIncludingThis(MallocSizeOf),
  1420     "Memory used by the cache storage service.");
  1421   if (NS_WARN_IF(NS_FAILED(rv)))
  1422     return rv;
  1424   // Report all entries, each storage separately (by the context key)
  1425   //
  1426   // References are:
  1427   // sGlobalEntryTables to N CacheEntryTable
  1428   // CacheEntryTable to N CacheEntry
  1429   // CacheEntry to 1 CacheFile
  1430   // CacheFile to
  1431   //   N CacheFileChunk (keeping the actual data)
  1432   //   1 CacheFileMetadata (keeping http headers etc.)
  1433   //   1 CacheFileOutputStream
  1434   //   N CacheFileInputStream
  1435   ReportStorageMemoryData data;
  1436   data.mHandleReport = aHandleReport;
  1437   data.mData = aData;
  1438   sGlobalEntryTables->EnumerateRead(&ReportStorageMemory, &data);
  1440   return NS_OK;
  1443 } // net
  1444 } // mozilla

mercurial