Wed, 31 Dec 2014 06:55:50 +0100
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 "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(¤tThread);
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;
1005 }
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;
1019 }
1021 if (entryExists && entry->SetUsingDisk(aWriteToDisk)) {
1022 RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */);
1023 }
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()));
1031 }
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();
1037 }
1038 }
1040 handle.forget(aResult);
1041 return NS_OK;
1042 }
1044 namespace { // anon
1046 class CacheEntryDoomByKeyCallback : public CacheFileIOListener
1047 {
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()
1067 {
1068 if (mCallback)
1069 ProxyReleaseMainThread(mCallback);
1070 }
1072 NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
1073 nsresult aResult)
1074 {
1075 if (!mCallback)
1076 return NS_OK;
1078 mCallback->OnCacheEntryDoomed(aResult);
1079 return NS_OK;
1080 }
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)
1091 {
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;
1105 {
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);
1119 }
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;
1125 }
1126 }
1127 }
1128 }
1130 if (entry) {
1131 LOG((" dooming entry %p for %s", entry.get(), entryKey.get()));
1132 return entry->AsyncDoom(aCallback);
1133 }
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;
1152 }
1154 if (aCallback)
1155 aCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
1157 return NS_OK;
1158 }
1160 nsresult
1161 CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
1162 nsICacheEntryDoomCallback* aCallback)
1163 {
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);
1176 }
1178 nsresult
1179 CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
1180 nsILoadContextInfo* aContext,
1181 bool aDiskStorage,
1182 nsICacheEntryDoomCallback* aCallback)
1183 {
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);
1201 }
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)
1210 {
1211 CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure);
1212 nsCString key(aKey);
1213 RemoveExactEntry(entries, key, aEntry, false);
1214 return PL_DHASH_NEXT;
1215 }
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);
1229 }
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
1238 {
1239 public:
1240 Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
1241 NS_IMETHODIMP Run()
1242 {
1243 mCallback->OnCacheEntryDoomed(NS_OK);
1244 return NS_OK;
1245 }
1246 nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1247 };
1249 if (aCallback) {
1250 nsRefPtr<nsRunnable> callback = new Callback(aCallback);
1251 return NS_DispatchToCurrentThread(callback);
1252 }
1254 return NS_OK;
1255 }
1257 nsresult
1258 CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
1259 bool aVisitEntries,
1260 nsICacheStorageVisitor* aVisitor)
1261 {
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);
1273 }
1275 void
1276 CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
1277 const nsACString & aIdExtension,
1278 const nsACString & aURISpec)
1279 {
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();
1309 }
1311 // nsIMemoryReporter
1313 size_t
1314 CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1315 {
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);
1327 }
1329 return n;
1330 }
1332 size_t
1333 CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1334 {
1335 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
1336 }
1338 namespace { // anon
1340 class ReportStorageMemoryData
1341 {
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)
1351 {
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;
1366 }
1368 PLDHashOperator ReportStorageMemory(const nsACString& aKey,
1369 CacheEntryTable* aTable,
1370 void* aClosure)
1371 {
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;
1391 }
1393 } // anon
1395 NS_IMETHODIMP
1396 CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport, nsISupports* aData)
1397 {
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;
1441 }
1443 } // net
1444 } // mozilla