Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / netwerk / cache / nsCacheService.cpp
blob2e5eebdc200adba4f2b31f72e36a7371abf5a2e1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/DebugOnly.h"
12 #include "necko-config.h"
14 #include "nsCache.h"
15 #include "nsCacheService.h"
16 #include "nsCacheRequest.h"
17 #include "nsCacheEntry.h"
18 #include "nsCacheEntryDescriptor.h"
19 #include "nsCacheDevice.h"
20 #include "nsMemoryCacheDevice.h"
21 #include "nsICacheVisitor.h"
22 #include "nsDiskCacheDevice.h"
23 #include "nsDiskCacheDeviceSQL.h"
24 #include "nsCacheUtils.h"
25 #include "../cache2/CacheObserver.h"
27 #include "nsIObserverService.h"
28 #include "nsIPrefService.h"
29 #include "nsIPrefBranch.h"
30 #include "nsIFile.h"
31 #include "nsIOService.h"
32 #include "nsDirectoryServiceDefs.h"
33 #include "nsAppDirectoryServiceDefs.h"
34 #include "nsThreadUtils.h"
35 #include "nsProxyRelease.h"
36 #include "nsVoidArray.h"
37 #include "nsDeleteDir.h"
38 #include "nsNetCID.h"
39 #include <math.h> // for log()
40 #include "mozilla/Services.h"
41 #include "nsITimer.h"
42 #include "mozIStorageService.h"
44 #include "mozilla/net/NeckoCommon.h"
45 #include "mozilla/VisualEventTracer.h"
46 #include <algorithm>
48 using namespace mozilla;
50 /******************************************************************************
51 * nsCacheProfilePrefObserver
52 *****************************************************************************/
53 #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
54 #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
55 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
56 "browser.cache.disk.smart_size.first_run"
57 #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
58 "browser.cache.disk.smart_size.enabled"
59 #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
60 #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
61 #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
62 #define DISK_CACHE_CAPACITY 256000
64 #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
65 "browser.cache.disk.smart_size.use_old_max"
67 #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
68 #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
69 #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
70 #define OFFLINE_CACHE_CAPACITY 512000
72 #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
73 #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
74 #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
76 #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
77 #define CACHE_COMPRESSION_LEVEL 1
79 #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
80 #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
82 static const char * observerList[] = {
83 "profile-before-change",
84 "profile-do-change",
85 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
86 "last-pb-context-exited",
87 "suspend_process_notification",
88 "resume_process_notification"
91 static const char * prefList[] = {
92 DISK_CACHE_ENABLE_PREF,
93 DISK_CACHE_SMART_SIZE_ENABLED_PREF,
94 DISK_CACHE_CAPACITY_PREF,
95 DISK_CACHE_DIR_PREF,
96 DISK_CACHE_MAX_ENTRY_SIZE_PREF,
97 DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
98 OFFLINE_CACHE_ENABLE_PREF,
99 OFFLINE_CACHE_CAPACITY_PREF,
100 OFFLINE_CACHE_DIR_PREF,
101 MEMORY_CACHE_ENABLE_PREF,
102 MEMORY_CACHE_CAPACITY_PREF,
103 MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
104 CACHE_COMPRESSION_LEVEL_PREF,
105 SANITIZE_ON_SHUTDOWN_PREF,
106 CLEAR_ON_SHUTDOWN_PREF
109 // Cache sizes, in KB
110 const int32_t DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB
111 #ifdef ANDROID
112 const int32_t MAX_CACHE_SIZE = 200 * 1024; // 200 MB
113 const int32_t OLD_MAX_CACHE_SIZE = 200 * 1024; // 200 MB
114 #else
115 const int32_t MAX_CACHE_SIZE = 350 * 1024; // 350 MB
116 const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; // 1 GB
117 #endif
118 // Default cache size was 50 MB for many years until FF 4:
119 const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
121 class nsCacheProfilePrefObserver : public nsIObserver
123 virtual ~nsCacheProfilePrefObserver() {}
125 public:
126 NS_DECL_THREADSAFE_ISUPPORTS
127 NS_DECL_NSIOBSERVER
129 nsCacheProfilePrefObserver()
130 : mHaveProfile(false)
131 , mDiskCacheEnabled(false)
132 , mDiskCacheCapacity(0)
133 , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
134 , mSmartSizeEnabled(false)
135 , mShouldUseOldMaxSmartSize(false)
136 , mOfflineCacheEnabled(false)
137 , mOfflineCacheCapacity(0)
138 , mMemoryCacheEnabled(true)
139 , mMemoryCacheCapacity(-1)
140 , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
141 , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
142 , mSanitizeOnShutdown(false)
143 , mClearCacheOnShutdown(false)
147 nsresult Install();
148 void Remove();
149 nsresult ReadPrefs(nsIPrefBranch* branch);
151 bool DiskCacheEnabled();
152 int32_t DiskCacheCapacity() { return mDiskCacheCapacity; }
153 void SetDiskCacheCapacity(int32_t);
154 int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
155 nsIFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
156 bool SmartSizeEnabled() { return mSmartSizeEnabled; }
158 bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize; }
159 void SetUseNewMaxSmartSize(bool useNew) { mShouldUseOldMaxSmartSize = !useNew; }
161 bool OfflineCacheEnabled();
162 int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity; }
163 nsIFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; }
165 bool MemoryCacheEnabled();
166 int32_t MemoryCacheCapacity();
167 int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize; }
169 int32_t CacheCompressionLevel();
171 bool SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
173 static uint32_t GetSmartCacheSize(const nsAString& cachePath,
174 uint32_t currentSize,
175 bool shouldUseOldMaxSmartSize);
177 bool PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
179 private:
180 bool mHaveProfile;
182 bool mDiskCacheEnabled;
183 int32_t mDiskCacheCapacity; // in kilobytes
184 int32_t mDiskCacheMaxEntrySize; // in kilobytes
185 nsCOMPtr<nsIFile> mDiskCacheParentDirectory;
186 bool mSmartSizeEnabled;
188 bool mShouldUseOldMaxSmartSize;
190 bool mOfflineCacheEnabled;
191 int32_t mOfflineCacheCapacity; // in kilobytes
192 nsCOMPtr<nsIFile> mOfflineCacheParentDirectory;
194 bool mMemoryCacheEnabled;
195 int32_t mMemoryCacheCapacity; // in kilobytes
196 int32_t mMemoryCacheMaxEntrySize; // in kilobytes
198 int32_t mCacheCompressionLevel;
200 bool mSanitizeOnShutdown;
201 bool mClearCacheOnShutdown;
204 NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
206 class nsSetDiskSmartSizeCallback MOZ_FINAL : public nsITimerCallback
208 ~nsSetDiskSmartSizeCallback() {}
210 public:
211 NS_DECL_THREADSAFE_ISUPPORTS
213 NS_IMETHOD Notify(nsITimer* aTimer) {
214 if (nsCacheService::gService) {
215 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY));
216 nsCacheService::gService->SetDiskSmartSize_Locked();
217 nsCacheService::gService->mSmartSizeTimer = nullptr;
219 return NS_OK;
223 NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
225 // Runnable sent to main thread after the cache IO thread calculates available
226 // disk space, so that there is no race in setting mDiskCacheCapacity.
227 class nsSetSmartSizeEvent: public nsRunnable
229 public:
230 explicit nsSetSmartSizeEvent(int32_t smartSize)
231 : mSmartSize(smartSize) {}
233 NS_IMETHOD Run()
235 NS_ASSERTION(NS_IsMainThread(),
236 "Setting smart size data off the main thread");
238 // Main thread may have already called nsCacheService::Shutdown
239 if (!nsCacheService::IsInitialized())
240 return NS_ERROR_NOT_AVAILABLE;
242 // Ensure smart sizing wasn't switched off while event was pending.
243 // It is safe to access the observer without the lock since we are
244 // on the main thread and the value changes only on the main thread.
245 if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
246 return NS_OK;
248 nsCacheService::SetDiskCacheCapacity(mSmartSize);
250 nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
251 if (!ps ||
252 NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
253 NS_WARNING("Failed to set smart size pref");
255 return NS_OK;
258 private:
259 int32_t mSmartSize;
263 // Runnable sent from main thread to cacheIO thread
264 class nsGetSmartSizeEvent: public nsRunnable
266 public:
267 nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize,
268 bool shouldUseOldMaxSmartSize)
269 : mCachePath(cachePath)
270 , mCurrentSize(currentSize)
271 , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
274 // Calculates user's disk space available on a background thread and
275 // dispatches this value back to the main thread.
276 NS_IMETHOD Run()
278 uint32_t size;
279 size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,
280 mCurrentSize,
281 mShouldUseOldMaxSmartSize);
282 NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
283 return NS_OK;
286 private:
287 nsString mCachePath;
288 uint32_t mCurrentSize;
289 bool mShouldUseOldMaxSmartSize;
292 class nsBlockOnCacheThreadEvent : public nsRunnable {
293 public:
294 nsBlockOnCacheThreadEvent()
297 NS_IMETHOD Run()
299 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN));
300 #ifdef PR_LOGGING
301 CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
302 #endif
303 nsCacheService::gService->mCondVar.Notify();
304 return NS_OK;
309 nsresult
310 nsCacheProfilePrefObserver::Install()
312 // install profile-change observer
313 nsCOMPtr<nsIObserverService> observerService =
314 mozilla::services::GetObserverService();
315 if (!observerService)
316 return NS_ERROR_FAILURE;
318 nsresult rv, rv2 = NS_OK;
319 for (unsigned int i=0; i<ArrayLength(observerList); i++) {
320 rv = observerService->AddObserver(this, observerList[i], false);
321 if (NS_FAILED(rv))
322 rv2 = rv;
325 // install preferences observer
326 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
327 if (!branch) return NS_ERROR_FAILURE;
329 for (unsigned int i=0; i<ArrayLength(prefList); i++) {
330 rv = branch->AddObserver(prefList[i], this, false);
331 if (NS_FAILED(rv))
332 rv2 = rv;
335 // Determine if we have a profile already
336 // Install() is called *after* the profile-after-change notification
337 // when there is only a single profile, or it is specified on the
338 // commandline at startup.
339 // In that case, we detect the presence of a profile by the existence
340 // of the NS_APP_USER_PROFILE_50_DIR directory.
342 nsCOMPtr<nsIFile> directory;
343 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
344 getter_AddRefs(directory));
345 if (NS_SUCCEEDED(rv))
346 mHaveProfile = true;
348 rv = ReadPrefs(branch);
349 NS_ENSURE_SUCCESS(rv, rv);
351 return rv2;
355 void
356 nsCacheProfilePrefObserver::Remove()
358 // remove Observer Service observers
359 nsCOMPtr<nsIObserverService> obs =
360 mozilla::services::GetObserverService();
361 if (obs) {
362 for (unsigned int i=0; i<ArrayLength(observerList); i++) {
363 obs->RemoveObserver(this, observerList[i]);
367 // remove Pref Service observers
368 nsCOMPtr<nsIPrefBranch> prefs =
369 do_GetService(NS_PREFSERVICE_CONTRACTID);
370 if (!prefs)
371 return;
372 for (unsigned int i=0; i<ArrayLength(prefList); i++)
373 prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
376 void
377 nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
379 mDiskCacheCapacity = std::max(0, capacity);
383 NS_IMETHODIMP
384 nsCacheProfilePrefObserver::Observe(nsISupports * subject,
385 const char * topic,
386 const char16_t * data_unicode)
388 nsresult rv;
389 NS_ConvertUTF16toUTF8 data(data_unicode);
390 CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic, data.get()));
392 if (!nsCacheService::IsInitialized()) {
393 if (!strcmp("resume_process_notification", topic)) {
394 // A suspended process has a closed cache, so re-open it here.
395 nsCacheService::GlobalInstance()->Init();
397 return NS_OK;
400 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
401 // xpcom going away, shutdown cache service
402 nsCacheService::GlobalInstance()->Shutdown();
403 } else if (!strcmp("profile-before-change", topic)) {
404 // profile before change
405 mHaveProfile = false;
407 // XXX shutdown devices
408 nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
409 data.get()));
411 } else if (!strcmp("suspend_process_notification", topic)) {
412 // A suspended process may never return, so shutdown the cache to reduce
413 // cache corruption.
414 nsCacheService::GlobalInstance()->Shutdown();
415 } else if (!strcmp("profile-do-change", topic)) {
416 // profile after change
417 mHaveProfile = true;
418 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
419 ReadPrefs(branch);
420 nsCacheService::OnProfileChanged();
422 } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
424 // ignore pref changes until we're done switch profiles
425 if (!mHaveProfile)
426 return NS_OK;
428 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
429 if (NS_FAILED(rv))
430 return rv;
432 // which preference changed?
433 if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
435 rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
436 &mDiskCacheEnabled);
437 if (NS_FAILED(rv))
438 return rv;
439 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
441 } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
443 int32_t capacity = 0;
444 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
445 if (NS_FAILED(rv))
446 return rv;
447 mDiskCacheCapacity = std::max(0, capacity);
448 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
450 // Update the cache capacity when smart sizing is turned on/off
451 } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
452 // Is the update because smartsizing was turned on, or off?
453 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
454 &mSmartSizeEnabled);
455 if (NS_FAILED(rv))
456 return rv;
457 int32_t newCapacity = 0;
458 if (mSmartSizeEnabled) {
459 nsCacheService::SetDiskSmartSize();
460 } else {
461 // Smart sizing switched off: use user specified size
462 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
463 if (NS_FAILED(rv))
464 return rv;
465 mDiskCacheCapacity = std::max(0, newCapacity);
466 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
468 } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
469 rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
470 &mShouldUseOldMaxSmartSize);
471 if (NS_FAILED(rv))
472 return rv;
473 } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
474 int32_t newMaxSize;
475 rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
476 &newMaxSize);
477 if (NS_FAILED(rv))
478 return rv;
480 mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
481 nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
483 #if 0
484 } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
485 // XXX We probaby don't want to respond to this pref except after
486 // XXX profile changes. Ideally, there should be somekind of user
487 // XXX notification that the pref change won't take effect until
488 // XXX the next time the profile changes (browser launch)
489 #endif
490 } else
492 // which preference changed?
493 if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
495 rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
496 &mOfflineCacheEnabled);
497 if (NS_FAILED(rv)) return rv;
498 nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
500 } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
502 int32_t capacity = 0;
503 rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
504 if (NS_FAILED(rv)) return rv;
505 mOfflineCacheCapacity = std::max(0, capacity);
506 nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
507 #if 0
508 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
509 // XXX We probaby don't want to respond to this pref except after
510 // XXX profile changes. Ideally, there should be some kind of user
511 // XXX notification that the pref change won't take effect until
512 // XXX the next time the profile changes (browser launch)
513 #endif
514 } else
516 if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
518 rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
519 &mMemoryCacheEnabled);
520 if (NS_FAILED(rv))
521 return rv;
522 nsCacheService::SetMemoryCache();
524 } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
526 mMemoryCacheCapacity = -1;
527 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
528 &mMemoryCacheCapacity);
529 nsCacheService::SetMemoryCache();
530 } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
531 int32_t newMaxSize;
532 rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
533 &newMaxSize);
534 if (NS_FAILED(rv))
535 return rv;
537 mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
538 nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
539 } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
540 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
541 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
542 &mCacheCompressionLevel);
543 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
544 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
545 } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
546 rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
547 &mSanitizeOnShutdown);
548 if (NS_FAILED(rv))
549 return rv;
550 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
551 } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
552 rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
553 &mClearCacheOnShutdown);
554 if (NS_FAILED(rv))
555 return rv;
556 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
558 } else if (!strcmp("last-pb-context-exited", topic)) {
559 nsCacheService::LeavePrivateBrowsing();
562 return NS_OK;
565 // Returns default ("smart") size (in KB) of cache, given available disk space
566 // (also in KB)
567 static uint32_t
568 SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
570 uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
572 if (availKB > 100 * 1024 * 1024)
573 return maxSize; // skip computing if we're over 100 GB
575 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
576 // don't shrink cache and evict items every time we startup (it's important
577 // that we don't slow down startup benchmarks).
578 uint32_t sz10MBs = 0;
579 uint32_t avail10MBs = availKB / (1024*10);
581 // .5% of space above 25 GB
582 if (avail10MBs > 2500) {
583 sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
584 avail10MBs = 2500;
586 // 1% of space between 7GB -> 25 GB
587 if (avail10MBs > 700) {
588 sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
589 avail10MBs = 700;
591 // 5% of space between 500 MB -> 7 GB
592 if (avail10MBs > 50) {
593 sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
594 avail10MBs = 50;
597 #ifdef ANDROID
598 // On Android, smaller/older devices may have very little storage and
599 // device owners may be sensitive to storage footprint: Use a smaller
600 // percentage of available space and a smaller minimum.
602 // 20% of space up to 500 MB (10 MB min)
603 sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
604 #else
605 // 40% of space up to 500 MB (50 MB min)
606 sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
607 #endif
609 return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
612 /* Computes our best guess for the default size of the user's disk cache,
613 * based on the amount of space they have free on their hard drive.
614 * We use a tiered scheme: the more space available,
615 * the larger the disk cache will be. However, we do not want
616 * to enable the disk cache to grow to an unbounded size, so the larger the
617 * user's available space is, the smaller of a percentage we take. We set a
618 * lower bound of 50MB and an upper bound of 1GB.
620 *@param: None.
621 *@return: The size that the user's disk cache should default to, in kBytes.
623 uint32_t
624 nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
625 uint32_t currentSize,
626 bool shouldUseOldMaxSmartSize)
628 // Check for free space on device where cache directory lives
629 nsresult rv;
630 nsCOMPtr<nsIFile>
631 cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
632 if (NS_FAILED(rv) || !cacheDirectory)
633 return DEFAULT_CACHE_SIZE;
634 rv = cacheDirectory->InitWithPath(cachePath);
635 if (NS_FAILED(rv))
636 return DEFAULT_CACHE_SIZE;
637 int64_t bytesAvailable;
638 rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
639 if (NS_FAILED(rv))
640 return DEFAULT_CACHE_SIZE;
642 return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
643 currentSize),
644 shouldUseOldMaxSmartSize);
647 /* Determine if we are permitted to dynamically size the user's disk cache based
648 * on their disk space available. We may do this so long as the pref
649 * smart_size.enabled is true.
651 bool
652 nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
653 firstRun)
655 nsresult rv;
656 if (firstRun) {
657 // check if user has set cache size in the past
658 bool userSet;
659 rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
660 if (NS_FAILED(rv)) userSet = true;
661 if (userSet) {
662 int32_t oldCapacity;
663 // If user explicitly set cache size to be smaller than old default
664 // of 50 MB, then keep user's value. Otherwise use smart sizing.
665 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
666 if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
667 mSmartSizeEnabled = false;
668 branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
669 mSmartSizeEnabled);
670 return mSmartSizeEnabled;
673 // Set manual setting to MAX cache size as starting val for any
674 // adjustment by user: (bug 559942 comment 65)
675 int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
676 branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
679 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
680 &mSmartSizeEnabled);
681 if (NS_FAILED(rv))
682 mSmartSizeEnabled = false;
683 return mSmartSizeEnabled;
687 nsresult
688 nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
690 nsresult rv = NS_OK;
692 // read disk cache device prefs
693 mDiskCacheEnabled = true; // presume disk cache is enabled
694 (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
696 mDiskCacheCapacity = DISK_CACHE_CAPACITY;
697 (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
698 mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
700 (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
701 &mDiskCacheMaxEntrySize);
702 mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
704 (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF, // ignore error
705 NS_GET_IID(nsIFile),
706 getter_AddRefs(mDiskCacheParentDirectory));
708 (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
709 &mShouldUseOldMaxSmartSize);
711 if (!mDiskCacheParentDirectory) {
712 nsCOMPtr<nsIFile> directory;
714 // try to get the disk cache parent directory
715 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
716 getter_AddRefs(directory));
717 if (NS_FAILED(rv)) {
718 // try to get the profile directory (there may not be a profile yet)
719 nsCOMPtr<nsIFile> profDir;
720 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
721 getter_AddRefs(profDir));
722 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
723 getter_AddRefs(directory));
724 if (!directory)
725 directory = profDir;
726 else if (profDir) {
727 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
728 "Cache");
731 // use file cache in build tree only if asked, to avoid cache dir litter
732 if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
733 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
734 getter_AddRefs(directory));
736 if (directory)
737 mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
739 if (mDiskCacheParentDirectory) {
740 bool firstSmartSizeRun;
741 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
742 &firstSmartSizeRun);
743 if (NS_FAILED(rv))
744 firstSmartSizeRun = false;
745 if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
746 // Avoid evictions: use previous cache size until smart size event
747 // updates mDiskCacheCapacity
748 rv = branch->GetIntPref(firstSmartSizeRun ?
749 DISK_CACHE_CAPACITY_PREF :
750 DISK_CACHE_SMART_SIZE_PREF,
751 &mDiskCacheCapacity);
752 if (NS_FAILED(rv))
753 mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
756 if (firstSmartSizeRun) {
757 // It is no longer our first run
758 rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
759 false);
760 if (NS_FAILED(rv))
761 NS_WARNING("Failed setting first_run pref in ReadPrefs.");
765 // read offline cache device prefs
766 mOfflineCacheEnabled = true; // presume offline cache is enabled
767 (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
768 &mOfflineCacheEnabled);
770 mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
771 (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
772 &mOfflineCacheCapacity);
773 mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
775 (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error
776 NS_GET_IID(nsIFile),
777 getter_AddRefs(mOfflineCacheParentDirectory));
779 if (!mOfflineCacheParentDirectory) {
780 nsCOMPtr<nsIFile> directory;
782 // try to get the offline cache parent directory
783 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
784 getter_AddRefs(directory));
785 if (NS_FAILED(rv)) {
786 // try to get the profile directory (there may not be a profile yet)
787 nsCOMPtr<nsIFile> profDir;
788 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
789 getter_AddRefs(profDir));
790 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
791 getter_AddRefs(directory));
792 if (!directory)
793 directory = profDir;
794 else if (profDir) {
795 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
796 "OfflineCache");
799 #if DEBUG
800 if (!directory) {
801 // use current process directory during development
802 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
803 getter_AddRefs(directory));
805 #endif
806 if (directory)
807 mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
810 // read memory cache device prefs
811 (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
813 mMemoryCacheCapacity = -1;
814 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
815 &mMemoryCacheCapacity);
817 (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
818 &mMemoryCacheMaxEntrySize);
819 mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
821 // read cache compression level pref
822 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
823 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
824 &mCacheCompressionLevel);
825 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
826 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
828 // read cache shutdown sanitization prefs
829 (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
830 &mSanitizeOnShutdown);
831 (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
832 &mClearCacheOnShutdown);
834 return rv;
837 nsresult
838 nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
840 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
841 return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
844 nsresult
845 nsCacheService::SyncWithCacheIOThread()
847 gService->mLock.AssertCurrentThreadOwns();
848 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
850 nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
852 // dispatch event - it will notify the monitor when it's done
853 nsresult rv =
854 gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
855 if (NS_FAILED(rv)) {
856 NS_WARNING("Failed dispatching block-event");
857 return NS_ERROR_UNEXPECTED;
860 // wait until notified, then return
861 rv = gService->mCondVar.Wait();
863 return rv;
867 bool
868 nsCacheProfilePrefObserver::DiskCacheEnabled()
870 if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory)) return false;
871 return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
875 bool
876 nsCacheProfilePrefObserver::OfflineCacheEnabled()
878 if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
879 return false;
881 return mOfflineCacheEnabled;
885 bool
886 nsCacheProfilePrefObserver::MemoryCacheEnabled()
888 if (mMemoryCacheCapacity == 0) return false;
889 return mMemoryCacheEnabled;
894 * MemoryCacheCapacity
896 * If the browser.cache.memory.capacity preference is positive, we use that
897 * value for the amount of memory available for the cache.
899 * If browser.cache.memory.capacity is zero, the memory cache is disabled.
901 * If browser.cache.memory.capacity is negative or not present, we use a
902 * formula that grows less than linearly with the amount of system memory,
903 * with an upper limit on the cache size. No matter how much physical RAM is
904 * present, the default cache size would not exceed 32 MB. This maximum would
905 * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
907 * RAM Cache
908 * --- -----
909 * 32 Mb 2 Mb
910 * 64 Mb 4 Mb
911 * 128 Mb 6 Mb
912 * 256 Mb 10 Mb
913 * 512 Mb 14 Mb
914 * 1024 Mb 18 Mb
915 * 2048 Mb 24 Mb
916 * 4096 Mb 30 Mb
918 * The equation for this is (for cache size C and memory size K (kbytes)):
919 * x = log2(K) - 14
920 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
921 * if (C > 32) C = 32
924 int32_t
925 nsCacheProfilePrefObserver::MemoryCacheCapacity()
927 int32_t capacity = mMemoryCacheCapacity;
928 if (capacity >= 0) {
929 CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
930 return capacity;
933 static uint64_t bytes = PR_GetPhysicalMemorySize();
934 CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
936 // If getting the physical memory failed, arbitrarily assume
937 // 32 MB of RAM. We use a low default to have a reasonable
938 // size on all the devices we support.
939 if (bytes == 0)
940 bytes = 32 * 1024 * 1024;
942 // Conversion from unsigned int64_t to double doesn't work on all platforms.
943 // We need to truncate the value at INT64_MAX to make sure we don't
944 // overflow.
945 if (bytes > INT64_MAX)
946 bytes = INT64_MAX;
948 uint64_t kbytes = bytes >> 10;
950 double kBytesD = double(kbytes);
952 double x = log(kBytesD)/log(2.0) - 14;
953 if (x > 0) {
954 capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
955 if (capacity > 32)
956 capacity = 32;
957 capacity *= 1024;
958 } else {
959 capacity = 0;
962 return capacity;
965 int32_t
966 nsCacheProfilePrefObserver::CacheCompressionLevel()
968 return mCacheCompressionLevel;
971 /******************************************************************************
972 * nsProcessRequestEvent
973 *****************************************************************************/
975 class nsProcessRequestEvent : public nsRunnable {
976 public:
977 explicit nsProcessRequestEvent(nsCacheRequest *aRequest)
979 MOZ_EVENT_TRACER_NAME_OBJECT(aRequest, aRequest->mKey.get());
980 MOZ_EVENT_TRACER_WAIT(aRequest, "net::cache::ProcessRequest");
981 mRequest = aRequest;
984 NS_IMETHOD Run()
986 nsresult rv;
988 NS_ASSERTION(mRequest->mListener,
989 "Sync OpenCacheEntry() posted to background thread!");
991 nsCacheServiceAutoLock lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN));
992 rv = nsCacheService::gService->ProcessRequest(mRequest,
993 false,
994 nullptr);
996 // Don't delete the request if it was queued
997 if (!(mRequest->IsBlocking() &&
998 rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
999 delete mRequest;
1001 return NS_OK;
1004 protected:
1005 virtual ~nsProcessRequestEvent() {}
1007 private:
1008 nsCacheRequest *mRequest;
1011 /******************************************************************************
1012 * nsDoomEvent
1013 *****************************************************************************/
1015 class nsDoomEvent : public nsRunnable {
1016 public:
1017 nsDoomEvent(nsCacheSession *session,
1018 const nsACString &key,
1019 nsICacheListener *listener)
1021 mKey = *session->ClientID();
1022 mKey.Append(':');
1023 mKey.Append(key);
1024 mStoragePolicy = session->StoragePolicy();
1025 mListener = listener;
1026 mThread = do_GetCurrentThread();
1027 // We addref the listener here and release it in nsNotifyDoomListener
1028 // on the callers thread. If posting of nsNotifyDoomListener event fails
1029 // we leak the listener which is better than releasing it on a wrong
1030 // thread.
1031 NS_IF_ADDREF(mListener);
1034 NS_IMETHOD Run()
1036 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDOOMEVENT_RUN));
1038 bool foundActive = true;
1039 nsresult status = NS_ERROR_NOT_AVAILABLE;
1040 nsCacheEntry *entry;
1041 entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
1042 if (!entry) {
1043 bool collision = false;
1044 foundActive = false;
1045 entry = nsCacheService::gService->SearchCacheDevices(&mKey,
1046 mStoragePolicy,
1047 &collision);
1050 if (entry) {
1051 status = NS_OK;
1052 nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
1055 if (mListener) {
1056 mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
1057 NS_DISPATCH_NORMAL);
1058 // posted event will release the reference on the correct thread
1059 mListener = nullptr;
1062 return NS_OK;
1065 private:
1066 nsCString mKey;
1067 nsCacheStoragePolicy mStoragePolicy;
1068 nsICacheListener *mListener;
1069 nsCOMPtr<nsIThread> mThread;
1072 /******************************************************************************
1073 * nsCacheService
1074 *****************************************************************************/
1075 nsCacheService * nsCacheService::gService = nullptr;
1077 NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
1078 nsIMemoryReporter)
1080 nsCacheService::nsCacheService()
1081 : mObserver(nullptr),
1082 mLock("nsCacheService.mLock"),
1083 mCondVar(mLock, "nsCacheService.mCondVar"),
1084 mTimeStampLock("nsCacheService.mTimeStampLock"),
1085 mInitialized(false),
1086 mClearingEntries(false),
1087 mEnableMemoryDevice(true),
1088 mEnableDiskDevice(true),
1089 mMemoryDevice(nullptr),
1090 mDiskDevice(nullptr),
1091 mOfflineDevice(nullptr),
1092 mTotalEntries(0),
1093 mCacheHits(0),
1094 mCacheMisses(0),
1095 mMaxKeyLength(0),
1096 mMaxDataSize(0),
1097 mMaxMetaSize(0),
1098 mDeactivateFailures(0),
1099 mDeactivatedUnboundEntries(0)
1101 NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
1102 gService = this;
1104 // create list of cache devices
1105 PR_INIT_CLIST(&mDoomedEntries);
1108 nsCacheService::~nsCacheService()
1110 if (mInitialized) // Shutdown hasn't been called yet.
1111 (void) Shutdown();
1113 if (mObserver) {
1114 mObserver->Remove();
1115 NS_RELEASE(mObserver);
1118 gService = nullptr;
1122 nsresult
1123 nsCacheService::Init()
1125 // Thie method must be called on the main thread because mCacheIOThread must
1126 // only be modified on the main thread.
1127 if (!NS_IsMainThread()) {
1128 NS_ERROR("nsCacheService::Init called off the main thread");
1129 return NS_ERROR_NOT_SAME_THREAD;
1132 NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
1133 if (mInitialized)
1134 return NS_ERROR_ALREADY_INITIALIZED;
1136 if (mozilla::net::IsNeckoChild()) {
1137 return NS_ERROR_UNEXPECTED;
1140 CACHE_LOG_INIT();
1142 nsresult rv;
1144 mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
1145 NS_ENSURE_SUCCESS(rv, rv);
1147 MOZ_EVENT_TRACER_NAME_OBJECT(nsCacheService::gService, "nsCacheService");
1149 rv = NS_NewNamedThread("Cache I/O",
1150 getter_AddRefs(mCacheIOThread));
1151 if (NS_FAILED(rv)) {
1152 NS_RUNTIMEABORT("Can't create cache IO thread");
1155 rv = nsDeleteDir::Init();
1156 if (NS_FAILED(rv)) {
1157 NS_WARNING("Can't initialize nsDeleteDir");
1160 // initialize hashtable for active cache entries
1161 rv = mActiveEntries.Init();
1162 if (NS_FAILED(rv)) return rv;
1164 // create profile/preference observer
1165 if (!mObserver) {
1166 mObserver = new nsCacheProfilePrefObserver();
1167 NS_ADDREF(mObserver);
1168 mObserver->Install();
1171 mEnableDiskDevice = mObserver->DiskCacheEnabled();
1172 mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
1173 mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
1175 RegisterWeakMemoryReporter(this);
1177 mInitialized = true;
1178 return NS_OK;
1181 // static
1182 PLDHashOperator
1183 nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir,
1184 nsRefPtr<nsOfflineCacheDevice>& aDevice,
1185 void* aUserArg)
1187 aDevice->Shutdown();
1188 return PL_DHASH_REMOVE;
1191 void
1192 nsCacheService::Shutdown()
1194 // This method must be called on the main thread because mCacheIOThread must
1195 // only be modified on the main thread.
1196 if (!NS_IsMainThread()) {
1197 NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
1200 nsCOMPtr<nsIThread> cacheIOThread;
1201 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer;
1203 bool shouldSanitize = false;
1204 nsCOMPtr<nsIFile> parentDir;
1207 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
1208 NS_ASSERTION(mInitialized,
1209 "can't shutdown nsCacheService unless it has been initialized.");
1210 if (!mInitialized)
1211 return;
1213 mClearingEntries = true;
1214 DoomActiveEntries(nullptr);
1217 CloseAllStreams();
1219 UnregisterWeakMemoryReporter(this);
1222 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
1223 NS_ASSERTION(mInitialized, "Bad state");
1225 mInitialized = false;
1227 // Clear entries
1228 ClearDoomList();
1230 if (mSmartSizeTimer) {
1231 mSmartSizeTimer->Cancel();
1232 mSmartSizeTimer = nullptr;
1235 // Make sure to wait for any pending cache-operations before
1236 // proceeding with destructive actions (bug #620660)
1237 (void) SyncWithCacheIOThread();
1239 // obtain the disk cache directory in case we need to sanitize it
1240 parentDir = mObserver->DiskCacheParentDirectory();
1241 shouldSanitize = mObserver->SanitizeAtShutdown();
1243 // deallocate memory and disk caches
1244 delete mMemoryDevice;
1245 mMemoryDevice = nullptr;
1247 delete mDiskDevice;
1248 mDiskDevice = nullptr;
1250 if (mOfflineDevice)
1251 mOfflineDevice->Shutdown();
1253 NS_IF_RELEASE(mOfflineDevice);
1255 mCustomOfflineDevices.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
1257 #ifdef PR_LOGGING
1258 LogCacheStatistics();
1259 #endif
1261 mClearingEntries = false;
1262 mCacheIOThread.swap(cacheIOThread);
1265 if (cacheIOThread)
1266 nsShutdownThread::BlockingShutdown(cacheIOThread);
1268 if (shouldSanitize) {
1269 nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
1270 if (NS_SUCCEEDED(rv)) {
1271 bool exists;
1272 if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
1273 nsDeleteDir::DeleteDir(parentDir, false);
1275 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer;
1276 nsDeleteDir::Shutdown(shouldSanitize);
1277 } else {
1278 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer;
1279 nsDeleteDir::Shutdown(shouldSanitize);
1284 nsresult
1285 nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
1287 nsresult rv;
1289 if (aOuter != nullptr)
1290 return NS_ERROR_NO_AGGREGATION;
1292 nsCacheService * cacheService = new nsCacheService();
1293 if (cacheService == nullptr)
1294 return NS_ERROR_OUT_OF_MEMORY;
1296 NS_ADDREF(cacheService);
1297 rv = cacheService->Init();
1298 if (NS_SUCCEEDED(rv)) {
1299 rv = cacheService->QueryInterface(aIID, aResult);
1301 NS_RELEASE(cacheService);
1302 return rv;
1306 NS_IMETHODIMP
1307 nsCacheService::CreateSession(const char * clientID,
1308 nsCacheStoragePolicy storagePolicy,
1309 bool streamBased,
1310 nsICacheSession **result)
1312 *result = nullptr;
1314 if (net::CacheObserver::UseNewCache())
1315 return NS_ERROR_NOT_IMPLEMENTED;
1317 return CreateSessionInternal(clientID, storagePolicy, streamBased, result);
1320 nsresult
1321 nsCacheService::CreateSessionInternal(const char * clientID,
1322 nsCacheStoragePolicy storagePolicy,
1323 bool streamBased,
1324 nsICacheSession **result)
1326 if (this == nullptr) return NS_ERROR_NOT_AVAILABLE;
1328 nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased);
1329 if (!session) return NS_ERROR_OUT_OF_MEMORY;
1331 NS_ADDREF(*result = session);
1333 return NS_OK;
1337 nsresult
1338 nsCacheService::EvictEntriesForSession(nsCacheSession * session)
1340 NS_ASSERTION(gService, "nsCacheService::gService is null.");
1341 return gService->EvictEntriesForClient(session->ClientID()->get(),
1342 session->StoragePolicy());
1345 namespace {
1347 class EvictionNotifierRunnable : public nsRunnable
1349 public:
1350 explicit EvictionNotifierRunnable(nsISupports* aSubject)
1351 : mSubject(aSubject)
1354 NS_DECL_NSIRUNNABLE
1356 private:
1357 nsCOMPtr<nsISupports> mSubject;
1360 NS_IMETHODIMP
1361 EvictionNotifierRunnable::Run()
1363 nsCOMPtr<nsIObserverService> obsSvc =
1364 mozilla::services::GetObserverService();
1365 if (obsSvc) {
1366 obsSvc->NotifyObservers(mSubject,
1367 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
1368 nullptr);
1370 return NS_OK;
1373 } // anonymous namespace
1375 nsresult
1376 nsCacheService::EvictEntriesForClient(const char * clientID,
1377 nsCacheStoragePolicy storagePolicy)
1379 nsRefPtr<EvictionNotifierRunnable> r =
1380 new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
1381 NS_DispatchToMainThread(r);
1383 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
1384 nsresult res = NS_OK;
1386 if (storagePolicy == nsICache::STORE_ANYWHERE ||
1387 storagePolicy == nsICache::STORE_ON_DISK) {
1389 if (mEnableDiskDevice) {
1390 nsresult rv = NS_OK;
1391 if (!mDiskDevice)
1392 rv = CreateDiskDevice();
1393 if (mDiskDevice)
1394 rv = mDiskDevice->EvictEntries(clientID);
1395 if (NS_FAILED(rv))
1396 res = rv;
1400 // Only clear the offline cache if it has been specifically asked for.
1401 if (storagePolicy == nsICache::STORE_OFFLINE) {
1402 if (mEnableOfflineDevice) {
1403 nsresult rv = NS_OK;
1404 if (!mOfflineDevice)
1405 rv = CreateOfflineDevice();
1406 if (mOfflineDevice)
1407 rv = mOfflineDevice->EvictEntries(clientID);
1408 if (NS_FAILED(rv))
1409 res = rv;
1413 if (storagePolicy == nsICache::STORE_ANYWHERE ||
1414 storagePolicy == nsICache::STORE_IN_MEMORY) {
1415 // If there is no memory device, there is no need to evict it...
1416 if (mMemoryDevice) {
1417 nsresult rv = mMemoryDevice->EvictEntries(clientID);
1418 if (NS_FAILED(rv))
1419 res = rv;
1423 return res;
1427 nsresult
1428 nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
1429 bool * result)
1431 if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
1432 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY));
1434 *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
1435 return NS_OK;
1439 nsresult
1440 nsCacheService::DoomEntry(nsCacheSession *session,
1441 const nsACString &key,
1442 nsICacheListener *listener)
1444 CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
1445 session, PromiseFlatCString(key).get()));
1446 NS_ASSERTION(gService, "nsCacheService::gService is null.");
1448 if (!gService->mInitialized)
1449 return NS_ERROR_NOT_INITIALIZED;
1451 return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
1455 bool
1456 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
1458 if (gService->mEnableMemoryDevice &&
1459 (storagePolicy == nsICache::STORE_ANYWHERE ||
1460 storagePolicy == nsICache::STORE_IN_MEMORY)) {
1461 return true;
1463 if (gService->mEnableDiskDevice &&
1464 (storagePolicy == nsICache::STORE_ANYWHERE ||
1465 storagePolicy == nsICache::STORE_ON_DISK)) {
1466 return true;
1468 if (gService->mEnableOfflineDevice &&
1469 storagePolicy == nsICache::STORE_OFFLINE) {
1470 return true;
1473 return false;
1476 NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
1478 if (net::CacheObserver::UseNewCache())
1479 return NS_ERROR_NOT_IMPLEMENTED;
1481 return VisitEntriesInternal(visitor);
1484 nsresult nsCacheService::VisitEntriesInternal(nsICacheVisitor *visitor)
1486 NS_ENSURE_ARG_POINTER(visitor);
1488 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES));
1490 if (!(mEnableDiskDevice || mEnableMemoryDevice))
1491 return NS_ERROR_NOT_AVAILABLE;
1493 // XXX record the fact that a visitation is in progress,
1494 // XXX i.e. keep list of visitors in progress.
1496 nsresult rv = NS_OK;
1497 // If there is no memory device, there are then also no entries to visit...
1498 if (mMemoryDevice) {
1499 rv = mMemoryDevice->Visit(visitor);
1500 if (NS_FAILED(rv)) return rv;
1503 if (mEnableDiskDevice) {
1504 if (!mDiskDevice) {
1505 rv = CreateDiskDevice();
1506 if (NS_FAILED(rv)) return rv;
1508 rv = mDiskDevice->Visit(visitor);
1509 if (NS_FAILED(rv)) return rv;
1512 if (mEnableOfflineDevice) {
1513 if (!mOfflineDevice) {
1514 rv = CreateOfflineDevice();
1515 if (NS_FAILED(rv)) return rv;
1517 rv = mOfflineDevice->Visit(visitor);
1518 if (NS_FAILED(rv)) return rv;
1521 // XXX notify any shutdown process that visitation is complete for THIS visitor.
1522 // XXX keep queue of visitors
1524 return NS_OK;
1527 void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
1529 MOZ_ASSERT(NS_IsMainThread());
1530 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
1531 if (obsvc) {
1532 obsvc->NotifyObservers(nullptr,
1533 "network-clear-cache-stored-anywhere",
1534 nullptr);
1538 NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
1540 if (net::CacheObserver::UseNewCache())
1541 return NS_ERROR_NOT_IMPLEMENTED;
1543 return EvictEntriesInternal(storagePolicy);
1546 nsresult nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy)
1548 if (storagePolicy == nsICache::STORE_ANYWHERE) {
1549 // if not called on main thread, dispatch the notification to the main thread to notify observers
1550 if (!NS_IsMainThread()) {
1551 nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this,
1552 &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
1553 NS_DispatchToMainThread(event);
1554 } else {
1555 // else you're already on main thread - notify observers
1556 FireClearNetworkCacheStoredAnywhereNotification();
1559 return EvictEntriesForClient(nullptr, storagePolicy);
1562 NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
1564 NS_ENSURE_ARG_POINTER(aCacheIOTarget);
1566 // Because mCacheIOThread can only be changed on the main thread, it can be
1567 // read from the main thread without the lock. This is useful to prevent
1568 // blocking the main thread on other cache operations.
1569 if (!NS_IsMainThread()) {
1570 Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET));
1573 nsresult rv;
1574 if (mCacheIOThread) {
1575 NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
1576 rv = NS_OK;
1577 } else {
1578 *aCacheIOTarget = nullptr;
1579 rv = NS_ERROR_NOT_AVAILABLE;
1582 if (!NS_IsMainThread()) {
1583 Unlock();
1586 return rv;
1589 /* nsICacheServiceInternal
1590 * readonly attribute double lockHeldTime;
1592 NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
1594 MutexAutoLock lock(mTimeStampLock);
1596 if (mLockAcquiredTimeStamp.IsNull()) {
1597 *aLockHeldTime = 0.0;
1599 else {
1600 *aLockHeldTime =
1601 (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
1604 return NS_OK;
1608 * Internal Methods
1610 nsresult
1611 nsCacheService::CreateDiskDevice()
1613 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
1614 if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
1615 if (mDiskDevice) return NS_OK;
1617 mDiskDevice = new nsDiskCacheDevice;
1618 if (!mDiskDevice) return NS_ERROR_OUT_OF_MEMORY;
1620 // set the preferences
1621 mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
1622 mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
1623 mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
1625 nsresult rv = mDiskDevice->Init();
1626 if (NS_FAILED(rv)) {
1627 #if DEBUG
1628 printf("###\n");
1629 printf("### mDiskDevice->Init() failed (0x%.8x)\n",
1630 static_cast<uint32_t>(rv));
1631 printf("### - disabling disk cache for this session.\n");
1632 printf("###\n");
1633 #endif
1634 mEnableDiskDevice = false;
1635 delete mDiskDevice;
1636 mDiskDevice = nullptr;
1637 return rv;
1640 Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX,
1641 mObserver->ShouldUseOldMaxSmartSize());
1643 NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
1645 // Disk device is usually created during the startup. Delay smart size
1646 // calculation to avoid possible massive IO caused by eviction of entries
1647 // in case the new smart size is smaller than current cache usage.
1648 mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1649 if (NS_SUCCEEDED(rv)) {
1650 rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
1651 1000*60*3,
1652 nsITimer::TYPE_ONE_SHOT);
1653 if (NS_FAILED(rv)) {
1654 NS_WARNING("Failed to post smart size timer");
1655 mSmartSizeTimer = nullptr;
1657 } else {
1658 NS_WARNING("Can't create smart size timer");
1660 // Ignore state of the timer and return success since the purpose of the
1661 // method (create the disk-device) has been fulfilled
1663 return NS_OK;
1666 // Runnable sent from cache thread to main thread
1667 class nsDisableOldMaxSmartSizePrefEvent: public nsRunnable
1669 public:
1670 nsDisableOldMaxSmartSizePrefEvent() {}
1672 NS_IMETHOD Run()
1674 // Main thread may have already called nsCacheService::Shutdown
1675 if (!nsCacheService::IsInitialized())
1676 return NS_ERROR_NOT_AVAILABLE;
1678 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
1679 if (!branch) {
1680 return NS_ERROR_NOT_AVAILABLE;
1683 nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
1684 if (NS_FAILED(rv)) {
1685 NS_WARNING("Failed to disable old max smart size");
1686 return rv;
1689 // It is safe to call SetDiskSmartSize_Locked() without holding the lock
1690 // when we are on main thread and nsCacheService is initialized.
1691 nsCacheService::gService->SetDiskSmartSize_Locked();
1693 if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) {
1694 rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
1695 if (NS_FAILED(rv)) {
1696 NS_WARNING("Failed to set cache capacity pref");
1700 return NS_OK;
1704 void
1705 nsCacheService::MarkStartingFresh()
1707 if (!gService->mObserver->ShouldUseOldMaxSmartSize()) {
1708 // Already using new max, nothing to do here
1709 return;
1712 gService->mObserver->SetUseNewMaxSmartSize(true);
1714 // We always dispatch an event here because we don't want to deal with lock
1715 // reentrance issues.
1716 NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
1719 nsresult
1720 nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
1722 if (!mOfflineDevice) {
1723 nsresult rv = CreateOfflineDevice();
1724 NS_ENSURE_SUCCESS(rv, rv);
1727 NS_ADDREF(*aDevice = mOfflineDevice);
1728 return NS_OK;
1731 nsresult
1732 nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
1733 int32_t aQuota,
1734 nsOfflineCacheDevice **aDevice)
1736 nsresult rv;
1738 nsAutoString profilePath;
1739 rv = aProfileDir->GetPath(profilePath);
1740 NS_ENSURE_SUCCESS(rv, rv);
1742 if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
1743 rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
1744 NS_ENSURE_SUCCESS(rv, rv);
1746 (*aDevice)->SetAutoShutdown();
1747 mCustomOfflineDevices.Put(profilePath, *aDevice);
1750 return NS_OK;
1753 nsresult
1754 nsCacheService::CreateOfflineDevice()
1756 CACHE_LOG_ALWAYS(("Creating default offline device"));
1758 if (mOfflineDevice) return NS_OK;
1759 if (!nsCacheService::IsInitialized()) {
1760 return NS_ERROR_NOT_AVAILABLE;
1763 nsresult rv = CreateCustomOfflineDevice(
1764 mObserver->OfflineCacheParentDirectory(),
1765 mObserver->OfflineCacheCapacity(),
1766 &mOfflineDevice);
1767 NS_ENSURE_SUCCESS(rv, rv);
1769 return NS_OK;
1772 nsresult
1773 nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
1774 int32_t aQuota,
1775 nsOfflineCacheDevice **aDevice)
1777 NS_ENSURE_ARG(aProfileDir);
1779 #if defined(PR_LOGGING)
1780 nsAutoCString profilePath;
1781 aProfileDir->GetNativePath(profilePath);
1782 CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
1783 profilePath.BeginReading(), aQuota));
1784 #endif
1786 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
1787 if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
1789 *aDevice = new nsOfflineCacheDevice;
1791 NS_ADDREF(*aDevice);
1793 // set the preferences
1794 (*aDevice)->SetCacheParentDirectory(aProfileDir);
1795 (*aDevice)->SetCapacity(aQuota);
1797 nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
1798 if (NS_FAILED(rv)) {
1799 CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv));
1800 CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
1802 NS_RELEASE(*aDevice);
1804 return rv;
1807 nsresult
1808 nsCacheService::CreateMemoryDevice()
1810 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
1811 if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
1812 if (mMemoryDevice) return NS_OK;
1814 mMemoryDevice = new nsMemoryCacheDevice;
1815 if (!mMemoryDevice) return NS_ERROR_OUT_OF_MEMORY;
1817 // set preference
1818 int32_t capacity = mObserver->MemoryCacheCapacity();
1819 CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
1820 mMemoryDevice->SetCapacity(capacity);
1821 mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
1823 nsresult rv = mMemoryDevice->Init();
1824 if (NS_FAILED(rv)) {
1825 NS_WARNING("Initialization of Memory Cache failed.");
1826 delete mMemoryDevice;
1827 mMemoryDevice = nullptr;
1830 return rv;
1833 nsresult
1834 nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
1836 nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
1837 if (!profileDir)
1838 return NS_ERROR_UNEXPECTED;
1840 nsAutoString profilePath;
1841 nsresult rv = profileDir->GetPath(profilePath);
1842 NS_ENSURE_SUCCESS(rv, rv);
1844 mCustomOfflineDevices.Remove(profilePath);
1845 return NS_OK;
1848 nsresult
1849 nsCacheService::CreateRequest(nsCacheSession * session,
1850 const nsACString & clientKey,
1851 nsCacheAccessMode accessRequested,
1852 bool blockingMode,
1853 nsICacheListener * listener,
1854 nsCacheRequest ** request)
1856 NS_ASSERTION(request, "CreateRequest: request is null");
1858 nsAutoCString key(*session->ClientID());
1859 key.Append(':');
1860 key.Append(clientKey);
1862 if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
1864 // create request
1865 *request = new nsCacheRequest(key, listener, accessRequested,
1866 blockingMode, session);
1868 if (!listener) return NS_OK; // we're sync, we're done.
1870 // get the request's thread
1871 (*request)->mThread = do_GetCurrentThread();
1873 return NS_OK;
1877 class nsCacheListenerEvent : public nsRunnable
1879 public:
1880 nsCacheListenerEvent(nsICacheListener *listener,
1881 nsICacheEntryDescriptor *descriptor,
1882 nsCacheAccessMode accessGranted,
1883 nsresult status)
1884 : mListener(listener) // transfers reference
1885 , mDescriptor(descriptor) // transfers reference (may be null)
1886 , mAccessGranted(accessGranted)
1887 , mStatus(status)
1890 NS_IMETHOD Run()
1892 mozilla::eventtracer::AutoEventTracer tracer(
1893 static_cast<nsIRunnable*>(this),
1894 eventtracer::eExec,
1895 eventtracer::eDone,
1896 "net::cache::OnCacheEntryAvailable");
1898 mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
1900 NS_RELEASE(mListener);
1901 NS_IF_RELEASE(mDescriptor);
1902 return NS_OK;
1905 private:
1906 // We explicitly leak mListener or mDescriptor if Run is not called
1907 // because otherwise we cannot guarantee that they are destroyed on
1908 // the right thread.
1910 nsICacheListener *mListener;
1911 nsICacheEntryDescriptor *mDescriptor;
1912 nsCacheAccessMode mAccessGranted;
1913 nsresult mStatus;
1917 nsresult
1918 nsCacheService::NotifyListener(nsCacheRequest * request,
1919 nsICacheEntryDescriptor * descriptor,
1920 nsCacheAccessMode accessGranted,
1921 nsresult status)
1923 NS_ASSERTION(request->mThread, "no thread set in async request!");
1925 // Swap ownership, and release listener on target thread...
1926 nsICacheListener *listener = request->mListener;
1927 request->mListener = nullptr;
1929 nsCOMPtr<nsIRunnable> ev =
1930 new nsCacheListenerEvent(listener, descriptor,
1931 accessGranted, status);
1932 if (!ev) {
1933 // Better to leak listener and descriptor if we fail because we don't
1934 // want to destroy them inside the cache service lock or on potentially
1935 // the wrong thread.
1936 return NS_ERROR_OUT_OF_MEMORY;
1939 MOZ_EVENT_TRACER_NAME_OBJECT(ev.get(), request->mKey.get());
1940 MOZ_EVENT_TRACER_WAIT(ev.get(), "net::cache::OnCacheEntryAvailable");
1941 return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
1945 nsresult
1946 nsCacheService::ProcessRequest(nsCacheRequest * request,
1947 bool calledFromOpenCacheEntry,
1948 nsICacheEntryDescriptor ** result)
1950 mozilla::eventtracer::AutoEventTracer tracer(
1951 request,
1952 eventtracer::eExec,
1953 eventtracer::eDone,
1954 "net::cache::ProcessRequest");
1956 // !!! must be called with mLock held !!!
1957 nsresult rv;
1958 nsCacheEntry * entry = nullptr;
1959 nsCacheEntry * doomedEntry = nullptr;
1960 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
1961 if (result) *result = nullptr;
1963 while(1) { // Activate entry loop
1964 rv = ActivateEntry(request, &entry, &doomedEntry); // get the entry for this request
1965 if (NS_FAILED(rv)) break;
1967 while(1) { // Request Access loop
1968 NS_ASSERTION(entry, "no entry in Request Access loop!");
1969 // entry->RequestAccess queues request on entry
1970 rv = entry->RequestAccess(request, &accessGranted);
1971 if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
1973 if (request->IsBlocking()) {
1974 if (request->mListener) {
1975 // async exits - validate, doom, or close will resume
1976 return rv;
1979 // XXX this is probably wrong...
1980 Unlock();
1981 rv = request->WaitForValidation();
1982 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST));
1985 PR_REMOVE_AND_INIT_LINK(request);
1986 if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
1987 // okay, we're ready to process this request, request access again
1989 if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break;
1991 if (entry->IsNotInUse()) {
1992 // this request was the last one keeping it around, so get rid of it
1993 DeactivateEntry(entry);
1995 // loop back around to look for another entry
1998 if (NS_SUCCEEDED(rv) && request->mProfileDir) {
1999 // Custom cache directory has been demanded. Preset the cache device.
2000 if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
2001 // Failsafe check: this is implemented only for offline cache atm.
2002 rv = NS_ERROR_FAILURE;
2003 } else {
2004 nsRefPtr<nsOfflineCacheDevice> customCacheDevice;
2005 rv = GetCustomOfflineDevice(request->mProfileDir, -1,
2006 getter_AddRefs(customCacheDevice));
2007 if (NS_SUCCEEDED(rv))
2008 entry->SetCustomCacheDevice(customCacheDevice);
2012 nsICacheEntryDescriptor *descriptor = nullptr;
2014 if (NS_SUCCEEDED(rv))
2015 rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
2017 // If doomedEntry is set, ActivatEntry() doomed an existing entry and
2018 // created a new one for that cache-key. However, any pending requests
2019 // on the doomed entry were not processed and we need to do that here.
2020 // This must be done after adding the created entry to list of active
2021 // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
2022 // (see bug ##561313). It is also important to do this after creating a
2023 // descriptor for this request, or some other request may end up being
2024 // executed first for the newly created entry.
2025 // Finally, it is worth to emphasize that if doomedEntry is set,
2026 // ActivateEntry() created a new entry for the request, which will be
2027 // initialized by RequestAccess() and they both should have returned NS_OK.
2028 if (doomedEntry) {
2029 (void) ProcessPendingRequests(doomedEntry);
2030 if (doomedEntry->IsNotInUse())
2031 DeactivateEntry(doomedEntry);
2032 doomedEntry = nullptr;
2035 if (request->mListener) { // Asynchronous
2037 if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
2038 return rv; // skip notifying listener, just return rv to caller
2040 // call listener to report error or descriptor
2041 nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
2042 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
2043 rv = rv2; // trigger delete request
2045 } else { // Synchronous
2046 *result = descriptor;
2048 return rv;
2052 nsresult
2053 nsCacheService::OpenCacheEntry(nsCacheSession * session,
2054 const nsACString & key,
2055 nsCacheAccessMode accessRequested,
2056 bool blockingMode,
2057 nsICacheListener * listener,
2058 nsICacheEntryDescriptor ** result)
2060 CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
2061 session, PromiseFlatCString(key).get(), accessRequested,
2062 blockingMode));
2063 NS_ASSERTION(gService, "nsCacheService::gService is null.");
2064 if (result)
2065 *result = nullptr;
2067 if (!gService->mInitialized)
2068 return NS_ERROR_NOT_INITIALIZED;
2070 nsCacheRequest * request = nullptr;
2072 nsresult rv = gService->CreateRequest(session,
2073 key,
2074 accessRequested,
2075 blockingMode,
2076 listener,
2077 &request);
2078 if (NS_FAILED(rv)) return rv;
2080 CACHE_LOG_DEBUG(("Created request %p\n", request));
2082 // Process the request on the background thread if we are on the main thread
2083 // and the the request is asynchronous
2084 if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
2085 nsCOMPtr<nsIRunnable> ev =
2086 new nsProcessRequestEvent(request);
2087 rv = DispatchToCacheIOThread(ev);
2089 // delete request if we didn't post the event
2090 if (NS_FAILED(rv))
2091 delete request;
2093 else {
2095 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY));
2096 rv = gService->ProcessRequest(request, true, result);
2098 // delete requests that have completed
2099 if (!(listener && blockingMode &&
2100 (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
2101 delete request;
2104 return rv;
2108 nsresult
2109 nsCacheService::ActivateEntry(nsCacheRequest * request,
2110 nsCacheEntry ** result,
2111 nsCacheEntry ** doomedEntry)
2113 CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
2114 if (!mInitialized || mClearingEntries)
2115 return NS_ERROR_NOT_AVAILABLE;
2117 mozilla::eventtracer::AutoEventTracer tracer(
2118 request,
2119 eventtracer::eExec,
2120 eventtracer::eDone,
2121 "net::cache::ActivateEntry");
2123 nsresult rv = NS_OK;
2125 NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
2126 if (result) *result = nullptr;
2127 if (doomedEntry) *doomedEntry = nullptr;
2128 if ((!request) || (!result) || (!doomedEntry))
2129 return NS_ERROR_NULL_POINTER;
2131 // check if the request can be satisfied
2132 if (!mEnableMemoryDevice && !request->IsStreamBased())
2133 return NS_ERROR_FAILURE;
2134 if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
2135 return NS_ERROR_FAILURE;
2137 // search active entries (including those not bound to device)
2138 nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
2139 CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
2141 if (!entry) {
2142 // search cache devices for entry
2143 bool collision = false;
2144 entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
2145 CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
2146 request, entry));
2147 // When there is a hashkey collision just refuse to cache it...
2148 if (collision) return NS_ERROR_CACHE_IN_USE;
2150 if (entry) entry->MarkInitialized();
2151 } else {
2152 NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
2155 if (entry) {
2156 ++mCacheHits;
2157 entry->Fetched();
2158 } else {
2159 ++mCacheMisses;
2162 if (entry &&
2163 ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
2164 ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
2165 (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
2166 request->WillDoomEntriesIfExpired()))))
2169 // this is FORCE-WRITE request or the entry has expired
2170 // we doom entry without processing pending requests, but store it in
2171 // doomedEntry which causes pending requests to be processed below
2172 rv = DoomEntry_Internal(entry, false);
2173 *doomedEntry = entry;
2174 if (NS_FAILED(rv)) {
2175 // XXX what to do? Increment FailedDooms counter?
2177 entry = nullptr;
2180 if (!entry) {
2181 if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
2182 // this is a READ-ONLY request
2183 rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
2184 goto error;
2187 entry = new nsCacheEntry(request->mKey,
2188 request->IsStreamBased(),
2189 request->StoragePolicy());
2190 if (!entry)
2191 return NS_ERROR_OUT_OF_MEMORY;
2193 if (request->IsPrivate())
2194 entry->MarkPrivate();
2196 entry->Fetched();
2197 ++mTotalEntries;
2199 // XXX we could perform an early bind in some cases based on storage policy
2202 if (!entry->IsActive()) {
2203 rv = mActiveEntries.AddEntry(entry);
2204 if (NS_FAILED(rv)) goto error;
2205 CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
2206 entry->MarkActive(); // mark entry active, because it's now in mActiveEntries
2208 *result = entry;
2209 return NS_OK;
2211 error:
2212 *result = nullptr;
2213 delete entry;
2214 return rv;
2218 nsCacheEntry *
2219 nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
2221 Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2> timer;
2222 nsCacheEntry * entry = nullptr;
2224 MOZ_EVENT_TRACER_NAME_OBJECT(key, key->BeginReading());
2225 eventtracer::AutoEventTracer searchCacheDevices(
2226 key,
2227 eventtracer::eExec,
2228 eventtracer::eDone,
2229 "net::cache::SearchCacheDevices");
2231 CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
2233 *collision = false;
2234 if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
2235 // If there is no memory device, then there is nothing to search...
2236 if (mMemoryDevice) {
2237 entry = mMemoryDevice->FindEntry(key, collision);
2238 CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
2239 "collision: %d\n", key->get(), entry, collision));
2243 if (!entry &&
2244 ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
2246 if (mEnableDiskDevice) {
2247 if (!mDiskDevice) {
2248 nsresult rv = CreateDiskDevice();
2249 if (NS_FAILED(rv))
2250 return nullptr;
2253 entry = mDiskDevice->FindEntry(key, collision);
2257 if (!entry && (policy == nsICache::STORE_OFFLINE ||
2258 (policy == nsICache::STORE_ANYWHERE &&
2259 gIOService->IsOffline()))) {
2261 if (mEnableOfflineDevice) {
2262 if (!mOfflineDevice) {
2263 nsresult rv = CreateOfflineDevice();
2264 if (NS_FAILED(rv))
2265 return nullptr;
2268 entry = mOfflineDevice->FindEntry(key, collision);
2272 return entry;
2276 nsCacheDevice *
2277 nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
2279 nsCacheDevice * device = entry->CacheDevice();
2280 // return device if found, possibly null if the entry is doomed i.e prevent
2281 // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
2282 if (device || entry->IsDoomed()) return device;
2284 int64_t predictedDataSize = entry->PredictedDataSize();
2285 if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
2286 // this is the default
2287 if (!mDiskDevice) {
2288 (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
2291 if (mDiskDevice) {
2292 // Bypass the cache if Content-Length says the entry will be too big
2293 if (predictedDataSize != -1 &&
2294 mDiskDevice->EntryIsTooBig(predictedDataSize)) {
2295 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
2296 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
2297 return nullptr;
2300 entry->MarkBinding(); // enter state of binding
2301 nsresult rv = mDiskDevice->BindEntry(entry);
2302 entry->ClearBinding(); // exit state of binding
2303 if (NS_SUCCEEDED(rv))
2304 device = mDiskDevice;
2308 // if we can't use mDiskDevice, try mMemoryDevice
2309 if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {
2310 if (!mMemoryDevice) {
2311 (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
2313 if (mMemoryDevice) {
2314 // Bypass the cache if Content-Length says entry will be too big
2315 if (predictedDataSize != -1 &&
2316 mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
2317 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
2318 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
2319 return nullptr;
2322 entry->MarkBinding(); // enter state of binding
2323 nsresult rv = mMemoryDevice->BindEntry(entry);
2324 entry->ClearBinding(); // exit state of binding
2325 if (NS_SUCCEEDED(rv))
2326 device = mMemoryDevice;
2330 if (!device && entry->IsStreamData() &&
2331 entry->IsAllowedOffline() && mEnableOfflineDevice) {
2332 if (!mOfflineDevice) {
2333 (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
2336 device = entry->CustomCacheDevice()
2337 ? entry->CustomCacheDevice()
2338 : mOfflineDevice;
2340 if (device) {
2341 entry->MarkBinding();
2342 nsresult rv = device->BindEntry(entry);
2343 entry->ClearBinding();
2344 if (NS_FAILED(rv))
2345 device = nullptr;
2349 if (device)
2350 entry->SetCacheDevice(device);
2351 return device;
2354 nsresult
2355 nsCacheService::DoomEntry(nsCacheEntry * entry)
2357 return gService->DoomEntry_Internal(entry, true);
2361 nsresult
2362 nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
2363 bool doProcessPendingRequests)
2365 if (entry->IsDoomed()) return NS_OK;
2367 CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
2368 nsresult rv = NS_OK;
2369 entry->MarkDoomed();
2371 NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
2372 nsCacheDevice * device = entry->CacheDevice();
2373 if (device) device->DoomEntry(entry);
2375 if (entry->IsActive()) {
2376 // remove from active entries
2377 mActiveEntries.RemoveEntry(entry);
2378 CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
2379 entry->MarkInactive();
2382 // put on doom list to wait for descriptors to close
2383 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
2384 PR_APPEND_LINK(entry, &mDoomedEntries);
2386 // handle pending requests only if we're supposed to
2387 if (doProcessPendingRequests) {
2388 // tell pending requests to get on with their lives...
2389 rv = ProcessPendingRequests(entry);
2391 // All requests have been removed, but there may still be open descriptors
2392 if (entry->IsNotInUse()) {
2393 DeactivateEntry(entry); // tell device to get rid of it
2396 return rv;
2400 void
2401 nsCacheService::OnProfileShutdown(bool cleanse)
2403 if (!gService) return;
2404 if (!gService->mInitialized) {
2405 // The cache service has been shut down, but someone is still holding
2406 // a reference to it. Ignore this call.
2407 return;
2410 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
2411 gService->mClearingEntries = true;
2412 gService->DoomActiveEntries(nullptr);
2415 gService->CloseAllStreams();
2417 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
2418 gService->ClearDoomList();
2420 // Make sure to wait for any pending cache-operations before
2421 // proceeding with destructive actions (bug #620660)
2422 (void) SyncWithCacheIOThread();
2424 if (gService->mDiskDevice && gService->mEnableDiskDevice) {
2425 if (cleanse)
2426 gService->mDiskDevice->EvictEntries(nullptr);
2428 gService->mDiskDevice->Shutdown();
2430 gService->mEnableDiskDevice = false;
2432 if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
2433 if (cleanse)
2434 gService->mOfflineDevice->EvictEntries(nullptr);
2436 gService->mOfflineDevice->Shutdown();
2438 gService->mCustomOfflineDevices.Enumerate(
2439 &nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
2441 gService->mEnableOfflineDevice = false;
2443 if (gService->mMemoryDevice) {
2444 // clear memory cache
2445 gService->mMemoryDevice->EvictEntries(nullptr);
2448 gService->mClearingEntries = false;
2452 void
2453 nsCacheService::OnProfileChanged()
2455 if (!gService) return;
2457 CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
2459 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED));
2461 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
2462 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
2463 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
2465 if (gService->mDiskDevice) {
2466 gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
2467 gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
2469 // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
2470 nsresult rv = gService->mDiskDevice->Init();
2471 if (NS_FAILED(rv)) {
2472 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
2473 gService->mEnableDiskDevice = false;
2474 // XXX delete mDiskDevice?
2478 if (gService->mOfflineDevice) {
2479 gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
2480 gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
2482 // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
2483 nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
2484 if (NS_FAILED(rv)) {
2485 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
2486 gService->mEnableOfflineDevice = false;
2487 // XXX delete mOfflineDevice?
2491 // If memoryDevice exists, reset its size to the new profile
2492 if (gService->mMemoryDevice) {
2493 if (gService->mEnableMemoryDevice) {
2494 // make sure that capacity is reset to the right value
2495 int32_t capacity = gService->mObserver->MemoryCacheCapacity();
2496 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2497 capacity));
2498 gService->mMemoryDevice->SetCapacity(capacity);
2499 } else {
2500 // tell memory device to evict everything
2501 CACHE_LOG_DEBUG(("memory device disabled\n"));
2502 gService->mMemoryDevice->SetCapacity(0);
2503 // Don't delete memory device, because some entries may be active still...
2509 void
2510 nsCacheService::SetDiskCacheEnabled(bool enabled)
2512 if (!gService) return;
2513 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED));
2514 gService->mEnableDiskDevice = enabled;
2518 void
2519 nsCacheService::SetDiskCacheCapacity(int32_t capacity)
2521 if (!gService) return;
2522 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY));
2524 if (gService->mDiskDevice) {
2525 gService->mDiskDevice->SetCapacity(capacity);
2528 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
2531 void
2532 nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize)
2534 if (!gService) return;
2535 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE));
2537 if (gService->mDiskDevice) {
2538 gService->mDiskDevice->SetMaxEntrySize(maxSize);
2542 void
2543 nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize)
2545 if (!gService) return;
2546 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE));
2548 if (gService->mMemoryDevice) {
2549 gService->mMemoryDevice->SetMaxEntrySize(maxSize);
2553 void
2554 nsCacheService::SetOfflineCacheEnabled(bool enabled)
2556 if (!gService) return;
2557 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED));
2558 gService->mEnableOfflineDevice = enabled;
2561 void
2562 nsCacheService::SetOfflineCacheCapacity(int32_t capacity)
2564 if (!gService) return;
2565 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY));
2567 if (gService->mOfflineDevice) {
2568 gService->mOfflineDevice->SetCapacity(capacity);
2571 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
2575 void
2576 nsCacheService::SetMemoryCache()
2578 if (!gService) return;
2580 CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
2582 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE));
2584 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
2586 if (gService->mEnableMemoryDevice) {
2587 if (gService->mMemoryDevice) {
2588 int32_t capacity = gService->mObserver->MemoryCacheCapacity();
2589 // make sure that capacity is reset to the right value
2590 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
2591 capacity));
2592 gService->mMemoryDevice->SetCapacity(capacity);
2594 } else {
2595 if (gService->mMemoryDevice) {
2596 // tell memory device to evict everything
2597 CACHE_LOG_DEBUG(("memory device disabled\n"));
2598 gService->mMemoryDevice->SetCapacity(0);
2599 // Don't delete memory device, because some entries may be active still...
2605 /******************************************************************************
2606 * static methods for nsCacheEntryDescriptor
2607 *****************************************************************************/
2608 void
2609 nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
2611 // ask entry to remove descriptor
2612 nsCacheEntry * entry = descriptor->CacheEntry();
2613 bool doomEntry;
2614 bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
2616 if (!entry->IsValid()) {
2617 gService->ProcessPendingRequests(entry);
2620 if (doomEntry) {
2621 gService->DoomEntry_Internal(entry, true);
2622 return;
2625 if (!stillActive) {
2626 gService->DeactivateEntry(entry);
2631 nsresult
2632 nsCacheService::GetFileForEntry(nsCacheEntry * entry,
2633 nsIFile ** result)
2635 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
2636 if (!device) return NS_ERROR_UNEXPECTED;
2638 return device->GetFileForEntry(entry, result);
2642 nsresult
2643 nsCacheService::OpenInputStreamForEntry(nsCacheEntry * entry,
2644 nsCacheAccessMode mode,
2645 uint32_t offset,
2646 nsIInputStream ** result)
2648 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
2649 if (!device) return NS_ERROR_UNEXPECTED;
2651 return device->OpenInputStreamForEntry(entry, mode, offset, result);
2654 nsresult
2655 nsCacheService::OpenOutputStreamForEntry(nsCacheEntry * entry,
2656 nsCacheAccessMode mode,
2657 uint32_t offset,
2658 nsIOutputStream ** result)
2660 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
2661 if (!device) return NS_ERROR_UNEXPECTED;
2663 return device->OpenOutputStreamForEntry(entry, mode, offset, result);
2667 nsresult
2668 nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
2670 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
2671 if (!device) return NS_ERROR_UNEXPECTED;
2673 return device->OnDataSizeChange(entry, deltaSize);
2676 void
2677 nsCacheService::LockAcquired()
2679 MutexAutoLock lock(mTimeStampLock);
2680 mLockAcquiredTimeStamp = TimeStamp::Now();
2683 void
2684 nsCacheService::LockReleased()
2686 MutexAutoLock lock(mTimeStampLock);
2687 mLockAcquiredTimeStamp = TimeStamp();
2690 void
2691 nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID)
2693 mozilla::Telemetry::ID lockerID;
2694 mozilla::Telemetry::ID generalID;
2696 if (NS_IsMainThread()) {
2697 lockerID = mainThreadLockerID;
2698 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2;
2699 } else {
2700 lockerID = mozilla::Telemetry::HistogramCount;
2701 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2;
2704 TimeStamp start(TimeStamp::Now());
2705 MOZ_EVENT_TRACER_WAIT(nsCacheService::gService, "net::cache::lock");
2707 gService->mLock.Lock();
2708 gService->LockAcquired();
2710 TimeStamp stop(TimeStamp::Now());
2711 MOZ_EVENT_TRACER_EXEC(nsCacheService::gService, "net::cache::lock");
2713 // Telemetry isn't thread safe on its own, but this is OK because we're
2714 // protecting it with the cache lock.
2715 if (lockerID != mozilla::Telemetry::HistogramCount) {
2716 mozilla::Telemetry::AccumulateTimeDelta(lockerID, start, stop);
2718 mozilla::Telemetry::AccumulateTimeDelta(generalID, start, stop);
2721 void
2722 nsCacheService::Unlock()
2724 gService->mLock.AssertCurrentThreadOwns();
2726 nsTArray<nsISupports*> doomed;
2727 doomed.SwapElements(gService->mDoomedObjects);
2729 gService->LockReleased();
2730 gService->mLock.Unlock();
2732 MOZ_EVENT_TRACER_DONE(nsCacheService::gService, "net::cache::lock");
2734 for (uint32_t i = 0; i < doomed.Length(); ++i)
2735 doomed[i]->Release();
2738 void
2739 nsCacheService::ReleaseObject_Locked(nsISupports * obj,
2740 nsIEventTarget * target)
2742 gService->mLock.AssertCurrentThreadOwns();
2744 bool isCur;
2745 if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
2746 gService->mDoomedObjects.AppendElement(obj);
2747 } else {
2748 NS_ProxyRelease(target, obj);
2753 nsresult
2754 nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
2756 entry->SetData(element);
2757 entry->TouchData();
2758 return NS_OK;
2762 nsresult
2763 nsCacheService::ValidateEntry(nsCacheEntry * entry)
2765 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
2766 if (!device) return NS_ERROR_UNEXPECTED;
2768 entry->MarkValid();
2769 nsresult rv = gService->ProcessPendingRequests(entry);
2770 NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
2771 // XXX what else should be done?
2773 return rv;
2777 int32_t
2778 nsCacheService::CacheCompressionLevel()
2780 int32_t level = gService->mObserver->CacheCompressionLevel();
2781 return level;
2785 void
2786 nsCacheService::DeactivateEntry(nsCacheEntry * entry)
2788 CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
2789 nsresult rv = NS_OK;
2790 NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
2791 nsCacheDevice * device = nullptr;
2793 if (mMaxDataSize < entry->DataSize() ) mMaxDataSize = entry->DataSize();
2794 if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
2796 if (entry->IsDoomed()) {
2797 // remove from Doomed list
2798 PR_REMOVE_AND_INIT_LINK(entry);
2799 } else if (entry->IsActive()) {
2800 // remove from active entries
2801 mActiveEntries.RemoveEntry(entry);
2802 CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
2803 entry));
2804 entry->MarkInactive();
2806 // bind entry if necessary to store meta-data
2807 device = EnsureEntryHasDevice(entry);
2808 if (!device) {
2809 CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
2810 "entry %p\n",
2811 entry));
2812 NS_WARNING("DeactivateEntry: unable to bind active entry\n");
2813 return;
2815 } else {
2816 // if mInitialized == false,
2817 // then we're shutting down and this state is okay.
2818 NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
2821 device = entry->CacheDevice();
2822 if (device) {
2823 rv = device->DeactivateEntry(entry);
2824 if (NS_FAILED(rv)) {
2825 // increment deactivate failure count
2826 ++mDeactivateFailures;
2828 } else {
2829 // increment deactivating unbound entry statistic
2830 ++mDeactivatedUnboundEntries;
2831 delete entry; // because no one else will
2836 nsresult
2837 nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
2839 mozilla::eventtracer::AutoEventTracer tracer(
2840 entry,
2841 eventtracer::eExec,
2842 eventtracer::eDone,
2843 "net::cache::ProcessPendingRequests");
2845 nsresult rv = NS_OK;
2846 nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
2847 nsCacheRequest * nextRequest;
2848 bool newWriter = false;
2850 CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
2851 (entry->IsInitialized()?"" : "Un"),
2852 (entry->IsDoomed()?"DOOMED" : ""),
2853 (entry->IsValid()? "V":"Inv"), entry));
2855 if (request == &entry->mRequestQ) return NS_OK; // no queued requests
2857 if (!entry->IsDoomed() && entry->IsInvalid()) {
2858 // 1st descriptor closed w/o MarkValid()
2859 NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
2861 #if DEBUG
2862 // verify no ACCESS_WRITE requests(shouldn't have any of these)
2863 while (request != &entry->mRequestQ) {
2864 NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
2865 "ACCESS_WRITE request should have been given a new entry");
2866 request = (nsCacheRequest *)PR_NEXT_LINK(request);
2868 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
2869 #endif
2870 // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
2871 while (request != &entry->mRequestQ) {
2872 if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
2873 newWriter = true;
2874 CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request));
2875 break;
2878 request = (nsCacheRequest *)PR_NEXT_LINK(request);
2881 if (request == &entry->mRequestQ) // no requests asked for ACCESS_READ_WRITE, back to top
2882 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
2884 // XXX what should we do if there are only READ requests in queue?
2885 // XXX serialize their accesses, give them only read access, but force them to check validate flag?
2886 // XXX or do readers simply presume the entry is valid
2887 // See fix for bug #467392 below
2890 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
2892 while (request != &entry->mRequestQ) {
2893 nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
2894 CACHE_LOG_DEBUG((" %sync request %p for %p\n",
2895 (request->mListener?"As":"S"), request, entry));
2897 if (request->mListener) {
2899 // Async request
2900 PR_REMOVE_AND_INIT_LINK(request);
2902 if (entry->IsDoomed()) {
2903 rv = ProcessRequest(request, false, nullptr);
2904 if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
2905 rv = NS_OK;
2906 else
2907 delete request;
2909 if (NS_FAILED(rv)) {
2910 // XXX what to do?
2912 } else if (entry->IsValid() || newWriter) {
2913 rv = entry->RequestAccess(request, &accessGranted);
2914 NS_ASSERTION(NS_SUCCEEDED(rv),
2915 "if entry is valid, RequestAccess must succeed.");
2916 // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
2918 // entry->CreateDescriptor dequeues request, and queues descriptor
2919 nsICacheEntryDescriptor *descriptor = nullptr;
2920 rv = entry->CreateDescriptor(request,
2921 accessGranted,
2922 &descriptor);
2924 // post call to listener to report error or descriptor
2925 rv = NotifyListener(request, descriptor, accessGranted, rv);
2926 delete request;
2927 if (NS_FAILED(rv)) {
2928 // XXX what to do?
2931 } else {
2932 // read-only request to an invalid entry - need to wait for
2933 // the entry to become valid so we post an event to process
2934 // the request again later (bug #467392)
2935 nsCOMPtr<nsIRunnable> ev =
2936 new nsProcessRequestEvent(request);
2937 rv = DispatchToCacheIOThread(ev);
2938 if (NS_FAILED(rv)) {
2939 delete request; // avoid leak
2942 } else {
2944 // Synchronous request
2945 request->WakeUp();
2947 if (newWriter) break; // process remaining requests after validation
2948 request = nextRequest;
2951 return NS_OK;
2954 bool
2955 nsCacheService::IsDoomListEmpty()
2957 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
2958 return &mDoomedEntries == entry;
2961 void
2962 nsCacheService::ClearDoomList()
2964 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
2966 while (entry != &mDoomedEntries) {
2967 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
2969 entry->DetachDescriptors();
2970 DeactivateEntry(entry);
2971 entry = next;
2975 PLDHashOperator
2976 nsCacheService::GetActiveEntries(PLDHashTable * table,
2977 PLDHashEntryHdr * hdr,
2978 uint32_t number,
2979 void * arg)
2981 static_cast<nsVoidArray *>(arg)->AppendElement(
2982 ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry);
2983 return PL_DHASH_NEXT;
2986 struct ActiveEntryArgs
2988 nsTArray<nsCacheEntry*>* mActiveArray;
2989 nsCacheService::DoomCheckFn mCheckFn;
2992 void
2993 nsCacheService::DoomActiveEntries(DoomCheckFn check)
2995 nsAutoTArray<nsCacheEntry*, 8> array;
2996 ActiveEntryArgs args = { &array, check };
2998 mActiveEntries.VisitEntries(RemoveActiveEntry, &args);
3000 uint32_t count = array.Length();
3001 for (uint32_t i=0; i < count; ++i)
3002 DoomEntry_Internal(array[i], true);
3005 PLDHashOperator
3006 nsCacheService::RemoveActiveEntry(PLDHashTable * table,
3007 PLDHashEntryHdr * hdr,
3008 uint32_t number,
3009 void * arg)
3011 nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry;
3012 NS_ASSERTION(entry, "### active entry = nullptr!");
3014 ActiveEntryArgs* args = static_cast<ActiveEntryArgs*>(arg);
3015 if (args->mCheckFn && !args->mCheckFn(entry))
3016 return PL_DHASH_NEXT;
3018 NS_ASSERTION(args->mActiveArray, "### array = nullptr!");
3019 args->mActiveArray->AppendElement(entry);
3021 // entry is being removed from the active entry list
3022 entry->MarkInactive();
3023 return PL_DHASH_REMOVE; // and continue enumerating
3027 void
3028 nsCacheService::CloseAllStreams()
3030 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
3031 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
3034 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS));
3036 nsVoidArray entries;
3038 #if DEBUG
3039 // make sure there is no active entry
3040 mActiveEntries.VisitEntries(GetActiveEntries, &entries);
3041 NS_ASSERTION(entries.Count() == 0, "Bad state");
3042 #endif
3044 // Get doomed entries
3045 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
3046 while (entry != &mDoomedEntries) {
3047 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
3048 entries.AppendElement(entry);
3049 entry = next;
3052 // Iterate through all entries and collect input and output streams
3053 for (int32_t i = 0 ; i < entries.Count() ; i++) {
3054 entry = static_cast<nsCacheEntry *>(entries.ElementAt(i));
3056 nsTArray<nsRefPtr<nsCacheEntryDescriptor> > descs;
3057 entry->GetDescriptors(descs);
3059 for (uint32_t j = 0 ; j < descs.Length() ; j++) {
3060 if (descs[j]->mOutputWrapper)
3061 outputs.AppendElement(descs[j]->mOutputWrapper);
3063 for (int32_t k = 0 ; k < descs[j]->mInputWrappers.Count() ; k++)
3064 inputs.AppendElement(static_cast<
3065 nsCacheEntryDescriptor::nsInputStreamWrapper *>(
3066 descs[j]->mInputWrappers[k]));
3071 uint32_t i;
3072 for (i = 0 ; i < inputs.Length() ; i++)
3073 inputs[i]->Close();
3075 for (i = 0 ; i < outputs.Length() ; i++)
3076 outputs[i]->Close();
3080 bool
3081 nsCacheService::GetClearingEntries()
3083 AssertOwnsLock();
3084 return gService->mClearingEntries;
3087 // static
3088 void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
3090 *result = nullptr;
3091 if (!gService || !gService->mObserver)
3092 return;
3094 nsCOMPtr<nsIFile> directory =
3095 gService->mObserver->DiskCacheParentDirectory();
3096 if (!directory)
3097 return;
3099 directory->Clone(result);
3102 // static
3103 void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
3105 nsCOMPtr<nsIFile> directory;
3106 GetCacheBaseDirectoty(getter_AddRefs(directory));
3107 if (!directory)
3108 return;
3110 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
3111 if (NS_FAILED(rv))
3112 return;
3114 directory.forget(result);
3117 // static
3118 void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
3120 nsCOMPtr<nsIFile> directory;
3121 GetCacheBaseDirectoty(getter_AddRefs(directory));
3122 if (!directory)
3123 return;
3125 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
3126 if (NS_FAILED(rv))
3127 return;
3129 directory.forget(result);
3133 #if defined(PR_LOGGING)
3134 void
3135 nsCacheService::LogCacheStatistics()
3137 uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
3138 ((double)(mCacheHits + mCacheMisses))) * 100);
3139 CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
3140 CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries));
3141 CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits));
3142 CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses));
3143 CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage));
3144 CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength));
3145 CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize));
3146 CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize));
3147 CACHE_LOG_ALWAYS(("\n"));
3148 CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n",
3149 mDeactivateFailures));
3150 CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n",
3151 mDeactivatedUnboundEntries));
3153 #endif
3155 nsresult
3156 nsCacheService::SetDiskSmartSize()
3158 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE));
3160 if (!gService) return NS_ERROR_NOT_AVAILABLE;
3162 return gService->SetDiskSmartSize_Locked();
3165 nsresult
3166 nsCacheService::SetDiskSmartSize_Locked()
3168 nsresult rv;
3170 if (mozilla::net::CacheObserver::UseNewCache()) {
3171 return NS_ERROR_NOT_AVAILABLE;
3174 if (!mObserver->DiskCacheParentDirectory())
3175 return NS_ERROR_NOT_AVAILABLE;
3177 if (!mDiskDevice)
3178 return NS_ERROR_NOT_AVAILABLE;
3180 if (!mObserver->SmartSizeEnabled())
3181 return NS_ERROR_NOT_AVAILABLE;
3183 nsAutoString cachePath;
3184 rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
3185 if (NS_SUCCEEDED(rv)) {
3186 nsCOMPtr<nsIRunnable> event =
3187 new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
3188 mObserver->ShouldUseOldMaxSmartSize());
3189 DispatchToCacheIOThread(event);
3190 } else {
3191 return NS_ERROR_FAILURE;
3194 return NS_OK;
3197 void
3198 nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
3199 nsIFile *aNewCacheDir,
3200 const char *aCacheSubdir)
3202 bool same;
3203 if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
3204 return;
3206 nsCOMPtr<nsIFile> aOldCacheSubdir;
3207 aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
3209 nsresult rv = aOldCacheSubdir->AppendNative(
3210 nsDependentCString(aCacheSubdir));
3211 if (NS_FAILED(rv))
3212 return;
3214 bool exists;
3215 if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
3216 return;
3218 nsCOMPtr<nsIFile> aNewCacheSubdir;
3219 aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
3221 rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
3222 if (NS_FAILED(rv))
3223 return;
3225 nsAutoCString newPath;
3226 rv = aNewCacheSubdir->GetNativePath(newPath);
3227 if (NS_FAILED(rv))
3228 return;
3230 if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
3231 // New cache directory does not exist, try to move the old one here
3232 // rename needs an empty target directory
3234 // Make sure the parent of the target sub-dir exists
3235 rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
3236 if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
3237 nsAutoCString oldPath;
3238 rv = aOldCacheSubdir->GetNativePath(oldPath);
3239 if (NS_FAILED(rv))
3240 return;
3241 if (rename(oldPath.get(), newPath.get()) == 0)
3242 return;
3246 // Delay delete by 1 minute to avoid IO thrash on startup.
3247 nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
3250 static bool
3251 IsEntryPrivate(nsCacheEntry* entry)
3253 return entry->IsPrivate();
3256 void
3257 nsCacheService::LeavePrivateBrowsing()
3259 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING));
3261 gService->DoomActiveEntries(IsEntryPrivate);
3263 if (gService->mMemoryDevice) {
3264 // clear memory cache
3265 gService->mMemoryDevice->EvictPrivateEntries();
3269 MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
3271 NS_IMETHODIMP
3272 nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
3273 nsISupports* aData, bool aAnonymize)
3275 size_t disk = 0;
3276 if (mDiskDevice) {
3277 nsCacheServiceAutoLock
3278 lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));
3279 disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
3282 size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
3284 #define REPORT(_path, _amount, _desc) \
3285 do { \
3286 nsresult rv; \
3287 rv = aHandleReport->Callback(EmptyCString(), \
3288 NS_LITERAL_CSTRING(_path), \
3289 KIND_HEAP, UNITS_BYTES, _amount, \
3290 NS_LITERAL_CSTRING(_desc), aData); \
3291 NS_ENSURE_SUCCESS(rv, rv); \
3292 } while (0)
3294 REPORT("explicit/network/disk-cache", disk,
3295 "Memory used by the network disk cache.");
3297 REPORT("explicit/network/memory-cache", memory,
3298 "Memory used by the network memory cache.");
3300 return NS_OK;