netwerk/cache/nsCacheService.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/ArrayUtils.h"
michael@0 8 #include "mozilla/Attributes.h"
michael@0 9 #include "mozilla/Assertions.h"
michael@0 10 #include "mozilla/DebugOnly.h"
michael@0 11
michael@0 12 #include "necko-config.h"
michael@0 13
michael@0 14 #include "nsCache.h"
michael@0 15 #include "nsCacheService.h"
michael@0 16 #include "nsCacheRequest.h"
michael@0 17 #include "nsCacheEntry.h"
michael@0 18 #include "nsCacheEntryDescriptor.h"
michael@0 19 #include "nsCacheDevice.h"
michael@0 20 #include "nsMemoryCacheDevice.h"
michael@0 21 #include "nsICacheVisitor.h"
michael@0 22 #include "nsDiskCacheDevice.h"
michael@0 23 #include "nsDiskCacheDeviceSQL.h"
michael@0 24 #include "nsCacheUtils.h"
michael@0 25 #include "../cache2/CacheObserver.h"
michael@0 26
michael@0 27 #include "nsIObserverService.h"
michael@0 28 #include "nsIPrefService.h"
michael@0 29 #include "nsIPrefBranch.h"
michael@0 30 #include "nsIFile.h"
michael@0 31 #include "nsIOService.h"
michael@0 32 #include "nsDirectoryServiceDefs.h"
michael@0 33 #include "nsAppDirectoryServiceDefs.h"
michael@0 34 #include "nsThreadUtils.h"
michael@0 35 #include "nsProxyRelease.h"
michael@0 36 #include "nsVoidArray.h"
michael@0 37 #include "nsDeleteDir.h"
michael@0 38 #include "nsNetCID.h"
michael@0 39 #include <math.h> // for log()
michael@0 40 #include "mozilla/Services.h"
michael@0 41 #include "nsITimer.h"
michael@0 42 #include "mozIStorageService.h"
michael@0 43
michael@0 44 #include "mozilla/net/NeckoCommon.h"
michael@0 45 #include "mozilla/VisualEventTracer.h"
michael@0 46 #include <algorithm>
michael@0 47
michael@0 48 using namespace mozilla;
michael@0 49
michael@0 50 /******************************************************************************
michael@0 51 * nsCacheProfilePrefObserver
michael@0 52 *****************************************************************************/
michael@0 53 #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
michael@0 54 #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
michael@0 55 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
michael@0 56 "browser.cache.disk.smart_size.first_run"
michael@0 57 #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
michael@0 58 "browser.cache.disk.smart_size.enabled"
michael@0 59 #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
michael@0 60 #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
michael@0 61 #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
michael@0 62 #define DISK_CACHE_CAPACITY 256000
michael@0 63
michael@0 64 #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
michael@0 65 "browser.cache.disk.smart_size.use_old_max"
michael@0 66
michael@0 67 #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
michael@0 68 #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
michael@0 69 #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
michael@0 70 #define OFFLINE_CACHE_CAPACITY 512000
michael@0 71
michael@0 72 #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
michael@0 73 #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
michael@0 74 #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
michael@0 75
michael@0 76 #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
michael@0 77 #define CACHE_COMPRESSION_LEVEL 1
michael@0 78
michael@0 79 #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
michael@0 80 #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
michael@0 81
michael@0 82 static const char * observerList[] = {
michael@0 83 "profile-before-change",
michael@0 84 "profile-do-change",
michael@0 85 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
michael@0 86 "last-pb-context-exited",
michael@0 87 "suspend_process_notification",
michael@0 88 "resume_process_notification"
michael@0 89 };
michael@0 90
michael@0 91 static const char * prefList[] = {
michael@0 92 DISK_CACHE_ENABLE_PREF,
michael@0 93 DISK_CACHE_SMART_SIZE_ENABLED_PREF,
michael@0 94 DISK_CACHE_CAPACITY_PREF,
michael@0 95 DISK_CACHE_DIR_PREF,
michael@0 96 DISK_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 97 DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
michael@0 98 OFFLINE_CACHE_ENABLE_PREF,
michael@0 99 OFFLINE_CACHE_CAPACITY_PREF,
michael@0 100 OFFLINE_CACHE_DIR_PREF,
michael@0 101 MEMORY_CACHE_ENABLE_PREF,
michael@0 102 MEMORY_CACHE_CAPACITY_PREF,
michael@0 103 MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 104 CACHE_COMPRESSION_LEVEL_PREF,
michael@0 105 SANITIZE_ON_SHUTDOWN_PREF,
michael@0 106 CLEAR_ON_SHUTDOWN_PREF
michael@0 107 };
michael@0 108
michael@0 109 // Cache sizes, in KB
michael@0 110 const int32_t DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB
michael@0 111 #ifdef ANDROID
michael@0 112 const int32_t MAX_CACHE_SIZE = 200 * 1024; // 200 MB
michael@0 113 const int32_t OLD_MAX_CACHE_SIZE = 200 * 1024; // 200 MB
michael@0 114 #else
michael@0 115 const int32_t MAX_CACHE_SIZE = 350 * 1024; // 350 MB
michael@0 116 const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; // 1 GB
michael@0 117 #endif
michael@0 118 // Default cache size was 50 MB for many years until FF 4:
michael@0 119 const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
michael@0 120
michael@0 121 class nsCacheProfilePrefObserver : public nsIObserver
michael@0 122 {
michael@0 123 public:
michael@0 124 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 125 NS_DECL_NSIOBSERVER
michael@0 126
michael@0 127 nsCacheProfilePrefObserver()
michael@0 128 : mHaveProfile(false)
michael@0 129 , mDiskCacheEnabled(false)
michael@0 130 , mDiskCacheCapacity(0)
michael@0 131 , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
michael@0 132 , mSmartSizeEnabled(false)
michael@0 133 , mShouldUseOldMaxSmartSize(false)
michael@0 134 , mOfflineCacheEnabled(false)
michael@0 135 , mOfflineCacheCapacity(0)
michael@0 136 , mMemoryCacheEnabled(true)
michael@0 137 , mMemoryCacheCapacity(-1)
michael@0 138 , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
michael@0 139 , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
michael@0 140 , mSanitizeOnShutdown(false)
michael@0 141 , mClearCacheOnShutdown(false)
michael@0 142 {
michael@0 143 }
michael@0 144
michael@0 145 virtual ~nsCacheProfilePrefObserver() {}
michael@0 146
michael@0 147 nsresult Install();
michael@0 148 void Remove();
michael@0 149 nsresult ReadPrefs(nsIPrefBranch* branch);
michael@0 150
michael@0 151 bool DiskCacheEnabled();
michael@0 152 int32_t DiskCacheCapacity() { return mDiskCacheCapacity; }
michael@0 153 void SetDiskCacheCapacity(int32_t);
michael@0 154 int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
michael@0 155 nsIFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
michael@0 156 bool SmartSizeEnabled() { return mSmartSizeEnabled; }
michael@0 157
michael@0 158 bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize; }
michael@0 159 void SetUseNewMaxSmartSize(bool useNew) { mShouldUseOldMaxSmartSize = !useNew; }
michael@0 160
michael@0 161 bool OfflineCacheEnabled();
michael@0 162 int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity; }
michael@0 163 nsIFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; }
michael@0 164
michael@0 165 bool MemoryCacheEnabled();
michael@0 166 int32_t MemoryCacheCapacity();
michael@0 167 int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize; }
michael@0 168
michael@0 169 int32_t CacheCompressionLevel();
michael@0 170
michael@0 171 bool SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
michael@0 172
michael@0 173 static uint32_t GetSmartCacheSize(const nsAString& cachePath,
michael@0 174 uint32_t currentSize,
michael@0 175 bool shouldUseOldMaxSmartSize);
michael@0 176
michael@0 177 bool PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
michael@0 178
michael@0 179 private:
michael@0 180 bool mHaveProfile;
michael@0 181
michael@0 182 bool mDiskCacheEnabled;
michael@0 183 int32_t mDiskCacheCapacity; // in kilobytes
michael@0 184 int32_t mDiskCacheMaxEntrySize; // in kilobytes
michael@0 185 nsCOMPtr<nsIFile> mDiskCacheParentDirectory;
michael@0 186 bool mSmartSizeEnabled;
michael@0 187
michael@0 188 bool mShouldUseOldMaxSmartSize;
michael@0 189
michael@0 190 bool mOfflineCacheEnabled;
michael@0 191 int32_t mOfflineCacheCapacity; // in kilobytes
michael@0 192 nsCOMPtr<nsIFile> mOfflineCacheParentDirectory;
michael@0 193
michael@0 194 bool mMemoryCacheEnabled;
michael@0 195 int32_t mMemoryCacheCapacity; // in kilobytes
michael@0 196 int32_t mMemoryCacheMaxEntrySize; // in kilobytes
michael@0 197
michael@0 198 int32_t mCacheCompressionLevel;
michael@0 199
michael@0 200 bool mSanitizeOnShutdown;
michael@0 201 bool mClearCacheOnShutdown;
michael@0 202 };
michael@0 203
michael@0 204 NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
michael@0 205
michael@0 206 class nsSetDiskSmartSizeCallback MOZ_FINAL : public nsITimerCallback
michael@0 207 {
michael@0 208 public:
michael@0 209 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 210
michael@0 211 NS_IMETHOD Notify(nsITimer* aTimer) {
michael@0 212 if (nsCacheService::gService) {
michael@0 213 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY));
michael@0 214 nsCacheService::gService->SetDiskSmartSize_Locked();
michael@0 215 nsCacheService::gService->mSmartSizeTimer = nullptr;
michael@0 216 }
michael@0 217 return NS_OK;
michael@0 218 }
michael@0 219 };
michael@0 220
michael@0 221 NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
michael@0 222
michael@0 223 // Runnable sent to main thread after the cache IO thread calculates available
michael@0 224 // disk space, so that there is no race in setting mDiskCacheCapacity.
michael@0 225 class nsSetSmartSizeEvent: public nsRunnable
michael@0 226 {
michael@0 227 public:
michael@0 228 nsSetSmartSizeEvent(int32_t smartSize)
michael@0 229 : mSmartSize(smartSize) {}
michael@0 230
michael@0 231 NS_IMETHOD Run()
michael@0 232 {
michael@0 233 NS_ASSERTION(NS_IsMainThread(),
michael@0 234 "Setting smart size data off the main thread");
michael@0 235
michael@0 236 // Main thread may have already called nsCacheService::Shutdown
michael@0 237 if (!nsCacheService::IsInitialized())
michael@0 238 return NS_ERROR_NOT_AVAILABLE;
michael@0 239
michael@0 240 // Ensure smart sizing wasn't switched off while event was pending.
michael@0 241 // It is safe to access the observer without the lock since we are
michael@0 242 // on the main thread and the value changes only on the main thread.
michael@0 243 if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
michael@0 244 return NS_OK;
michael@0 245
michael@0 246 nsCacheService::SetDiskCacheCapacity(mSmartSize);
michael@0 247
michael@0 248 nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 249 if (!ps ||
michael@0 250 NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
michael@0 251 NS_WARNING("Failed to set smart size pref");
michael@0 252
michael@0 253 return NS_OK;
michael@0 254 }
michael@0 255
michael@0 256 private:
michael@0 257 int32_t mSmartSize;
michael@0 258 };
michael@0 259
michael@0 260
michael@0 261 // Runnable sent from main thread to cacheIO thread
michael@0 262 class nsGetSmartSizeEvent: public nsRunnable
michael@0 263 {
michael@0 264 public:
michael@0 265 nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize,
michael@0 266 bool shouldUseOldMaxSmartSize)
michael@0 267 : mCachePath(cachePath)
michael@0 268 , mCurrentSize(currentSize)
michael@0 269 , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
michael@0 270 {}
michael@0 271
michael@0 272 // Calculates user's disk space available on a background thread and
michael@0 273 // dispatches this value back to the main thread.
michael@0 274 NS_IMETHOD Run()
michael@0 275 {
michael@0 276 uint32_t size;
michael@0 277 size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,
michael@0 278 mCurrentSize,
michael@0 279 mShouldUseOldMaxSmartSize);
michael@0 280 NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
michael@0 281 return NS_OK;
michael@0 282 }
michael@0 283
michael@0 284 private:
michael@0 285 nsString mCachePath;
michael@0 286 uint32_t mCurrentSize;
michael@0 287 bool mShouldUseOldMaxSmartSize;
michael@0 288 };
michael@0 289
michael@0 290 class nsBlockOnCacheThreadEvent : public nsRunnable {
michael@0 291 public:
michael@0 292 nsBlockOnCacheThreadEvent()
michael@0 293 {
michael@0 294 }
michael@0 295 NS_IMETHOD Run()
michael@0 296 {
michael@0 297 nsCacheServiceAutoLock autoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN));
michael@0 298 #ifdef PR_LOGGING
michael@0 299 CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
michael@0 300 #endif
michael@0 301 nsCacheService::gService->mCondVar.Notify();
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304 };
michael@0 305
michael@0 306
michael@0 307 nsresult
michael@0 308 nsCacheProfilePrefObserver::Install()
michael@0 309 {
michael@0 310 // install profile-change observer
michael@0 311 nsCOMPtr<nsIObserverService> observerService =
michael@0 312 mozilla::services::GetObserverService();
michael@0 313 if (!observerService)
michael@0 314 return NS_ERROR_FAILURE;
michael@0 315
michael@0 316 nsresult rv, rv2 = NS_OK;
michael@0 317 for (unsigned int i=0; i<ArrayLength(observerList); i++) {
michael@0 318 rv = observerService->AddObserver(this, observerList[i], false);
michael@0 319 if (NS_FAILED(rv))
michael@0 320 rv2 = rv;
michael@0 321 }
michael@0 322
michael@0 323 // install preferences observer
michael@0 324 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 325 if (!branch) return NS_ERROR_FAILURE;
michael@0 326
michael@0 327 for (unsigned int i=0; i<ArrayLength(prefList); i++) {
michael@0 328 rv = branch->AddObserver(prefList[i], this, false);
michael@0 329 if (NS_FAILED(rv))
michael@0 330 rv2 = rv;
michael@0 331 }
michael@0 332
michael@0 333 // Determine if we have a profile already
michael@0 334 // Install() is called *after* the profile-after-change notification
michael@0 335 // when there is only a single profile, or it is specified on the
michael@0 336 // commandline at startup.
michael@0 337 // In that case, we detect the presence of a profile by the existence
michael@0 338 // of the NS_APP_USER_PROFILE_50_DIR directory.
michael@0 339
michael@0 340 nsCOMPtr<nsIFile> directory;
michael@0 341 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 342 getter_AddRefs(directory));
michael@0 343 if (NS_SUCCEEDED(rv))
michael@0 344 mHaveProfile = true;
michael@0 345
michael@0 346 rv = ReadPrefs(branch);
michael@0 347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 348
michael@0 349 return rv2;
michael@0 350 }
michael@0 351
michael@0 352
michael@0 353 void
michael@0 354 nsCacheProfilePrefObserver::Remove()
michael@0 355 {
michael@0 356 // remove Observer Service observers
michael@0 357 nsCOMPtr<nsIObserverService> obs =
michael@0 358 mozilla::services::GetObserverService();
michael@0 359 if (obs) {
michael@0 360 for (unsigned int i=0; i<ArrayLength(observerList); i++) {
michael@0 361 obs->RemoveObserver(this, observerList[i]);
michael@0 362 }
michael@0 363 }
michael@0 364
michael@0 365 // remove Pref Service observers
michael@0 366 nsCOMPtr<nsIPrefBranch> prefs =
michael@0 367 do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 368 if (!prefs)
michael@0 369 return;
michael@0 370 for (unsigned int i=0; i<ArrayLength(prefList); i++)
michael@0 371 prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
michael@0 372 }
michael@0 373
michael@0 374 void
michael@0 375 nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
michael@0 376 {
michael@0 377 mDiskCacheCapacity = std::max(0, capacity);
michael@0 378 }
michael@0 379
michael@0 380
michael@0 381 NS_IMETHODIMP
michael@0 382 nsCacheProfilePrefObserver::Observe(nsISupports * subject,
michael@0 383 const char * topic,
michael@0 384 const char16_t * data_unicode)
michael@0 385 {
michael@0 386 nsresult rv;
michael@0 387 NS_ConvertUTF16toUTF8 data(data_unicode);
michael@0 388 CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic, data.get()));
michael@0 389
michael@0 390 if (!nsCacheService::IsInitialized()) {
michael@0 391 if (!strcmp("resume_process_notification", topic)) {
michael@0 392 // A suspended process has a closed cache, so re-open it here.
michael@0 393 nsCacheService::GlobalInstance()->Init();
michael@0 394 }
michael@0 395 return NS_OK;
michael@0 396 }
michael@0 397
michael@0 398 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
michael@0 399 // xpcom going away, shutdown cache service
michael@0 400 nsCacheService::GlobalInstance()->Shutdown();
michael@0 401 } else if (!strcmp("profile-before-change", topic)) {
michael@0 402 // profile before change
michael@0 403 mHaveProfile = false;
michael@0 404
michael@0 405 // XXX shutdown devices
michael@0 406 nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
michael@0 407 data.get()));
michael@0 408
michael@0 409 } else if (!strcmp("suspend_process_notification", topic)) {
michael@0 410 // A suspended process may never return, so shutdown the cache to reduce
michael@0 411 // cache corruption.
michael@0 412 nsCacheService::GlobalInstance()->Shutdown();
michael@0 413 } else if (!strcmp("profile-do-change", topic)) {
michael@0 414 // profile after change
michael@0 415 mHaveProfile = true;
michael@0 416 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 417 ReadPrefs(branch);
michael@0 418 nsCacheService::OnProfileChanged();
michael@0 419
michael@0 420 } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
michael@0 421
michael@0 422 // ignore pref changes until we're done switch profiles
michael@0 423 if (!mHaveProfile)
michael@0 424 return NS_OK;
michael@0 425
michael@0 426 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
michael@0 427 if (NS_FAILED(rv))
michael@0 428 return rv;
michael@0 429
michael@0 430 // which preference changed?
michael@0 431 if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
michael@0 432
michael@0 433 rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
michael@0 434 &mDiskCacheEnabled);
michael@0 435 if (NS_FAILED(rv))
michael@0 436 return rv;
michael@0 437 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
michael@0 438
michael@0 439 } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
michael@0 440
michael@0 441 int32_t capacity = 0;
michael@0 442 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
michael@0 443 if (NS_FAILED(rv))
michael@0 444 return rv;
michael@0 445 mDiskCacheCapacity = std::max(0, capacity);
michael@0 446 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
michael@0 447
michael@0 448 // Update the cache capacity when smart sizing is turned on/off
michael@0 449 } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
michael@0 450 // Is the update because smartsizing was turned on, or off?
michael@0 451 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
michael@0 452 &mSmartSizeEnabled);
michael@0 453 if (NS_FAILED(rv))
michael@0 454 return rv;
michael@0 455 int32_t newCapacity = 0;
michael@0 456 if (mSmartSizeEnabled) {
michael@0 457 nsCacheService::SetDiskSmartSize();
michael@0 458 } else {
michael@0 459 // Smart sizing switched off: use user specified size
michael@0 460 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
michael@0 461 if (NS_FAILED(rv))
michael@0 462 return rv;
michael@0 463 mDiskCacheCapacity = std::max(0, newCapacity);
michael@0 464 nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
michael@0 465 }
michael@0 466 } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
michael@0 467 rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
michael@0 468 &mShouldUseOldMaxSmartSize);
michael@0 469 if (NS_FAILED(rv))
michael@0 470 return rv;
michael@0 471 } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
michael@0 472 int32_t newMaxSize;
michael@0 473 rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 474 &newMaxSize);
michael@0 475 if (NS_FAILED(rv))
michael@0 476 return rv;
michael@0 477
michael@0 478 mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
michael@0 479 nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
michael@0 480
michael@0 481 #if 0
michael@0 482 } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
michael@0 483 // XXX We probaby don't want to respond to this pref except after
michael@0 484 // XXX profile changes. Ideally, there should be somekind of user
michael@0 485 // XXX notification that the pref change won't take effect until
michael@0 486 // XXX the next time the profile changes (browser launch)
michael@0 487 #endif
michael@0 488 } else
michael@0 489
michael@0 490 // which preference changed?
michael@0 491 if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
michael@0 492
michael@0 493 rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
michael@0 494 &mOfflineCacheEnabled);
michael@0 495 if (NS_FAILED(rv)) return rv;
michael@0 496 nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
michael@0 497
michael@0 498 } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
michael@0 499
michael@0 500 int32_t capacity = 0;
michael@0 501 rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
michael@0 502 if (NS_FAILED(rv)) return rv;
michael@0 503 mOfflineCacheCapacity = std::max(0, capacity);
michael@0 504 nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
michael@0 505 #if 0
michael@0 506 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
michael@0 507 // XXX We probaby don't want to respond to this pref except after
michael@0 508 // XXX profile changes. Ideally, there should be some kind of user
michael@0 509 // XXX notification that the pref change won't take effect until
michael@0 510 // XXX the next time the profile changes (browser launch)
michael@0 511 #endif
michael@0 512 } else
michael@0 513
michael@0 514 if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
michael@0 515
michael@0 516 rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
michael@0 517 &mMemoryCacheEnabled);
michael@0 518 if (NS_FAILED(rv))
michael@0 519 return rv;
michael@0 520 nsCacheService::SetMemoryCache();
michael@0 521
michael@0 522 } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
michael@0 523
michael@0 524 mMemoryCacheCapacity = -1;
michael@0 525 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
michael@0 526 &mMemoryCacheCapacity);
michael@0 527 nsCacheService::SetMemoryCache();
michael@0 528 } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
michael@0 529 int32_t newMaxSize;
michael@0 530 rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 531 &newMaxSize);
michael@0 532 if (NS_FAILED(rv))
michael@0 533 return rv;
michael@0 534
michael@0 535 mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
michael@0 536 nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
michael@0 537 } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
michael@0 538 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
michael@0 539 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
michael@0 540 &mCacheCompressionLevel);
michael@0 541 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
michael@0 542 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
michael@0 543 } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
michael@0 544 rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
michael@0 545 &mSanitizeOnShutdown);
michael@0 546 if (NS_FAILED(rv))
michael@0 547 return rv;
michael@0 548 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
michael@0 549 } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
michael@0 550 rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
michael@0 551 &mClearCacheOnShutdown);
michael@0 552 if (NS_FAILED(rv))
michael@0 553 return rv;
michael@0 554 nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
michael@0 555 }
michael@0 556 } else if (!strcmp("last-pb-context-exited", topic)) {
michael@0 557 nsCacheService::LeavePrivateBrowsing();
michael@0 558 }
michael@0 559
michael@0 560 return NS_OK;
michael@0 561 }
michael@0 562
michael@0 563 // Returns default ("smart") size (in KB) of cache, given available disk space
michael@0 564 // (also in KB)
michael@0 565 static uint32_t
michael@0 566 SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
michael@0 567 {
michael@0 568 uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
michael@0 569
michael@0 570 if (availKB > 100 * 1024 * 1024)
michael@0 571 return maxSize; // skip computing if we're over 100 GB
michael@0 572
michael@0 573 // Grow/shrink in 10 MB units, deliberately, so that in the common case we
michael@0 574 // don't shrink cache and evict items every time we startup (it's important
michael@0 575 // that we don't slow down startup benchmarks).
michael@0 576 uint32_t sz10MBs = 0;
michael@0 577 uint32_t avail10MBs = availKB / (1024*10);
michael@0 578
michael@0 579 // .5% of space above 25 GB
michael@0 580 if (avail10MBs > 2500) {
michael@0 581 sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
michael@0 582 avail10MBs = 2500;
michael@0 583 }
michael@0 584 // 1% of space between 7GB -> 25 GB
michael@0 585 if (avail10MBs > 700) {
michael@0 586 sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
michael@0 587 avail10MBs = 700;
michael@0 588 }
michael@0 589 // 5% of space between 500 MB -> 7 GB
michael@0 590 if (avail10MBs > 50) {
michael@0 591 sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
michael@0 592 avail10MBs = 50;
michael@0 593 }
michael@0 594
michael@0 595 #ifdef ANDROID
michael@0 596 // On Android, smaller/older devices may have very little storage and
michael@0 597 // device owners may be sensitive to storage footprint: Use a smaller
michael@0 598 // percentage of available space and a smaller minimum.
michael@0 599
michael@0 600 // 20% of space up to 500 MB (10 MB min)
michael@0 601 sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
michael@0 602 #else
michael@0 603 // 40% of space up to 500 MB (50 MB min)
michael@0 604 sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
michael@0 605 #endif
michael@0 606
michael@0 607 return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
michael@0 608 }
michael@0 609
michael@0 610 /* Computes our best guess for the default size of the user's disk cache,
michael@0 611 * based on the amount of space they have free on their hard drive.
michael@0 612 * We use a tiered scheme: the more space available,
michael@0 613 * the larger the disk cache will be. However, we do not want
michael@0 614 * to enable the disk cache to grow to an unbounded size, so the larger the
michael@0 615 * user's available space is, the smaller of a percentage we take. We set a
michael@0 616 * lower bound of 50MB and an upper bound of 1GB.
michael@0 617 *
michael@0 618 *@param: None.
michael@0 619 *@return: The size that the user's disk cache should default to, in kBytes.
michael@0 620 */
michael@0 621 uint32_t
michael@0 622 nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
michael@0 623 uint32_t currentSize,
michael@0 624 bool shouldUseOldMaxSmartSize)
michael@0 625 {
michael@0 626 // Check for free space on device where cache directory lives
michael@0 627 nsresult rv;
michael@0 628 nsCOMPtr<nsIFile>
michael@0 629 cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
michael@0 630 if (NS_FAILED(rv) || !cacheDirectory)
michael@0 631 return DEFAULT_CACHE_SIZE;
michael@0 632 rv = cacheDirectory->InitWithPath(cachePath);
michael@0 633 if (NS_FAILED(rv))
michael@0 634 return DEFAULT_CACHE_SIZE;
michael@0 635 int64_t bytesAvailable;
michael@0 636 rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
michael@0 637 if (NS_FAILED(rv))
michael@0 638 return DEFAULT_CACHE_SIZE;
michael@0 639
michael@0 640 return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
michael@0 641 currentSize),
michael@0 642 shouldUseOldMaxSmartSize);
michael@0 643 }
michael@0 644
michael@0 645 /* Determine if we are permitted to dynamically size the user's disk cache based
michael@0 646 * on their disk space available. We may do this so long as the pref
michael@0 647 * smart_size.enabled is true.
michael@0 648 */
michael@0 649 bool
michael@0 650 nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
michael@0 651 firstRun)
michael@0 652 {
michael@0 653 nsresult rv;
michael@0 654 if (firstRun) {
michael@0 655 // check if user has set cache size in the past
michael@0 656 bool userSet;
michael@0 657 rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
michael@0 658 if (NS_FAILED(rv)) userSet = true;
michael@0 659 if (userSet) {
michael@0 660 int32_t oldCapacity;
michael@0 661 // If user explicitly set cache size to be smaller than old default
michael@0 662 // of 50 MB, then keep user's value. Otherwise use smart sizing.
michael@0 663 rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
michael@0 664 if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
michael@0 665 mSmartSizeEnabled = false;
michael@0 666 branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
michael@0 667 mSmartSizeEnabled);
michael@0 668 return mSmartSizeEnabled;
michael@0 669 }
michael@0 670 }
michael@0 671 // Set manual setting to MAX cache size as starting val for any
michael@0 672 // adjustment by user: (bug 559942 comment 65)
michael@0 673 int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
michael@0 674 branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
michael@0 675 }
michael@0 676
michael@0 677 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
michael@0 678 &mSmartSizeEnabled);
michael@0 679 if (NS_FAILED(rv))
michael@0 680 mSmartSizeEnabled = false;
michael@0 681 return mSmartSizeEnabled;
michael@0 682 }
michael@0 683
michael@0 684
michael@0 685 nsresult
michael@0 686 nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
michael@0 687 {
michael@0 688 nsresult rv = NS_OK;
michael@0 689
michael@0 690 // read disk cache device prefs
michael@0 691 mDiskCacheEnabled = true; // presume disk cache is enabled
michael@0 692 (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
michael@0 693
michael@0 694 mDiskCacheCapacity = DISK_CACHE_CAPACITY;
michael@0 695 (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
michael@0 696 mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
michael@0 697
michael@0 698 (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 699 &mDiskCacheMaxEntrySize);
michael@0 700 mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
michael@0 701
michael@0 702 (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF, // ignore error
michael@0 703 NS_GET_IID(nsIFile),
michael@0 704 getter_AddRefs(mDiskCacheParentDirectory));
michael@0 705
michael@0 706 (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
michael@0 707 &mShouldUseOldMaxSmartSize);
michael@0 708
michael@0 709 if (!mDiskCacheParentDirectory) {
michael@0 710 nsCOMPtr<nsIFile> directory;
michael@0 711
michael@0 712 // try to get the disk cache parent directory
michael@0 713 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
michael@0 714 getter_AddRefs(directory));
michael@0 715 if (NS_FAILED(rv)) {
michael@0 716 // try to get the profile directory (there may not be a profile yet)
michael@0 717 nsCOMPtr<nsIFile> profDir;
michael@0 718 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 719 getter_AddRefs(profDir));
michael@0 720 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
michael@0 721 getter_AddRefs(directory));
michael@0 722 if (!directory)
michael@0 723 directory = profDir;
michael@0 724 else if (profDir) {
michael@0 725 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
michael@0 726 "Cache");
michael@0 727 }
michael@0 728 }
michael@0 729 // use file cache in build tree only if asked, to avoid cache dir litter
michael@0 730 if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
michael@0 731 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
michael@0 732 getter_AddRefs(directory));
michael@0 733 }
michael@0 734 if (directory)
michael@0 735 mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
michael@0 736 }
michael@0 737 if (mDiskCacheParentDirectory) {
michael@0 738 bool firstSmartSizeRun;
michael@0 739 rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
michael@0 740 &firstSmartSizeRun);
michael@0 741 if (NS_FAILED(rv))
michael@0 742 firstSmartSizeRun = false;
michael@0 743 if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
michael@0 744 // Avoid evictions: use previous cache size until smart size event
michael@0 745 // updates mDiskCacheCapacity
michael@0 746 rv = branch->GetIntPref(firstSmartSizeRun ?
michael@0 747 DISK_CACHE_CAPACITY_PREF :
michael@0 748 DISK_CACHE_SMART_SIZE_PREF,
michael@0 749 &mDiskCacheCapacity);
michael@0 750 if (NS_FAILED(rv))
michael@0 751 mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
michael@0 752 }
michael@0 753
michael@0 754 if (firstSmartSizeRun) {
michael@0 755 // It is no longer our first run
michael@0 756 rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
michael@0 757 false);
michael@0 758 if (NS_FAILED(rv))
michael@0 759 NS_WARNING("Failed setting first_run pref in ReadPrefs.");
michael@0 760 }
michael@0 761 }
michael@0 762
michael@0 763 // read offline cache device prefs
michael@0 764 mOfflineCacheEnabled = true; // presume offline cache is enabled
michael@0 765 (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
michael@0 766 &mOfflineCacheEnabled);
michael@0 767
michael@0 768 mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
michael@0 769 (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
michael@0 770 &mOfflineCacheCapacity);
michael@0 771 mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
michael@0 772
michael@0 773 (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error
michael@0 774 NS_GET_IID(nsIFile),
michael@0 775 getter_AddRefs(mOfflineCacheParentDirectory));
michael@0 776
michael@0 777 if (!mOfflineCacheParentDirectory) {
michael@0 778 nsCOMPtr<nsIFile> directory;
michael@0 779
michael@0 780 // try to get the offline cache parent directory
michael@0 781 rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
michael@0 782 getter_AddRefs(directory));
michael@0 783 if (NS_FAILED(rv)) {
michael@0 784 // try to get the profile directory (there may not be a profile yet)
michael@0 785 nsCOMPtr<nsIFile> profDir;
michael@0 786 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 787 getter_AddRefs(profDir));
michael@0 788 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
michael@0 789 getter_AddRefs(directory));
michael@0 790 if (!directory)
michael@0 791 directory = profDir;
michael@0 792 else if (profDir) {
michael@0 793 nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
michael@0 794 "OfflineCache");
michael@0 795 }
michael@0 796 }
michael@0 797 #if DEBUG
michael@0 798 if (!directory) {
michael@0 799 // use current process directory during development
michael@0 800 rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
michael@0 801 getter_AddRefs(directory));
michael@0 802 }
michael@0 803 #endif
michael@0 804 if (directory)
michael@0 805 mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
michael@0 806 }
michael@0 807
michael@0 808 // read memory cache device prefs
michael@0 809 (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
michael@0 810
michael@0 811 mMemoryCacheCapacity = -1;
michael@0 812 (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
michael@0 813 &mMemoryCacheCapacity);
michael@0 814
michael@0 815 (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
michael@0 816 &mMemoryCacheMaxEntrySize);
michael@0 817 mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
michael@0 818
michael@0 819 // read cache compression level pref
michael@0 820 mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
michael@0 821 (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
michael@0 822 &mCacheCompressionLevel);
michael@0 823 mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
michael@0 824 mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
michael@0 825
michael@0 826 // read cache shutdown sanitization prefs
michael@0 827 (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
michael@0 828 &mSanitizeOnShutdown);
michael@0 829 (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
michael@0 830 &mClearCacheOnShutdown);
michael@0 831
michael@0 832 return rv;
michael@0 833 }
michael@0 834
michael@0 835 nsresult
michael@0 836 nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
michael@0 837 {
michael@0 838 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
michael@0 839 return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 840 }
michael@0 841
michael@0 842 nsresult
michael@0 843 nsCacheService::SyncWithCacheIOThread()
michael@0 844 {
michael@0 845 gService->mLock.AssertCurrentThreadOwns();
michael@0 846 if (!gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
michael@0 847
michael@0 848 nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
michael@0 849
michael@0 850 // dispatch event - it will notify the monitor when it's done
michael@0 851 nsresult rv =
michael@0 852 gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 853 if (NS_FAILED(rv)) {
michael@0 854 NS_WARNING("Failed dispatching block-event");
michael@0 855 return NS_ERROR_UNEXPECTED;
michael@0 856 }
michael@0 857
michael@0 858 // wait until notified, then return
michael@0 859 rv = gService->mCondVar.Wait();
michael@0 860
michael@0 861 return rv;
michael@0 862 }
michael@0 863
michael@0 864
michael@0 865 bool
michael@0 866 nsCacheProfilePrefObserver::DiskCacheEnabled()
michael@0 867 {
michael@0 868 if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory)) return false;
michael@0 869 return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
michael@0 870 }
michael@0 871
michael@0 872
michael@0 873 bool
michael@0 874 nsCacheProfilePrefObserver::OfflineCacheEnabled()
michael@0 875 {
michael@0 876 if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
michael@0 877 return false;
michael@0 878
michael@0 879 return mOfflineCacheEnabled;
michael@0 880 }
michael@0 881
michael@0 882
michael@0 883 bool
michael@0 884 nsCacheProfilePrefObserver::MemoryCacheEnabled()
michael@0 885 {
michael@0 886 if (mMemoryCacheCapacity == 0) return false;
michael@0 887 return mMemoryCacheEnabled;
michael@0 888 }
michael@0 889
michael@0 890
michael@0 891 /**
michael@0 892 * MemoryCacheCapacity
michael@0 893 *
michael@0 894 * If the browser.cache.memory.capacity preference is positive, we use that
michael@0 895 * value for the amount of memory available for the cache.
michael@0 896 *
michael@0 897 * If browser.cache.memory.capacity is zero, the memory cache is disabled.
michael@0 898 *
michael@0 899 * If browser.cache.memory.capacity is negative or not present, we use a
michael@0 900 * formula that grows less than linearly with the amount of system memory,
michael@0 901 * with an upper limit on the cache size. No matter how much physical RAM is
michael@0 902 * present, the default cache size would not exceed 32 MB. This maximum would
michael@0 903 * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
michael@0 904 *
michael@0 905 * RAM Cache
michael@0 906 * --- -----
michael@0 907 * 32 Mb 2 Mb
michael@0 908 * 64 Mb 4 Mb
michael@0 909 * 128 Mb 6 Mb
michael@0 910 * 256 Mb 10 Mb
michael@0 911 * 512 Mb 14 Mb
michael@0 912 * 1024 Mb 18 Mb
michael@0 913 * 2048 Mb 24 Mb
michael@0 914 * 4096 Mb 30 Mb
michael@0 915 *
michael@0 916 * The equation for this is (for cache size C and memory size K (kbytes)):
michael@0 917 * x = log2(K) - 14
michael@0 918 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
michael@0 919 * if (C > 32) C = 32
michael@0 920 */
michael@0 921
michael@0 922 int32_t
michael@0 923 nsCacheProfilePrefObserver::MemoryCacheCapacity()
michael@0 924 {
michael@0 925 int32_t capacity = mMemoryCacheCapacity;
michael@0 926 if (capacity >= 0) {
michael@0 927 CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
michael@0 928 return capacity;
michael@0 929 }
michael@0 930
michael@0 931 static uint64_t bytes = PR_GetPhysicalMemorySize();
michael@0 932 CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
michael@0 933
michael@0 934 // If getting the physical memory failed, arbitrarily assume
michael@0 935 // 32 MB of RAM. We use a low default to have a reasonable
michael@0 936 // size on all the devices we support.
michael@0 937 if (bytes == 0)
michael@0 938 bytes = 32 * 1024 * 1024;
michael@0 939
michael@0 940 // Conversion from unsigned int64_t to double doesn't work on all platforms.
michael@0 941 // We need to truncate the value at INT64_MAX to make sure we don't
michael@0 942 // overflow.
michael@0 943 if (bytes > INT64_MAX)
michael@0 944 bytes = INT64_MAX;
michael@0 945
michael@0 946 uint64_t kbytes = bytes >> 10;
michael@0 947
michael@0 948 double kBytesD = double(kbytes);
michael@0 949
michael@0 950 double x = log(kBytesD)/log(2.0) - 14;
michael@0 951 if (x > 0) {
michael@0 952 capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
michael@0 953 if (capacity > 32)
michael@0 954 capacity = 32;
michael@0 955 capacity *= 1024;
michael@0 956 } else {
michael@0 957 capacity = 0;
michael@0 958 }
michael@0 959
michael@0 960 return capacity;
michael@0 961 }
michael@0 962
michael@0 963 int32_t
michael@0 964 nsCacheProfilePrefObserver::CacheCompressionLevel()
michael@0 965 {
michael@0 966 return mCacheCompressionLevel;
michael@0 967 }
michael@0 968
michael@0 969 /******************************************************************************
michael@0 970 * nsProcessRequestEvent
michael@0 971 *****************************************************************************/
michael@0 972
michael@0 973 class nsProcessRequestEvent : public nsRunnable {
michael@0 974 public:
michael@0 975 nsProcessRequestEvent(nsCacheRequest *aRequest)
michael@0 976 {
michael@0 977 MOZ_EVENT_TRACER_NAME_OBJECT(aRequest, aRequest->mKey.get());
michael@0 978 MOZ_EVENT_TRACER_WAIT(aRequest, "net::cache::ProcessRequest");
michael@0 979 mRequest = aRequest;
michael@0 980 }
michael@0 981
michael@0 982 NS_IMETHOD Run()
michael@0 983 {
michael@0 984 nsresult rv;
michael@0 985
michael@0 986 NS_ASSERTION(mRequest->mListener,
michael@0 987 "Sync OpenCacheEntry() posted to background thread!");
michael@0 988
michael@0 989 nsCacheServiceAutoLock lock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN));
michael@0 990 rv = nsCacheService::gService->ProcessRequest(mRequest,
michael@0 991 false,
michael@0 992 nullptr);
michael@0 993
michael@0 994 // Don't delete the request if it was queued
michael@0 995 if (!(mRequest->IsBlocking() &&
michael@0 996 rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
michael@0 997 delete mRequest;
michael@0 998
michael@0 999 return NS_OK;
michael@0 1000 }
michael@0 1001
michael@0 1002 protected:
michael@0 1003 virtual ~nsProcessRequestEvent() {}
michael@0 1004
michael@0 1005 private:
michael@0 1006 nsCacheRequest *mRequest;
michael@0 1007 };
michael@0 1008
michael@0 1009 /******************************************************************************
michael@0 1010 * nsDoomEvent
michael@0 1011 *****************************************************************************/
michael@0 1012
michael@0 1013 class nsDoomEvent : public nsRunnable {
michael@0 1014 public:
michael@0 1015 nsDoomEvent(nsCacheSession *session,
michael@0 1016 const nsACString &key,
michael@0 1017 nsICacheListener *listener)
michael@0 1018 {
michael@0 1019 mKey = *session->ClientID();
michael@0 1020 mKey.Append(':');
michael@0 1021 mKey.Append(key);
michael@0 1022 mStoragePolicy = session->StoragePolicy();
michael@0 1023 mListener = listener;
michael@0 1024 mThread = do_GetCurrentThread();
michael@0 1025 // We addref the listener here and release it in nsNotifyDoomListener
michael@0 1026 // on the callers thread. If posting of nsNotifyDoomListener event fails
michael@0 1027 // we leak the listener which is better than releasing it on a wrong
michael@0 1028 // thread.
michael@0 1029 NS_IF_ADDREF(mListener);
michael@0 1030 }
michael@0 1031
michael@0 1032 NS_IMETHOD Run()
michael@0 1033 {
michael@0 1034 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDOOMEVENT_RUN));
michael@0 1035
michael@0 1036 bool foundActive = true;
michael@0 1037 nsresult status = NS_ERROR_NOT_AVAILABLE;
michael@0 1038 nsCacheEntry *entry;
michael@0 1039 entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
michael@0 1040 if (!entry) {
michael@0 1041 bool collision = false;
michael@0 1042 foundActive = false;
michael@0 1043 entry = nsCacheService::gService->SearchCacheDevices(&mKey,
michael@0 1044 mStoragePolicy,
michael@0 1045 &collision);
michael@0 1046 }
michael@0 1047
michael@0 1048 if (entry) {
michael@0 1049 status = NS_OK;
michael@0 1050 nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
michael@0 1051 }
michael@0 1052
michael@0 1053 if (mListener) {
michael@0 1054 mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
michael@0 1055 NS_DISPATCH_NORMAL);
michael@0 1056 // posted event will release the reference on the correct thread
michael@0 1057 mListener = nullptr;
michael@0 1058 }
michael@0 1059
michael@0 1060 return NS_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 private:
michael@0 1064 nsCString mKey;
michael@0 1065 nsCacheStoragePolicy mStoragePolicy;
michael@0 1066 nsICacheListener *mListener;
michael@0 1067 nsCOMPtr<nsIThread> mThread;
michael@0 1068 };
michael@0 1069
michael@0 1070 /******************************************************************************
michael@0 1071 * nsCacheService
michael@0 1072 *****************************************************************************/
michael@0 1073 nsCacheService * nsCacheService::gService = nullptr;
michael@0 1074
michael@0 1075 NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
michael@0 1076 nsIMemoryReporter)
michael@0 1077
michael@0 1078 nsCacheService::nsCacheService()
michael@0 1079 : mObserver(nullptr),
michael@0 1080 mLock("nsCacheService.mLock"),
michael@0 1081 mCondVar(mLock, "nsCacheService.mCondVar"),
michael@0 1082 mTimeStampLock("nsCacheService.mTimeStampLock"),
michael@0 1083 mInitialized(false),
michael@0 1084 mClearingEntries(false),
michael@0 1085 mEnableMemoryDevice(true),
michael@0 1086 mEnableDiskDevice(true),
michael@0 1087 mMemoryDevice(nullptr),
michael@0 1088 mDiskDevice(nullptr),
michael@0 1089 mOfflineDevice(nullptr),
michael@0 1090 mTotalEntries(0),
michael@0 1091 mCacheHits(0),
michael@0 1092 mCacheMisses(0),
michael@0 1093 mMaxKeyLength(0),
michael@0 1094 mMaxDataSize(0),
michael@0 1095 mMaxMetaSize(0),
michael@0 1096 mDeactivateFailures(0),
michael@0 1097 mDeactivatedUnboundEntries(0)
michael@0 1098 {
michael@0 1099 NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
michael@0 1100 gService = this;
michael@0 1101
michael@0 1102 // create list of cache devices
michael@0 1103 PR_INIT_CLIST(&mDoomedEntries);
michael@0 1104 }
michael@0 1105
michael@0 1106 nsCacheService::~nsCacheService()
michael@0 1107 {
michael@0 1108 if (mInitialized) // Shutdown hasn't been called yet.
michael@0 1109 (void) Shutdown();
michael@0 1110
michael@0 1111 if (mObserver) {
michael@0 1112 mObserver->Remove();
michael@0 1113 NS_RELEASE(mObserver);
michael@0 1114 }
michael@0 1115
michael@0 1116 gService = nullptr;
michael@0 1117 }
michael@0 1118
michael@0 1119
michael@0 1120 nsresult
michael@0 1121 nsCacheService::Init()
michael@0 1122 {
michael@0 1123 // Thie method must be called on the main thread because mCacheIOThread must
michael@0 1124 // only be modified on the main thread.
michael@0 1125 if (!NS_IsMainThread()) {
michael@0 1126 NS_ERROR("nsCacheService::Init called off the main thread");
michael@0 1127 return NS_ERROR_NOT_SAME_THREAD;
michael@0 1128 }
michael@0 1129
michael@0 1130 NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
michael@0 1131 if (mInitialized)
michael@0 1132 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 1133
michael@0 1134 if (mozilla::net::IsNeckoChild()) {
michael@0 1135 return NS_ERROR_UNEXPECTED;
michael@0 1136 }
michael@0 1137
michael@0 1138 CACHE_LOG_INIT();
michael@0 1139
michael@0 1140 nsresult rv;
michael@0 1141
michael@0 1142 mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
michael@0 1143 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1144
michael@0 1145 MOZ_EVENT_TRACER_NAME_OBJECT(nsCacheService::gService, "nsCacheService");
michael@0 1146
michael@0 1147 rv = NS_NewNamedThread("Cache I/O",
michael@0 1148 getter_AddRefs(mCacheIOThread));
michael@0 1149 if (NS_FAILED(rv)) {
michael@0 1150 NS_RUNTIMEABORT("Can't create cache IO thread");
michael@0 1151 }
michael@0 1152
michael@0 1153 rv = nsDeleteDir::Init();
michael@0 1154 if (NS_FAILED(rv)) {
michael@0 1155 NS_WARNING("Can't initialize nsDeleteDir");
michael@0 1156 }
michael@0 1157
michael@0 1158 // initialize hashtable for active cache entries
michael@0 1159 rv = mActiveEntries.Init();
michael@0 1160 if (NS_FAILED(rv)) return rv;
michael@0 1161
michael@0 1162 // create profile/preference observer
michael@0 1163 if (!mObserver) {
michael@0 1164 mObserver = new nsCacheProfilePrefObserver();
michael@0 1165 NS_ADDREF(mObserver);
michael@0 1166 mObserver->Install();
michael@0 1167 }
michael@0 1168
michael@0 1169 mEnableDiskDevice = mObserver->DiskCacheEnabled();
michael@0 1170 mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
michael@0 1171 mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
michael@0 1172
michael@0 1173 RegisterWeakMemoryReporter(this);
michael@0 1174
michael@0 1175 mInitialized = true;
michael@0 1176 return NS_OK;
michael@0 1177 }
michael@0 1178
michael@0 1179 // static
michael@0 1180 PLDHashOperator
michael@0 1181 nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir,
michael@0 1182 nsRefPtr<nsOfflineCacheDevice>& aDevice,
michael@0 1183 void* aUserArg)
michael@0 1184 {
michael@0 1185 aDevice->Shutdown();
michael@0 1186 return PL_DHASH_REMOVE;
michael@0 1187 }
michael@0 1188
michael@0 1189 void
michael@0 1190 nsCacheService::Shutdown()
michael@0 1191 {
michael@0 1192 // This method must be called on the main thread because mCacheIOThread must
michael@0 1193 // only be modified on the main thread.
michael@0 1194 if (!NS_IsMainThread()) {
michael@0 1195 NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
michael@0 1196 }
michael@0 1197
michael@0 1198 nsCOMPtr<nsIThread> cacheIOThread;
michael@0 1199 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer;
michael@0 1200
michael@0 1201 bool shouldSanitize = false;
michael@0 1202 nsCOMPtr<nsIFile> parentDir;
michael@0 1203
michael@0 1204 {
michael@0 1205 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
michael@0 1206 NS_ASSERTION(mInitialized,
michael@0 1207 "can't shutdown nsCacheService unless it has been initialized.");
michael@0 1208 if (!mInitialized)
michael@0 1209 return;
michael@0 1210
michael@0 1211 mClearingEntries = true;
michael@0 1212 DoomActiveEntries(nullptr);
michael@0 1213 }
michael@0 1214
michael@0 1215 CloseAllStreams();
michael@0 1216
michael@0 1217 UnregisterWeakMemoryReporter(this);
michael@0 1218
michael@0 1219 {
michael@0 1220 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));
michael@0 1221 NS_ASSERTION(mInitialized, "Bad state");
michael@0 1222
michael@0 1223 mInitialized = false;
michael@0 1224
michael@0 1225 // Clear entries
michael@0 1226 ClearDoomList();
michael@0 1227
michael@0 1228 if (mSmartSizeTimer) {
michael@0 1229 mSmartSizeTimer->Cancel();
michael@0 1230 mSmartSizeTimer = nullptr;
michael@0 1231 }
michael@0 1232
michael@0 1233 // Make sure to wait for any pending cache-operations before
michael@0 1234 // proceeding with destructive actions (bug #620660)
michael@0 1235 (void) SyncWithCacheIOThread();
michael@0 1236
michael@0 1237 // obtain the disk cache directory in case we need to sanitize it
michael@0 1238 parentDir = mObserver->DiskCacheParentDirectory();
michael@0 1239 shouldSanitize = mObserver->SanitizeAtShutdown();
michael@0 1240
michael@0 1241 // deallocate memory and disk caches
michael@0 1242 delete mMemoryDevice;
michael@0 1243 mMemoryDevice = nullptr;
michael@0 1244
michael@0 1245 delete mDiskDevice;
michael@0 1246 mDiskDevice = nullptr;
michael@0 1247
michael@0 1248 if (mOfflineDevice)
michael@0 1249 mOfflineDevice->Shutdown();
michael@0 1250
michael@0 1251 NS_IF_RELEASE(mOfflineDevice);
michael@0 1252
michael@0 1253 mCustomOfflineDevices.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
michael@0 1254
michael@0 1255 #ifdef PR_LOGGING
michael@0 1256 LogCacheStatistics();
michael@0 1257 #endif
michael@0 1258
michael@0 1259 mClearingEntries = false;
michael@0 1260 mCacheIOThread.swap(cacheIOThread);
michael@0 1261 }
michael@0 1262
michael@0 1263 if (cacheIOThread)
michael@0 1264 nsShutdownThread::BlockingShutdown(cacheIOThread);
michael@0 1265
michael@0 1266 if (shouldSanitize) {
michael@0 1267 nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
michael@0 1268 if (NS_SUCCEEDED(rv)) {
michael@0 1269 bool exists;
michael@0 1270 if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
michael@0 1271 nsDeleteDir::DeleteDir(parentDir, false);
michael@0 1272 }
michael@0 1273 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer;
michael@0 1274 nsDeleteDir::Shutdown(shouldSanitize);
michael@0 1275 } else {
michael@0 1276 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer;
michael@0 1277 nsDeleteDir::Shutdown(shouldSanitize);
michael@0 1278 }
michael@0 1279 }
michael@0 1280
michael@0 1281
michael@0 1282 nsresult
michael@0 1283 nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
michael@0 1284 {
michael@0 1285 nsresult rv;
michael@0 1286
michael@0 1287 if (aOuter != nullptr)
michael@0 1288 return NS_ERROR_NO_AGGREGATION;
michael@0 1289
michael@0 1290 nsCacheService * cacheService = new nsCacheService();
michael@0 1291 if (cacheService == nullptr)
michael@0 1292 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1293
michael@0 1294 NS_ADDREF(cacheService);
michael@0 1295 rv = cacheService->Init();
michael@0 1296 if (NS_SUCCEEDED(rv)) {
michael@0 1297 rv = cacheService->QueryInterface(aIID, aResult);
michael@0 1298 }
michael@0 1299 NS_RELEASE(cacheService);
michael@0 1300 return rv;
michael@0 1301 }
michael@0 1302
michael@0 1303
michael@0 1304 NS_IMETHODIMP
michael@0 1305 nsCacheService::CreateSession(const char * clientID,
michael@0 1306 nsCacheStoragePolicy storagePolicy,
michael@0 1307 bool streamBased,
michael@0 1308 nsICacheSession **result)
michael@0 1309 {
michael@0 1310 *result = nullptr;
michael@0 1311
michael@0 1312 if (this == nullptr) return NS_ERROR_NOT_AVAILABLE;
michael@0 1313
michael@0 1314 nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased);
michael@0 1315 if (!session) return NS_ERROR_OUT_OF_MEMORY;
michael@0 1316
michael@0 1317 NS_ADDREF(*result = session);
michael@0 1318
michael@0 1319 return NS_OK;
michael@0 1320 }
michael@0 1321
michael@0 1322
michael@0 1323 nsresult
michael@0 1324 nsCacheService::EvictEntriesForSession(nsCacheSession * session)
michael@0 1325 {
michael@0 1326 NS_ASSERTION(gService, "nsCacheService::gService is null.");
michael@0 1327 return gService->EvictEntriesForClient(session->ClientID()->get(),
michael@0 1328 session->StoragePolicy());
michael@0 1329 }
michael@0 1330
michael@0 1331 namespace {
michael@0 1332
michael@0 1333 class EvictionNotifierRunnable : public nsRunnable
michael@0 1334 {
michael@0 1335 public:
michael@0 1336 EvictionNotifierRunnable(nsISupports* aSubject)
michael@0 1337 : mSubject(aSubject)
michael@0 1338 { }
michael@0 1339
michael@0 1340 NS_DECL_NSIRUNNABLE
michael@0 1341
michael@0 1342 private:
michael@0 1343 nsCOMPtr<nsISupports> mSubject;
michael@0 1344 };
michael@0 1345
michael@0 1346 NS_IMETHODIMP
michael@0 1347 EvictionNotifierRunnable::Run()
michael@0 1348 {
michael@0 1349 nsCOMPtr<nsIObserverService> obsSvc =
michael@0 1350 mozilla::services::GetObserverService();
michael@0 1351 if (obsSvc) {
michael@0 1352 obsSvc->NotifyObservers(mSubject,
michael@0 1353 NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
michael@0 1354 nullptr);
michael@0 1355 }
michael@0 1356 return NS_OK;
michael@0 1357 }
michael@0 1358
michael@0 1359 } // anonymous namespace
michael@0 1360
michael@0 1361 nsresult
michael@0 1362 nsCacheService::EvictEntriesForClient(const char * clientID,
michael@0 1363 nsCacheStoragePolicy storagePolicy)
michael@0 1364 {
michael@0 1365 nsRefPtr<EvictionNotifierRunnable> r =
michael@0 1366 new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
michael@0 1367 NS_DispatchToMainThread(r);
michael@0 1368
michael@0 1369 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
michael@0 1370 nsresult res = NS_OK;
michael@0 1371
michael@0 1372 if (storagePolicy == nsICache::STORE_ANYWHERE ||
michael@0 1373 storagePolicy == nsICache::STORE_ON_DISK) {
michael@0 1374
michael@0 1375 if (mEnableDiskDevice) {
michael@0 1376 nsresult rv = NS_OK;
michael@0 1377 if (!mDiskDevice)
michael@0 1378 rv = CreateDiskDevice();
michael@0 1379 if (mDiskDevice)
michael@0 1380 rv = mDiskDevice->EvictEntries(clientID);
michael@0 1381 if (NS_FAILED(rv))
michael@0 1382 res = rv;
michael@0 1383 }
michael@0 1384 }
michael@0 1385
michael@0 1386 // Only clear the offline cache if it has been specifically asked for.
michael@0 1387 if (storagePolicy == nsICache::STORE_OFFLINE) {
michael@0 1388 if (mEnableOfflineDevice) {
michael@0 1389 nsresult rv = NS_OK;
michael@0 1390 if (!mOfflineDevice)
michael@0 1391 rv = CreateOfflineDevice();
michael@0 1392 if (mOfflineDevice)
michael@0 1393 rv = mOfflineDevice->EvictEntries(clientID);
michael@0 1394 if (NS_FAILED(rv))
michael@0 1395 res = rv;
michael@0 1396 }
michael@0 1397 }
michael@0 1398
michael@0 1399 if (storagePolicy == nsICache::STORE_ANYWHERE ||
michael@0 1400 storagePolicy == nsICache::STORE_IN_MEMORY) {
michael@0 1401 // If there is no memory device, there is no need to evict it...
michael@0 1402 if (mMemoryDevice) {
michael@0 1403 nsresult rv = mMemoryDevice->EvictEntries(clientID);
michael@0 1404 if (NS_FAILED(rv))
michael@0 1405 res = rv;
michael@0 1406 }
michael@0 1407 }
michael@0 1408
michael@0 1409 return res;
michael@0 1410 }
michael@0 1411
michael@0 1412
michael@0 1413 nsresult
michael@0 1414 nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
michael@0 1415 bool * result)
michael@0 1416 {
michael@0 1417 if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
michael@0 1418 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY));
michael@0 1419
michael@0 1420 *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
michael@0 1421 return NS_OK;
michael@0 1422 }
michael@0 1423
michael@0 1424
michael@0 1425 nsresult
michael@0 1426 nsCacheService::DoomEntry(nsCacheSession *session,
michael@0 1427 const nsACString &key,
michael@0 1428 nsICacheListener *listener)
michael@0 1429 {
michael@0 1430 CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
michael@0 1431 session, PromiseFlatCString(key).get()));
michael@0 1432 NS_ASSERTION(gService, "nsCacheService::gService is null.");
michael@0 1433
michael@0 1434 if (!gService->mInitialized)
michael@0 1435 return NS_ERROR_NOT_INITIALIZED;
michael@0 1436
michael@0 1437 return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
michael@0 1438 }
michael@0 1439
michael@0 1440
michael@0 1441 bool
michael@0 1442 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
michael@0 1443 {
michael@0 1444 if (gService->mEnableMemoryDevice &&
michael@0 1445 (storagePolicy == nsICache::STORE_ANYWHERE ||
michael@0 1446 storagePolicy == nsICache::STORE_IN_MEMORY)) {
michael@0 1447 return true;
michael@0 1448 }
michael@0 1449 if (gService->mEnableDiskDevice &&
michael@0 1450 (storagePolicy == nsICache::STORE_ANYWHERE ||
michael@0 1451 storagePolicy == nsICache::STORE_ON_DISK)) {
michael@0 1452 return true;
michael@0 1453 }
michael@0 1454 if (gService->mEnableOfflineDevice &&
michael@0 1455 storagePolicy == nsICache::STORE_OFFLINE) {
michael@0 1456 return true;
michael@0 1457 }
michael@0 1458
michael@0 1459 return false;
michael@0 1460 }
michael@0 1461
michael@0 1462 NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
michael@0 1463 {
michael@0 1464 NS_ENSURE_ARG_POINTER(visitor);
michael@0 1465
michael@0 1466 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES));
michael@0 1467
michael@0 1468 if (!(mEnableDiskDevice || mEnableMemoryDevice))
michael@0 1469 return NS_ERROR_NOT_AVAILABLE;
michael@0 1470
michael@0 1471 // XXX record the fact that a visitation is in progress,
michael@0 1472 // XXX i.e. keep list of visitors in progress.
michael@0 1473
michael@0 1474 nsresult rv = NS_OK;
michael@0 1475 // If there is no memory device, there are then also no entries to visit...
michael@0 1476 if (mMemoryDevice) {
michael@0 1477 rv = mMemoryDevice->Visit(visitor);
michael@0 1478 if (NS_FAILED(rv)) return rv;
michael@0 1479 }
michael@0 1480
michael@0 1481 if (mEnableDiskDevice) {
michael@0 1482 if (!mDiskDevice) {
michael@0 1483 rv = CreateDiskDevice();
michael@0 1484 if (NS_FAILED(rv)) return rv;
michael@0 1485 }
michael@0 1486 rv = mDiskDevice->Visit(visitor);
michael@0 1487 if (NS_FAILED(rv)) return rv;
michael@0 1488 }
michael@0 1489
michael@0 1490 if (mEnableOfflineDevice) {
michael@0 1491 if (!mOfflineDevice) {
michael@0 1492 rv = CreateOfflineDevice();
michael@0 1493 if (NS_FAILED(rv)) return rv;
michael@0 1494 }
michael@0 1495 rv = mOfflineDevice->Visit(visitor);
michael@0 1496 if (NS_FAILED(rv)) return rv;
michael@0 1497 }
michael@0 1498
michael@0 1499 // XXX notify any shutdown process that visitation is complete for THIS visitor.
michael@0 1500 // XXX keep queue of visitors
michael@0 1501
michael@0 1502 return NS_OK;
michael@0 1503 }
michael@0 1504
michael@0 1505 void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
michael@0 1506 {
michael@0 1507 MOZ_ASSERT(NS_IsMainThread());
michael@0 1508 nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
michael@0 1509 if (obsvc) {
michael@0 1510 obsvc->NotifyObservers(nullptr,
michael@0 1511 "network-clear-cache-stored-anywhere",
michael@0 1512 nullptr);
michael@0 1513 }
michael@0 1514 }
michael@0 1515
michael@0 1516 NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
michael@0 1517 {
michael@0 1518 if (storagePolicy == nsICache::STORE_ANYWHERE) {
michael@0 1519 // if not called on main thread, dispatch the notification to the main thread to notify observers
michael@0 1520 if (!NS_IsMainThread()) {
michael@0 1521 nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this,
michael@0 1522 &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
michael@0 1523 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
michael@0 1524 } else {
michael@0 1525 // else you're already on main thread - notify observers
michael@0 1526 FireClearNetworkCacheStoredAnywhereNotification();
michael@0 1527 }
michael@0 1528 }
michael@0 1529
michael@0 1530 NS_IMETHODIMP r;
michael@0 1531 r = EvictEntriesForClient(nullptr, storagePolicy);
michael@0 1532
michael@0 1533 // XXX: Bloody hack until we get this notifier in FF14.0:
michael@0 1534 // https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsICacheListener#onCacheEntryDoomed%28%29
michael@0 1535 if (storagePolicy == nsICache::STORE_ANYWHERE &&
michael@0 1536 NS_IsMainThread() && gService && gService->mInitialized) {
michael@0 1537 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));
michael@0 1538 gService->mClearingEntries = true;
michael@0 1539 gService->DoomActiveEntries(nullptr);
michael@0 1540 gService->ClearDoomList();
michael@0 1541 (void) SyncWithCacheIOThread();
michael@0 1542 gService->mClearingEntries = false;
michael@0 1543 }
michael@0 1544 return r;
michael@0 1545 }
michael@0 1546
michael@0 1547 NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
michael@0 1548 {
michael@0 1549 NS_ENSURE_ARG_POINTER(aCacheIOTarget);
michael@0 1550
michael@0 1551 // Because mCacheIOThread can only be changed on the main thread, it can be
michael@0 1552 // read from the main thread without the lock. This is useful to prevent
michael@0 1553 // blocking the main thread on other cache operations.
michael@0 1554 if (!NS_IsMainThread()) {
michael@0 1555 Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET));
michael@0 1556 }
michael@0 1557
michael@0 1558 nsresult rv;
michael@0 1559 if (mCacheIOThread) {
michael@0 1560 NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
michael@0 1561 rv = NS_OK;
michael@0 1562 } else {
michael@0 1563 *aCacheIOTarget = nullptr;
michael@0 1564 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 1565 }
michael@0 1566
michael@0 1567 if (!NS_IsMainThread()) {
michael@0 1568 Unlock();
michael@0 1569 }
michael@0 1570
michael@0 1571 return rv;
michael@0 1572 }
michael@0 1573
michael@0 1574 /* nsICacheServiceInternal
michael@0 1575 * readonly attribute double lockHeldTime;
michael@0 1576 */
michael@0 1577 NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
michael@0 1578 {
michael@0 1579 MutexAutoLock lock(mTimeStampLock);
michael@0 1580
michael@0 1581 if (mLockAcquiredTimeStamp.IsNull()) {
michael@0 1582 *aLockHeldTime = 0.0;
michael@0 1583 }
michael@0 1584 else {
michael@0 1585 *aLockHeldTime =
michael@0 1586 (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
michael@0 1587 }
michael@0 1588
michael@0 1589 return NS_OK;
michael@0 1590 }
michael@0 1591
michael@0 1592 /**
michael@0 1593 * Internal Methods
michael@0 1594 */
michael@0 1595 nsresult
michael@0 1596 nsCacheService::CreateDiskDevice()
michael@0 1597 {
michael@0 1598 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
michael@0 1599 if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
michael@0 1600 if (mDiskDevice) return NS_OK;
michael@0 1601
michael@0 1602 mDiskDevice = new nsDiskCacheDevice;
michael@0 1603 if (!mDiskDevice) return NS_ERROR_OUT_OF_MEMORY;
michael@0 1604
michael@0 1605 // set the preferences
michael@0 1606 mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
michael@0 1607 mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
michael@0 1608 mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
michael@0 1609
michael@0 1610 nsresult rv = mDiskDevice->Init();
michael@0 1611 if (NS_FAILED(rv)) {
michael@0 1612 #if DEBUG
michael@0 1613 printf("###\n");
michael@0 1614 printf("### mDiskDevice->Init() failed (0x%.8x)\n",
michael@0 1615 static_cast<uint32_t>(rv));
michael@0 1616 printf("### - disabling disk cache for this session.\n");
michael@0 1617 printf("###\n");
michael@0 1618 #endif
michael@0 1619 mEnableDiskDevice = false;
michael@0 1620 delete mDiskDevice;
michael@0 1621 mDiskDevice = nullptr;
michael@0 1622 return rv;
michael@0 1623 }
michael@0 1624
michael@0 1625 Telemetry::Accumulate(Telemetry::DISK_CACHE_SMART_SIZE_USING_OLD_MAX,
michael@0 1626 mObserver->ShouldUseOldMaxSmartSize());
michael@0 1627
michael@0 1628 NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
michael@0 1629
michael@0 1630 // Disk device is usually created during the startup. Delay smart size
michael@0 1631 // calculation to avoid possible massive IO caused by eviction of entries
michael@0 1632 // in case the new smart size is smaller than current cache usage.
michael@0 1633 mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 1634 if (NS_SUCCEEDED(rv)) {
michael@0 1635 rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
michael@0 1636 1000*60*3,
michael@0 1637 nsITimer::TYPE_ONE_SHOT);
michael@0 1638 if (NS_FAILED(rv)) {
michael@0 1639 NS_WARNING("Failed to post smart size timer");
michael@0 1640 mSmartSizeTimer = nullptr;
michael@0 1641 }
michael@0 1642 } else {
michael@0 1643 NS_WARNING("Can't create smart size timer");
michael@0 1644 }
michael@0 1645 // Ignore state of the timer and return success since the purpose of the
michael@0 1646 // method (create the disk-device) has been fulfilled
michael@0 1647
michael@0 1648 return NS_OK;
michael@0 1649 }
michael@0 1650
michael@0 1651 // Runnable sent from cache thread to main thread
michael@0 1652 class nsDisableOldMaxSmartSizePrefEvent: public nsRunnable
michael@0 1653 {
michael@0 1654 public:
michael@0 1655 nsDisableOldMaxSmartSizePrefEvent() {}
michael@0 1656
michael@0 1657 NS_IMETHOD Run()
michael@0 1658 {
michael@0 1659 // Main thread may have already called nsCacheService::Shutdown
michael@0 1660 if (!nsCacheService::IsInitialized())
michael@0 1661 return NS_ERROR_NOT_AVAILABLE;
michael@0 1662
michael@0 1663 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 1664 if (!branch) {
michael@0 1665 return NS_ERROR_NOT_AVAILABLE;
michael@0 1666 }
michael@0 1667
michael@0 1668 nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
michael@0 1669 if (NS_FAILED(rv)) {
michael@0 1670 NS_WARNING("Failed to disable old max smart size");
michael@0 1671 return rv;
michael@0 1672 }
michael@0 1673
michael@0 1674 // It is safe to call SetDiskSmartSize_Locked() without holding the lock
michael@0 1675 // when we are on main thread and nsCacheService is initialized.
michael@0 1676 nsCacheService::gService->SetDiskSmartSize_Locked();
michael@0 1677
michael@0 1678 if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) {
michael@0 1679 rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
michael@0 1680 if (NS_FAILED(rv)) {
michael@0 1681 NS_WARNING("Failed to set cache capacity pref");
michael@0 1682 }
michael@0 1683 }
michael@0 1684
michael@0 1685 return NS_OK;
michael@0 1686 }
michael@0 1687 };
michael@0 1688
michael@0 1689 void
michael@0 1690 nsCacheService::MarkStartingFresh()
michael@0 1691 {
michael@0 1692 if (!gService->mObserver->ShouldUseOldMaxSmartSize()) {
michael@0 1693 // Already using new max, nothing to do here
michael@0 1694 return;
michael@0 1695 }
michael@0 1696
michael@0 1697 gService->mObserver->SetUseNewMaxSmartSize(true);
michael@0 1698
michael@0 1699 // We always dispatch an event here because we don't want to deal with lock
michael@0 1700 // reentrance issues.
michael@0 1701 NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
michael@0 1702 }
michael@0 1703
michael@0 1704 nsresult
michael@0 1705 nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
michael@0 1706 {
michael@0 1707 if (!mOfflineDevice) {
michael@0 1708 nsresult rv = CreateOfflineDevice();
michael@0 1709 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1710 }
michael@0 1711
michael@0 1712 NS_ADDREF(*aDevice = mOfflineDevice);
michael@0 1713 return NS_OK;
michael@0 1714 }
michael@0 1715
michael@0 1716 nsresult
michael@0 1717 nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
michael@0 1718 int32_t aQuota,
michael@0 1719 nsOfflineCacheDevice **aDevice)
michael@0 1720 {
michael@0 1721 nsresult rv;
michael@0 1722
michael@0 1723 nsAutoString profilePath;
michael@0 1724 rv = aProfileDir->GetPath(profilePath);
michael@0 1725 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1726
michael@0 1727 if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
michael@0 1728 rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
michael@0 1729 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1730
michael@0 1731 (*aDevice)->SetAutoShutdown();
michael@0 1732 mCustomOfflineDevices.Put(profilePath, *aDevice);
michael@0 1733 }
michael@0 1734
michael@0 1735 return NS_OK;
michael@0 1736 }
michael@0 1737
michael@0 1738 nsresult
michael@0 1739 nsCacheService::CreateOfflineDevice()
michael@0 1740 {
michael@0 1741 CACHE_LOG_ALWAYS(("Creating default offline device"));
michael@0 1742
michael@0 1743 if (mOfflineDevice) return NS_OK;
michael@0 1744 if (!nsCacheService::IsInitialized()) {
michael@0 1745 return NS_ERROR_NOT_AVAILABLE;
michael@0 1746 }
michael@0 1747
michael@0 1748 nsresult rv = CreateCustomOfflineDevice(
michael@0 1749 mObserver->OfflineCacheParentDirectory(),
michael@0 1750 mObserver->OfflineCacheCapacity(),
michael@0 1751 &mOfflineDevice);
michael@0 1752 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1753
michael@0 1754 return NS_OK;
michael@0 1755 }
michael@0 1756
michael@0 1757 nsresult
michael@0 1758 nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
michael@0 1759 int32_t aQuota,
michael@0 1760 nsOfflineCacheDevice **aDevice)
michael@0 1761 {
michael@0 1762 NS_ENSURE_ARG(aProfileDir);
michael@0 1763
michael@0 1764 #if defined(PR_LOGGING)
michael@0 1765 nsAutoCString profilePath;
michael@0 1766 aProfileDir->GetNativePath(profilePath);
michael@0 1767 CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
michael@0 1768 profilePath.BeginReading(), aQuota));
michael@0 1769 #endif
michael@0 1770
michael@0 1771 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
michael@0 1772 if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
michael@0 1773
michael@0 1774 *aDevice = new nsOfflineCacheDevice;
michael@0 1775
michael@0 1776 NS_ADDREF(*aDevice);
michael@0 1777
michael@0 1778 // set the preferences
michael@0 1779 (*aDevice)->SetCacheParentDirectory(aProfileDir);
michael@0 1780 (*aDevice)->SetCapacity(aQuota);
michael@0 1781
michael@0 1782 nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
michael@0 1783 if (NS_FAILED(rv)) {
michael@0 1784 CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv));
michael@0 1785 CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
michael@0 1786
michael@0 1787 NS_RELEASE(*aDevice);
michael@0 1788 }
michael@0 1789 return rv;
michael@0 1790 }
michael@0 1791
michael@0 1792 nsresult
michael@0 1793 nsCacheService::CreateMemoryDevice()
michael@0 1794 {
michael@0 1795 if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
michael@0 1796 if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
michael@0 1797 if (mMemoryDevice) return NS_OK;
michael@0 1798
michael@0 1799 mMemoryDevice = new nsMemoryCacheDevice;
michael@0 1800 if (!mMemoryDevice) return NS_ERROR_OUT_OF_MEMORY;
michael@0 1801
michael@0 1802 // set preference
michael@0 1803 int32_t capacity = mObserver->MemoryCacheCapacity();
michael@0 1804 CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
michael@0 1805 mMemoryDevice->SetCapacity(capacity);
michael@0 1806 mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
michael@0 1807
michael@0 1808 nsresult rv = mMemoryDevice->Init();
michael@0 1809 if (NS_FAILED(rv)) {
michael@0 1810 NS_WARNING("Initialization of Memory Cache failed.");
michael@0 1811 delete mMemoryDevice;
michael@0 1812 mMemoryDevice = nullptr;
michael@0 1813 }
michael@0 1814
michael@0 1815 return rv;
michael@0 1816 }
michael@0 1817
michael@0 1818 nsresult
michael@0 1819 nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
michael@0 1820 {
michael@0 1821 nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
michael@0 1822 if (!profileDir)
michael@0 1823 return NS_ERROR_UNEXPECTED;
michael@0 1824
michael@0 1825 nsAutoString profilePath;
michael@0 1826 nsresult rv = profileDir->GetPath(profilePath);
michael@0 1827 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1828
michael@0 1829 mCustomOfflineDevices.Remove(profilePath);
michael@0 1830 return NS_OK;
michael@0 1831 }
michael@0 1832
michael@0 1833 nsresult
michael@0 1834 nsCacheService::CreateRequest(nsCacheSession * session,
michael@0 1835 const nsACString & clientKey,
michael@0 1836 nsCacheAccessMode accessRequested,
michael@0 1837 bool blockingMode,
michael@0 1838 nsICacheListener * listener,
michael@0 1839 nsCacheRequest ** request)
michael@0 1840 {
michael@0 1841 NS_ASSERTION(request, "CreateRequest: request is null");
michael@0 1842
michael@0 1843 nsAutoCString key(*session->ClientID());
michael@0 1844 key.Append(':');
michael@0 1845 key.Append(clientKey);
michael@0 1846
michael@0 1847 if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
michael@0 1848
michael@0 1849 // create request
michael@0 1850 *request = new nsCacheRequest(key, listener, accessRequested,
michael@0 1851 blockingMode, session);
michael@0 1852
michael@0 1853 if (!listener) return NS_OK; // we're sync, we're done.
michael@0 1854
michael@0 1855 // get the request's thread
michael@0 1856 (*request)->mThread = do_GetCurrentThread();
michael@0 1857
michael@0 1858 return NS_OK;
michael@0 1859 }
michael@0 1860
michael@0 1861
michael@0 1862 class nsCacheListenerEvent : public nsRunnable
michael@0 1863 {
michael@0 1864 public:
michael@0 1865 nsCacheListenerEvent(nsICacheListener *listener,
michael@0 1866 nsICacheEntryDescriptor *descriptor,
michael@0 1867 nsCacheAccessMode accessGranted,
michael@0 1868 nsresult status)
michael@0 1869 : mListener(listener) // transfers reference
michael@0 1870 , mDescriptor(descriptor) // transfers reference (may be null)
michael@0 1871 , mAccessGranted(accessGranted)
michael@0 1872 , mStatus(status)
michael@0 1873 {}
michael@0 1874
michael@0 1875 NS_IMETHOD Run()
michael@0 1876 {
michael@0 1877 mozilla::eventtracer::AutoEventTracer tracer(
michael@0 1878 static_cast<nsIRunnable*>(this),
michael@0 1879 eventtracer::eExec,
michael@0 1880 eventtracer::eDone,
michael@0 1881 "net::cache::OnCacheEntryAvailable");
michael@0 1882
michael@0 1883 mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
michael@0 1884
michael@0 1885 NS_RELEASE(mListener);
michael@0 1886 NS_IF_RELEASE(mDescriptor);
michael@0 1887 return NS_OK;
michael@0 1888 }
michael@0 1889
michael@0 1890 private:
michael@0 1891 // We explicitly leak mListener or mDescriptor if Run is not called
michael@0 1892 // because otherwise we cannot guarantee that they are destroyed on
michael@0 1893 // the right thread.
michael@0 1894
michael@0 1895 nsICacheListener *mListener;
michael@0 1896 nsICacheEntryDescriptor *mDescriptor;
michael@0 1897 nsCacheAccessMode mAccessGranted;
michael@0 1898 nsresult mStatus;
michael@0 1899 };
michael@0 1900
michael@0 1901
michael@0 1902 nsresult
michael@0 1903 nsCacheService::NotifyListener(nsCacheRequest * request,
michael@0 1904 nsICacheEntryDescriptor * descriptor,
michael@0 1905 nsCacheAccessMode accessGranted,
michael@0 1906 nsresult status)
michael@0 1907 {
michael@0 1908 NS_ASSERTION(request->mThread, "no thread set in async request!");
michael@0 1909
michael@0 1910 // Swap ownership, and release listener on target thread...
michael@0 1911 nsICacheListener *listener = request->mListener;
michael@0 1912 request->mListener = nullptr;
michael@0 1913
michael@0 1914 nsCOMPtr<nsIRunnable> ev =
michael@0 1915 new nsCacheListenerEvent(listener, descriptor,
michael@0 1916 accessGranted, status);
michael@0 1917 if (!ev) {
michael@0 1918 // Better to leak listener and descriptor if we fail because we don't
michael@0 1919 // want to destroy them inside the cache service lock or on potentially
michael@0 1920 // the wrong thread.
michael@0 1921 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1922 }
michael@0 1923
michael@0 1924 MOZ_EVENT_TRACER_NAME_OBJECT(ev.get(), request->mKey.get());
michael@0 1925 MOZ_EVENT_TRACER_WAIT(ev.get(), "net::cache::OnCacheEntryAvailable");
michael@0 1926 return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
michael@0 1927 }
michael@0 1928
michael@0 1929
michael@0 1930 nsresult
michael@0 1931 nsCacheService::ProcessRequest(nsCacheRequest * request,
michael@0 1932 bool calledFromOpenCacheEntry,
michael@0 1933 nsICacheEntryDescriptor ** result)
michael@0 1934 {
michael@0 1935 mozilla::eventtracer::AutoEventTracer tracer(
michael@0 1936 request,
michael@0 1937 eventtracer::eExec,
michael@0 1938 eventtracer::eDone,
michael@0 1939 "net::cache::ProcessRequest");
michael@0 1940
michael@0 1941 // !!! must be called with mLock held !!!
michael@0 1942 nsresult rv;
michael@0 1943 nsCacheEntry * entry = nullptr;
michael@0 1944 nsCacheEntry * doomedEntry = nullptr;
michael@0 1945 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
michael@0 1946 if (result) *result = nullptr;
michael@0 1947
michael@0 1948 while(1) { // Activate entry loop
michael@0 1949 rv = ActivateEntry(request, &entry, &doomedEntry); // get the entry for this request
michael@0 1950 if (NS_FAILED(rv)) break;
michael@0 1951
michael@0 1952 while(1) { // Request Access loop
michael@0 1953 NS_ASSERTION(entry, "no entry in Request Access loop!");
michael@0 1954 // entry->RequestAccess queues request on entry
michael@0 1955 rv = entry->RequestAccess(request, &accessGranted);
michael@0 1956 if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
michael@0 1957
michael@0 1958 if (request->IsBlocking()) {
michael@0 1959 if (request->mListener) {
michael@0 1960 // async exits - validate, doom, or close will resume
michael@0 1961 return rv;
michael@0 1962 }
michael@0 1963
michael@0 1964 // XXX this is probably wrong...
michael@0 1965 Unlock();
michael@0 1966 rv = request->WaitForValidation();
michael@0 1967 Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST));
michael@0 1968 }
michael@0 1969
michael@0 1970 PR_REMOVE_AND_INIT_LINK(request);
michael@0 1971 if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
michael@0 1972 // okay, we're ready to process this request, request access again
michael@0 1973 }
michael@0 1974 if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break;
michael@0 1975
michael@0 1976 if (entry->IsNotInUse()) {
michael@0 1977 // this request was the last one keeping it around, so get rid of it
michael@0 1978 DeactivateEntry(entry);
michael@0 1979 }
michael@0 1980 // loop back around to look for another entry
michael@0 1981 }
michael@0 1982
michael@0 1983 if (NS_SUCCEEDED(rv) && request->mProfileDir) {
michael@0 1984 // Custom cache directory has been demanded. Preset the cache device.
michael@0 1985 if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
michael@0 1986 // Failsafe check: this is implemented only for offline cache atm.
michael@0 1987 rv = NS_ERROR_FAILURE;
michael@0 1988 } else {
michael@0 1989 nsRefPtr<nsOfflineCacheDevice> customCacheDevice;
michael@0 1990 rv = GetCustomOfflineDevice(request->mProfileDir, -1,
michael@0 1991 getter_AddRefs(customCacheDevice));
michael@0 1992 if (NS_SUCCEEDED(rv))
michael@0 1993 entry->SetCustomCacheDevice(customCacheDevice);
michael@0 1994 }
michael@0 1995 }
michael@0 1996
michael@0 1997 nsICacheEntryDescriptor *descriptor = nullptr;
michael@0 1998
michael@0 1999 if (NS_SUCCEEDED(rv))
michael@0 2000 rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
michael@0 2001
michael@0 2002 // If doomedEntry is set, ActivatEntry() doomed an existing entry and
michael@0 2003 // created a new one for that cache-key. However, any pending requests
michael@0 2004 // on the doomed entry were not processed and we need to do that here.
michael@0 2005 // This must be done after adding the created entry to list of active
michael@0 2006 // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
michael@0 2007 // (see bug ##561313). It is also important to do this after creating a
michael@0 2008 // descriptor for this request, or some other request may end up being
michael@0 2009 // executed first for the newly created entry.
michael@0 2010 // Finally, it is worth to emphasize that if doomedEntry is set,
michael@0 2011 // ActivateEntry() created a new entry for the request, which will be
michael@0 2012 // initialized by RequestAccess() and they both should have returned NS_OK.
michael@0 2013 if (doomedEntry) {
michael@0 2014 (void) ProcessPendingRequests(doomedEntry);
michael@0 2015 if (doomedEntry->IsNotInUse())
michael@0 2016 DeactivateEntry(doomedEntry);
michael@0 2017 doomedEntry = nullptr;
michael@0 2018 }
michael@0 2019
michael@0 2020 if (request->mListener) { // Asynchronous
michael@0 2021
michael@0 2022 if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
michael@0 2023 return rv; // skip notifying listener, just return rv to caller
michael@0 2024
michael@0 2025 // call listener to report error or descriptor
michael@0 2026 nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
michael@0 2027 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
michael@0 2028 rv = rv2; // trigger delete request
michael@0 2029 }
michael@0 2030 } else { // Synchronous
michael@0 2031 *result = descriptor;
michael@0 2032 }
michael@0 2033 return rv;
michael@0 2034 }
michael@0 2035
michael@0 2036
michael@0 2037 nsresult
michael@0 2038 nsCacheService::OpenCacheEntry(nsCacheSession * session,
michael@0 2039 const nsACString & key,
michael@0 2040 nsCacheAccessMode accessRequested,
michael@0 2041 bool blockingMode,
michael@0 2042 nsICacheListener * listener,
michael@0 2043 nsICacheEntryDescriptor ** result)
michael@0 2044 {
michael@0 2045 CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
michael@0 2046 session, PromiseFlatCString(key).get(), accessRequested,
michael@0 2047 blockingMode));
michael@0 2048 NS_ASSERTION(gService, "nsCacheService::gService is null.");
michael@0 2049 if (result)
michael@0 2050 *result = nullptr;
michael@0 2051
michael@0 2052 if (!gService->mInitialized)
michael@0 2053 return NS_ERROR_NOT_INITIALIZED;
michael@0 2054
michael@0 2055 nsCacheRequest * request = nullptr;
michael@0 2056
michael@0 2057 nsresult rv = gService->CreateRequest(session,
michael@0 2058 key,
michael@0 2059 accessRequested,
michael@0 2060 blockingMode,
michael@0 2061 listener,
michael@0 2062 &request);
michael@0 2063 if (NS_FAILED(rv)) return rv;
michael@0 2064
michael@0 2065 CACHE_LOG_DEBUG(("Created request %p\n", request));
michael@0 2066
michael@0 2067 // Process the request on the background thread if we are on the main thread
michael@0 2068 // and the the request is asynchronous
michael@0 2069 if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
michael@0 2070 nsCOMPtr<nsIRunnable> ev =
michael@0 2071 new nsProcessRequestEvent(request);
michael@0 2072 rv = DispatchToCacheIOThread(ev);
michael@0 2073
michael@0 2074 // delete request if we didn't post the event
michael@0 2075 if (NS_FAILED(rv))
michael@0 2076 delete request;
michael@0 2077 }
michael@0 2078 else {
michael@0 2079
michael@0 2080 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY));
michael@0 2081 rv = gService->ProcessRequest(request, true, result);
michael@0 2082
michael@0 2083 // delete requests that have completed
michael@0 2084 if (!(listener && blockingMode &&
michael@0 2085 (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
michael@0 2086 delete request;
michael@0 2087 }
michael@0 2088
michael@0 2089 return rv;
michael@0 2090 }
michael@0 2091
michael@0 2092
michael@0 2093 nsresult
michael@0 2094 nsCacheService::ActivateEntry(nsCacheRequest * request,
michael@0 2095 nsCacheEntry ** result,
michael@0 2096 nsCacheEntry ** doomedEntry)
michael@0 2097 {
michael@0 2098 CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
michael@0 2099 if (!mInitialized || mClearingEntries)
michael@0 2100 return NS_ERROR_NOT_AVAILABLE;
michael@0 2101
michael@0 2102 mozilla::eventtracer::AutoEventTracer tracer(
michael@0 2103 request,
michael@0 2104 eventtracer::eExec,
michael@0 2105 eventtracer::eDone,
michael@0 2106 "net::cache::ActivateEntry");
michael@0 2107
michael@0 2108 nsresult rv = NS_OK;
michael@0 2109
michael@0 2110 NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
michael@0 2111 if (result) *result = nullptr;
michael@0 2112 if (doomedEntry) *doomedEntry = nullptr;
michael@0 2113 if ((!request) || (!result) || (!doomedEntry))
michael@0 2114 return NS_ERROR_NULL_POINTER;
michael@0 2115
michael@0 2116 // check if the request can be satisfied
michael@0 2117 if (!mEnableMemoryDevice && !request->IsStreamBased())
michael@0 2118 return NS_ERROR_FAILURE;
michael@0 2119 if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
michael@0 2120 return NS_ERROR_FAILURE;
michael@0 2121
michael@0 2122 // search active entries (including those not bound to device)
michael@0 2123 nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
michael@0 2124 CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
michael@0 2125
michael@0 2126 if (!entry) {
michael@0 2127 // search cache devices for entry
michael@0 2128 bool collision = false;
michael@0 2129 entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
michael@0 2130 CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
michael@0 2131 request, entry));
michael@0 2132 // When there is a hashkey collision just refuse to cache it...
michael@0 2133 if (collision) return NS_ERROR_CACHE_IN_USE;
michael@0 2134
michael@0 2135 if (entry) entry->MarkInitialized();
michael@0 2136 } else {
michael@0 2137 NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
michael@0 2138 }
michael@0 2139
michael@0 2140 if (entry) {
michael@0 2141 ++mCacheHits;
michael@0 2142 entry->Fetched();
michael@0 2143 } else {
michael@0 2144 ++mCacheMisses;
michael@0 2145 }
michael@0 2146
michael@0 2147 if (entry &&
michael@0 2148 ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
michael@0 2149 ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
michael@0 2150 (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
michael@0 2151 request->WillDoomEntriesIfExpired()))))
michael@0 2152
michael@0 2153 {
michael@0 2154 // this is FORCE-WRITE request or the entry has expired
michael@0 2155 // we doom entry without processing pending requests, but store it in
michael@0 2156 // doomedEntry which causes pending requests to be processed below
michael@0 2157 rv = DoomEntry_Internal(entry, false);
michael@0 2158 *doomedEntry = entry;
michael@0 2159 if (NS_FAILED(rv)) {
michael@0 2160 // XXX what to do? Increment FailedDooms counter?
michael@0 2161 }
michael@0 2162 entry = nullptr;
michael@0 2163 }
michael@0 2164
michael@0 2165 if (!entry) {
michael@0 2166 if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
michael@0 2167 // this is a READ-ONLY request
michael@0 2168 rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
michael@0 2169 goto error;
michael@0 2170 }
michael@0 2171
michael@0 2172 entry = new nsCacheEntry(request->mKey,
michael@0 2173 request->IsStreamBased(),
michael@0 2174 request->StoragePolicy());
michael@0 2175 if (!entry)
michael@0 2176 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2177
michael@0 2178 if (request->IsPrivate())
michael@0 2179 entry->MarkPrivate();
michael@0 2180
michael@0 2181 entry->Fetched();
michael@0 2182 ++mTotalEntries;
michael@0 2183
michael@0 2184 // XXX we could perform an early bind in some cases based on storage policy
michael@0 2185 }
michael@0 2186
michael@0 2187 if (!entry->IsActive()) {
michael@0 2188 rv = mActiveEntries.AddEntry(entry);
michael@0 2189 if (NS_FAILED(rv)) goto error;
michael@0 2190 CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
michael@0 2191 entry->MarkActive(); // mark entry active, because it's now in mActiveEntries
michael@0 2192 }
michael@0 2193 *result = entry;
michael@0 2194 return NS_OK;
michael@0 2195
michael@0 2196 error:
michael@0 2197 *result = nullptr;
michael@0 2198 delete entry;
michael@0 2199 return rv;
michael@0 2200 }
michael@0 2201
michael@0 2202
michael@0 2203 nsCacheEntry *
michael@0 2204 nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
michael@0 2205 {
michael@0 2206 Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2> timer;
michael@0 2207 nsCacheEntry * entry = nullptr;
michael@0 2208
michael@0 2209 MOZ_EVENT_TRACER_NAME_OBJECT(key, key->BeginReading());
michael@0 2210 eventtracer::AutoEventTracer searchCacheDevices(
michael@0 2211 key,
michael@0 2212 eventtracer::eExec,
michael@0 2213 eventtracer::eDone,
michael@0 2214 "net::cache::SearchCacheDevices");
michael@0 2215
michael@0 2216 CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
michael@0 2217
michael@0 2218 *collision = false;
michael@0 2219 if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
michael@0 2220 // If there is no memory device, then there is nothing to search...
michael@0 2221 if (mMemoryDevice) {
michael@0 2222 entry = mMemoryDevice->FindEntry(key, collision);
michael@0 2223 CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
michael@0 2224 "collision: %d\n", key->get(), entry, collision));
michael@0 2225 }
michael@0 2226 }
michael@0 2227
michael@0 2228 if (!entry &&
michael@0 2229 ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
michael@0 2230
michael@0 2231 if (mEnableDiskDevice) {
michael@0 2232 if (!mDiskDevice) {
michael@0 2233 nsresult rv = CreateDiskDevice();
michael@0 2234 if (NS_FAILED(rv))
michael@0 2235 return nullptr;
michael@0 2236 }
michael@0 2237
michael@0 2238 entry = mDiskDevice->FindEntry(key, collision);
michael@0 2239 }
michael@0 2240 }
michael@0 2241
michael@0 2242 if (!entry && (policy == nsICache::STORE_OFFLINE ||
michael@0 2243 (policy == nsICache::STORE_ANYWHERE &&
michael@0 2244 gIOService->IsOffline()))) {
michael@0 2245
michael@0 2246 if (mEnableOfflineDevice) {
michael@0 2247 if (!mOfflineDevice) {
michael@0 2248 nsresult rv = CreateOfflineDevice();
michael@0 2249 if (NS_FAILED(rv))
michael@0 2250 return nullptr;
michael@0 2251 }
michael@0 2252
michael@0 2253 entry = mOfflineDevice->FindEntry(key, collision);
michael@0 2254 }
michael@0 2255 }
michael@0 2256
michael@0 2257 return entry;
michael@0 2258 }
michael@0 2259
michael@0 2260
michael@0 2261 nsCacheDevice *
michael@0 2262 nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
michael@0 2263 {
michael@0 2264 nsCacheDevice * device = entry->CacheDevice();
michael@0 2265 // return device if found, possibly null if the entry is doomed i.e prevent
michael@0 2266 // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
michael@0 2267 if (device || entry->IsDoomed()) return device;
michael@0 2268
michael@0 2269 int64_t predictedDataSize = entry->PredictedDataSize();
michael@0 2270 if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
michael@0 2271 // this is the default
michael@0 2272 if (!mDiskDevice) {
michael@0 2273 (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
michael@0 2274 }
michael@0 2275
michael@0 2276 if (mDiskDevice) {
michael@0 2277 // Bypass the cache if Content-Length says the entry will be too big
michael@0 2278 if (predictedDataSize != -1 &&
michael@0 2279 mDiskDevice->EntryIsTooBig(predictedDataSize)) {
michael@0 2280 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
michael@0 2281 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
michael@0 2282 return nullptr;
michael@0 2283 }
michael@0 2284
michael@0 2285 entry->MarkBinding(); // enter state of binding
michael@0 2286 nsresult rv = mDiskDevice->BindEntry(entry);
michael@0 2287 entry->ClearBinding(); // exit state of binding
michael@0 2288 if (NS_SUCCEEDED(rv))
michael@0 2289 device = mDiskDevice;
michael@0 2290 }
michael@0 2291 }
michael@0 2292
michael@0 2293 // if we can't use mDiskDevice, try mMemoryDevice
michael@0 2294 if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {
michael@0 2295 if (!mMemoryDevice) {
michael@0 2296 (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
michael@0 2297 }
michael@0 2298 if (mMemoryDevice) {
michael@0 2299 // Bypass the cache if Content-Length says entry will be too big
michael@0 2300 if (predictedDataSize != -1 &&
michael@0 2301 mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
michael@0 2302 DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
michael@0 2303 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
michael@0 2304 return nullptr;
michael@0 2305 }
michael@0 2306
michael@0 2307 entry->MarkBinding(); // enter state of binding
michael@0 2308 nsresult rv = mMemoryDevice->BindEntry(entry);
michael@0 2309 entry->ClearBinding(); // exit state of binding
michael@0 2310 if (NS_SUCCEEDED(rv))
michael@0 2311 device = mMemoryDevice;
michael@0 2312 }
michael@0 2313 }
michael@0 2314
michael@0 2315 if (!device && entry->IsStreamData() &&
michael@0 2316 entry->IsAllowedOffline() && mEnableOfflineDevice) {
michael@0 2317 if (!mOfflineDevice) {
michael@0 2318 (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
michael@0 2319 }
michael@0 2320
michael@0 2321 device = entry->CustomCacheDevice()
michael@0 2322 ? entry->CustomCacheDevice()
michael@0 2323 : mOfflineDevice;
michael@0 2324
michael@0 2325 if (device) {
michael@0 2326 entry->MarkBinding();
michael@0 2327 nsresult rv = device->BindEntry(entry);
michael@0 2328 entry->ClearBinding();
michael@0 2329 if (NS_FAILED(rv))
michael@0 2330 device = nullptr;
michael@0 2331 }
michael@0 2332 }
michael@0 2333
michael@0 2334 if (device)
michael@0 2335 entry->SetCacheDevice(device);
michael@0 2336 return device;
michael@0 2337 }
michael@0 2338
michael@0 2339 nsresult
michael@0 2340 nsCacheService::DoomEntry(nsCacheEntry * entry)
michael@0 2341 {
michael@0 2342 return gService->DoomEntry_Internal(entry, true);
michael@0 2343 }
michael@0 2344
michael@0 2345
michael@0 2346 nsresult
michael@0 2347 nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
michael@0 2348 bool doProcessPendingRequests)
michael@0 2349 {
michael@0 2350 if (entry->IsDoomed()) return NS_OK;
michael@0 2351
michael@0 2352 CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
michael@0 2353 nsresult rv = NS_OK;
michael@0 2354 entry->MarkDoomed();
michael@0 2355
michael@0 2356 NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
michael@0 2357 nsCacheDevice * device = entry->CacheDevice();
michael@0 2358 if (device) device->DoomEntry(entry);
michael@0 2359
michael@0 2360 if (entry->IsActive()) {
michael@0 2361 // remove from active entries
michael@0 2362 mActiveEntries.RemoveEntry(entry);
michael@0 2363 CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
michael@0 2364 entry->MarkInactive();
michael@0 2365 }
michael@0 2366
michael@0 2367 // put on doom list to wait for descriptors to close
michael@0 2368 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
michael@0 2369 PR_APPEND_LINK(entry, &mDoomedEntries);
michael@0 2370
michael@0 2371 // handle pending requests only if we're supposed to
michael@0 2372 if (doProcessPendingRequests) {
michael@0 2373 // tell pending requests to get on with their lives...
michael@0 2374 rv = ProcessPendingRequests(entry);
michael@0 2375
michael@0 2376 // All requests have been removed, but there may still be open descriptors
michael@0 2377 if (entry->IsNotInUse()) {
michael@0 2378 DeactivateEntry(entry); // tell device to get rid of it
michael@0 2379 }
michael@0 2380 }
michael@0 2381 return rv;
michael@0 2382 }
michael@0 2383
michael@0 2384
michael@0 2385 void
michael@0 2386 nsCacheService::OnProfileShutdown(bool cleanse)
michael@0 2387 {
michael@0 2388 if (!gService) return;
michael@0 2389 if (!gService->mInitialized) {
michael@0 2390 // The cache service has been shut down, but someone is still holding
michael@0 2391 // a reference to it. Ignore this call.
michael@0 2392 return;
michael@0 2393 }
michael@0 2394 {
michael@0 2395 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
michael@0 2396 gService->mClearingEntries = true;
michael@0 2397 gService->DoomActiveEntries(nullptr);
michael@0 2398 }
michael@0 2399
michael@0 2400 gService->CloseAllStreams();
michael@0 2401
michael@0 2402 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));
michael@0 2403 gService->ClearDoomList();
michael@0 2404
michael@0 2405 // Make sure to wait for any pending cache-operations before
michael@0 2406 // proceeding with destructive actions (bug #620660)
michael@0 2407 (void) SyncWithCacheIOThread();
michael@0 2408
michael@0 2409 if (gService->mDiskDevice && gService->mEnableDiskDevice) {
michael@0 2410 if (cleanse)
michael@0 2411 gService->mDiskDevice->EvictEntries(nullptr);
michael@0 2412
michael@0 2413 gService->mDiskDevice->Shutdown();
michael@0 2414 }
michael@0 2415 gService->mEnableDiskDevice = false;
michael@0 2416
michael@0 2417 if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
michael@0 2418 if (cleanse)
michael@0 2419 gService->mOfflineDevice->EvictEntries(nullptr);
michael@0 2420
michael@0 2421 gService->mOfflineDevice->Shutdown();
michael@0 2422 }
michael@0 2423 gService->mCustomOfflineDevices.Enumerate(
michael@0 2424 &nsCacheService::ShutdownCustomCacheDeviceEnum, nullptr);
michael@0 2425
michael@0 2426 gService->mEnableOfflineDevice = false;
michael@0 2427
michael@0 2428 if (gService->mMemoryDevice) {
michael@0 2429 // clear memory cache
michael@0 2430 gService->mMemoryDevice->EvictEntries(nullptr);
michael@0 2431 }
michael@0 2432
michael@0 2433 gService->mClearingEntries = false;
michael@0 2434 }
michael@0 2435
michael@0 2436
michael@0 2437 void
michael@0 2438 nsCacheService::OnProfileChanged()
michael@0 2439 {
michael@0 2440 if (!gService) return;
michael@0 2441
michael@0 2442 CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
michael@0 2443
michael@0 2444 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED));
michael@0 2445
michael@0 2446 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
michael@0 2447 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
michael@0 2448 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
michael@0 2449
michael@0 2450 if (gService->mDiskDevice) {
michael@0 2451 gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
michael@0 2452 gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
michael@0 2453
michael@0 2454 // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
michael@0 2455 nsresult rv = gService->mDiskDevice->Init();
michael@0 2456 if (NS_FAILED(rv)) {
michael@0 2457 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
michael@0 2458 gService->mEnableDiskDevice = false;
michael@0 2459 // XXX delete mDiskDevice?
michael@0 2460 }
michael@0 2461 }
michael@0 2462
michael@0 2463 if (gService->mOfflineDevice) {
michael@0 2464 gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
michael@0 2465 gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
michael@0 2466
michael@0 2467 // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
michael@0 2468 nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
michael@0 2469 if (NS_FAILED(rv)) {
michael@0 2470 NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
michael@0 2471 gService->mEnableOfflineDevice = false;
michael@0 2472 // XXX delete mOfflineDevice?
michael@0 2473 }
michael@0 2474 }
michael@0 2475
michael@0 2476 // If memoryDevice exists, reset its size to the new profile
michael@0 2477 if (gService->mMemoryDevice) {
michael@0 2478 if (gService->mEnableMemoryDevice) {
michael@0 2479 // make sure that capacity is reset to the right value
michael@0 2480 int32_t capacity = gService->mObserver->MemoryCacheCapacity();
michael@0 2481 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
michael@0 2482 capacity));
michael@0 2483 gService->mMemoryDevice->SetCapacity(capacity);
michael@0 2484 } else {
michael@0 2485 // tell memory device to evict everything
michael@0 2486 CACHE_LOG_DEBUG(("memory device disabled\n"));
michael@0 2487 gService->mMemoryDevice->SetCapacity(0);
michael@0 2488 // Don't delete memory device, because some entries may be active still...
michael@0 2489 }
michael@0 2490 }
michael@0 2491 }
michael@0 2492
michael@0 2493
michael@0 2494 void
michael@0 2495 nsCacheService::SetDiskCacheEnabled(bool enabled)
michael@0 2496 {
michael@0 2497 if (!gService) return;
michael@0 2498 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED));
michael@0 2499 gService->mEnableDiskDevice = enabled;
michael@0 2500 }
michael@0 2501
michael@0 2502
michael@0 2503 void
michael@0 2504 nsCacheService::SetDiskCacheCapacity(int32_t capacity)
michael@0 2505 {
michael@0 2506 if (!gService) return;
michael@0 2507 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY));
michael@0 2508
michael@0 2509 if (gService->mDiskDevice) {
michael@0 2510 gService->mDiskDevice->SetCapacity(capacity);
michael@0 2511 }
michael@0 2512
michael@0 2513 gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
michael@0 2514 }
michael@0 2515
michael@0 2516 void
michael@0 2517 nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize)
michael@0 2518 {
michael@0 2519 if (!gService) return;
michael@0 2520 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE));
michael@0 2521
michael@0 2522 if (gService->mDiskDevice) {
michael@0 2523 gService->mDiskDevice->SetMaxEntrySize(maxSize);
michael@0 2524 }
michael@0 2525 }
michael@0 2526
michael@0 2527 void
michael@0 2528 nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize)
michael@0 2529 {
michael@0 2530 if (!gService) return;
michael@0 2531 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE));
michael@0 2532
michael@0 2533 if (gService->mMemoryDevice) {
michael@0 2534 gService->mMemoryDevice->SetMaxEntrySize(maxSize);
michael@0 2535 }
michael@0 2536 }
michael@0 2537
michael@0 2538 void
michael@0 2539 nsCacheService::SetOfflineCacheEnabled(bool enabled)
michael@0 2540 {
michael@0 2541 if (!gService) return;
michael@0 2542 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED));
michael@0 2543 gService->mEnableOfflineDevice = enabled;
michael@0 2544 }
michael@0 2545
michael@0 2546 void
michael@0 2547 nsCacheService::SetOfflineCacheCapacity(int32_t capacity)
michael@0 2548 {
michael@0 2549 if (!gService) return;
michael@0 2550 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY));
michael@0 2551
michael@0 2552 if (gService->mOfflineDevice) {
michael@0 2553 gService->mOfflineDevice->SetCapacity(capacity);
michael@0 2554 }
michael@0 2555
michael@0 2556 gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
michael@0 2557 }
michael@0 2558
michael@0 2559
michael@0 2560 void
michael@0 2561 nsCacheService::SetMemoryCache()
michael@0 2562 {
michael@0 2563 if (!gService) return;
michael@0 2564
michael@0 2565 CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
michael@0 2566
michael@0 2567 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE));
michael@0 2568
michael@0 2569 gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
michael@0 2570
michael@0 2571 if (gService->mEnableMemoryDevice) {
michael@0 2572 if (gService->mMemoryDevice) {
michael@0 2573 int32_t capacity = gService->mObserver->MemoryCacheCapacity();
michael@0 2574 // make sure that capacity is reset to the right value
michael@0 2575 CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
michael@0 2576 capacity));
michael@0 2577 gService->mMemoryDevice->SetCapacity(capacity);
michael@0 2578 }
michael@0 2579 } else {
michael@0 2580 if (gService->mMemoryDevice) {
michael@0 2581 // tell memory device to evict everything
michael@0 2582 CACHE_LOG_DEBUG(("memory device disabled\n"));
michael@0 2583 gService->mMemoryDevice->SetCapacity(0);
michael@0 2584 // Don't delete memory device, because some entries may be active still...
michael@0 2585 }
michael@0 2586 }
michael@0 2587 }
michael@0 2588
michael@0 2589
michael@0 2590 /******************************************************************************
michael@0 2591 * static methods for nsCacheEntryDescriptor
michael@0 2592 *****************************************************************************/
michael@0 2593 void
michael@0 2594 nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
michael@0 2595 {
michael@0 2596 // ask entry to remove descriptor
michael@0 2597 nsCacheEntry * entry = descriptor->CacheEntry();
michael@0 2598 bool doomEntry;
michael@0 2599 bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
michael@0 2600
michael@0 2601 if (!entry->IsValid()) {
michael@0 2602 gService->ProcessPendingRequests(entry);
michael@0 2603 }
michael@0 2604
michael@0 2605 if (doomEntry) {
michael@0 2606 gService->DoomEntry_Internal(entry, true);
michael@0 2607 return;
michael@0 2608 }
michael@0 2609
michael@0 2610 if (!stillActive) {
michael@0 2611 gService->DeactivateEntry(entry);
michael@0 2612 }
michael@0 2613 }
michael@0 2614
michael@0 2615
michael@0 2616 nsresult
michael@0 2617 nsCacheService::GetFileForEntry(nsCacheEntry * entry,
michael@0 2618 nsIFile ** result)
michael@0 2619 {
michael@0 2620 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
michael@0 2621 if (!device) return NS_ERROR_UNEXPECTED;
michael@0 2622
michael@0 2623 return device->GetFileForEntry(entry, result);
michael@0 2624 }
michael@0 2625
michael@0 2626
michael@0 2627 nsresult
michael@0 2628 nsCacheService::OpenInputStreamForEntry(nsCacheEntry * entry,
michael@0 2629 nsCacheAccessMode mode,
michael@0 2630 uint32_t offset,
michael@0 2631 nsIInputStream ** result)
michael@0 2632 {
michael@0 2633 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
michael@0 2634 if (!device) return NS_ERROR_UNEXPECTED;
michael@0 2635
michael@0 2636 return device->OpenInputStreamForEntry(entry, mode, offset, result);
michael@0 2637 }
michael@0 2638
michael@0 2639 nsresult
michael@0 2640 nsCacheService::OpenOutputStreamForEntry(nsCacheEntry * entry,
michael@0 2641 nsCacheAccessMode mode,
michael@0 2642 uint32_t offset,
michael@0 2643 nsIOutputStream ** result)
michael@0 2644 {
michael@0 2645 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
michael@0 2646 if (!device) return NS_ERROR_UNEXPECTED;
michael@0 2647
michael@0 2648 return device->OpenOutputStreamForEntry(entry, mode, offset, result);
michael@0 2649 }
michael@0 2650
michael@0 2651
michael@0 2652 nsresult
michael@0 2653 nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
michael@0 2654 {
michael@0 2655 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
michael@0 2656 if (!device) return NS_ERROR_UNEXPECTED;
michael@0 2657
michael@0 2658 return device->OnDataSizeChange(entry, deltaSize);
michael@0 2659 }
michael@0 2660
michael@0 2661 void
michael@0 2662 nsCacheService::LockAcquired()
michael@0 2663 {
michael@0 2664 MutexAutoLock lock(mTimeStampLock);
michael@0 2665 mLockAcquiredTimeStamp = TimeStamp::Now();
michael@0 2666 }
michael@0 2667
michael@0 2668 void
michael@0 2669 nsCacheService::LockReleased()
michael@0 2670 {
michael@0 2671 MutexAutoLock lock(mTimeStampLock);
michael@0 2672 mLockAcquiredTimeStamp = TimeStamp();
michael@0 2673 }
michael@0 2674
michael@0 2675 void
michael@0 2676 nsCacheService::Lock(mozilla::Telemetry::ID mainThreadLockerID)
michael@0 2677 {
michael@0 2678 mozilla::Telemetry::ID lockerID;
michael@0 2679 mozilla::Telemetry::ID generalID;
michael@0 2680
michael@0 2681 if (NS_IsMainThread()) {
michael@0 2682 lockerID = mainThreadLockerID;
michael@0 2683 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2;
michael@0 2684 } else {
michael@0 2685 lockerID = mozilla::Telemetry::HistogramCount;
michael@0 2686 generalID = mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2;
michael@0 2687 }
michael@0 2688
michael@0 2689 TimeStamp start(TimeStamp::Now());
michael@0 2690 MOZ_EVENT_TRACER_WAIT(nsCacheService::gService, "net::cache::lock");
michael@0 2691
michael@0 2692 gService->mLock.Lock();
michael@0 2693 gService->LockAcquired();
michael@0 2694
michael@0 2695 TimeStamp stop(TimeStamp::Now());
michael@0 2696 MOZ_EVENT_TRACER_EXEC(nsCacheService::gService, "net::cache::lock");
michael@0 2697
michael@0 2698 // Telemetry isn't thread safe on its own, but this is OK because we're
michael@0 2699 // protecting it with the cache lock.
michael@0 2700 if (lockerID != mozilla::Telemetry::HistogramCount) {
michael@0 2701 mozilla::Telemetry::AccumulateTimeDelta(lockerID, start, stop);
michael@0 2702 }
michael@0 2703 mozilla::Telemetry::AccumulateTimeDelta(generalID, start, stop);
michael@0 2704 }
michael@0 2705
michael@0 2706 void
michael@0 2707 nsCacheService::Unlock()
michael@0 2708 {
michael@0 2709 gService->mLock.AssertCurrentThreadOwns();
michael@0 2710
michael@0 2711 nsTArray<nsISupports*> doomed;
michael@0 2712 doomed.SwapElements(gService->mDoomedObjects);
michael@0 2713
michael@0 2714 gService->LockReleased();
michael@0 2715 gService->mLock.Unlock();
michael@0 2716
michael@0 2717 MOZ_EVENT_TRACER_DONE(nsCacheService::gService, "net::cache::lock");
michael@0 2718
michael@0 2719 for (uint32_t i = 0; i < doomed.Length(); ++i)
michael@0 2720 doomed[i]->Release();
michael@0 2721 }
michael@0 2722
michael@0 2723 void
michael@0 2724 nsCacheService::ReleaseObject_Locked(nsISupports * obj,
michael@0 2725 nsIEventTarget * target)
michael@0 2726 {
michael@0 2727 gService->mLock.AssertCurrentThreadOwns();
michael@0 2728
michael@0 2729 bool isCur;
michael@0 2730 if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
michael@0 2731 gService->mDoomedObjects.AppendElement(obj);
michael@0 2732 } else {
michael@0 2733 NS_ProxyRelease(target, obj);
michael@0 2734 }
michael@0 2735 }
michael@0 2736
michael@0 2737
michael@0 2738 nsresult
michael@0 2739 nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
michael@0 2740 {
michael@0 2741 entry->SetData(element);
michael@0 2742 entry->TouchData();
michael@0 2743 return NS_OK;
michael@0 2744 }
michael@0 2745
michael@0 2746
michael@0 2747 nsresult
michael@0 2748 nsCacheService::ValidateEntry(nsCacheEntry * entry)
michael@0 2749 {
michael@0 2750 nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
michael@0 2751 if (!device) return NS_ERROR_UNEXPECTED;
michael@0 2752
michael@0 2753 entry->MarkValid();
michael@0 2754 nsresult rv = gService->ProcessPendingRequests(entry);
michael@0 2755 NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
michael@0 2756 // XXX what else should be done?
michael@0 2757
michael@0 2758 return rv;
michael@0 2759 }
michael@0 2760
michael@0 2761
michael@0 2762 int32_t
michael@0 2763 nsCacheService::CacheCompressionLevel()
michael@0 2764 {
michael@0 2765 int32_t level = gService->mObserver->CacheCompressionLevel();
michael@0 2766 return level;
michael@0 2767 }
michael@0 2768
michael@0 2769
michael@0 2770 void
michael@0 2771 nsCacheService::DeactivateEntry(nsCacheEntry * entry)
michael@0 2772 {
michael@0 2773 CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
michael@0 2774 nsresult rv = NS_OK;
michael@0 2775 NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
michael@0 2776 nsCacheDevice * device = nullptr;
michael@0 2777
michael@0 2778 if (mMaxDataSize < entry->DataSize() ) mMaxDataSize = entry->DataSize();
michael@0 2779 if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
michael@0 2780
michael@0 2781 if (entry->IsDoomed()) {
michael@0 2782 // remove from Doomed list
michael@0 2783 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 2784 } else if (entry->IsActive()) {
michael@0 2785 // remove from active entries
michael@0 2786 mActiveEntries.RemoveEntry(entry);
michael@0 2787 CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
michael@0 2788 entry));
michael@0 2789 entry->MarkInactive();
michael@0 2790
michael@0 2791 // bind entry if necessary to store meta-data
michael@0 2792 device = EnsureEntryHasDevice(entry);
michael@0 2793 if (!device) {
michael@0 2794 CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
michael@0 2795 "entry %p\n",
michael@0 2796 entry));
michael@0 2797 NS_WARNING("DeactivateEntry: unable to bind active entry\n");
michael@0 2798 return;
michael@0 2799 }
michael@0 2800 } else {
michael@0 2801 // if mInitialized == false,
michael@0 2802 // then we're shutting down and this state is okay.
michael@0 2803 NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
michael@0 2804 }
michael@0 2805
michael@0 2806 device = entry->CacheDevice();
michael@0 2807 if (device) {
michael@0 2808 rv = device->DeactivateEntry(entry);
michael@0 2809 if (NS_FAILED(rv)) {
michael@0 2810 // increment deactivate failure count
michael@0 2811 ++mDeactivateFailures;
michael@0 2812 }
michael@0 2813 } else {
michael@0 2814 // increment deactivating unbound entry statistic
michael@0 2815 ++mDeactivatedUnboundEntries;
michael@0 2816 delete entry; // because no one else will
michael@0 2817 }
michael@0 2818 }
michael@0 2819
michael@0 2820
michael@0 2821 nsresult
michael@0 2822 nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
michael@0 2823 {
michael@0 2824 mozilla::eventtracer::AutoEventTracer tracer(
michael@0 2825 entry,
michael@0 2826 eventtracer::eExec,
michael@0 2827 eventtracer::eDone,
michael@0 2828 "net::cache::ProcessPendingRequests");
michael@0 2829
michael@0 2830 nsresult rv = NS_OK;
michael@0 2831 nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
michael@0 2832 nsCacheRequest * nextRequest;
michael@0 2833 bool newWriter = false;
michael@0 2834
michael@0 2835 CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
michael@0 2836 (entry->IsInitialized()?"" : "Un"),
michael@0 2837 (entry->IsDoomed()?"DOOMED" : ""),
michael@0 2838 (entry->IsValid()? "V":"Inv"), entry));
michael@0 2839
michael@0 2840 if (request == &entry->mRequestQ) return NS_OK; // no queued requests
michael@0 2841
michael@0 2842 if (!entry->IsDoomed() && entry->IsInvalid()) {
michael@0 2843 // 1st descriptor closed w/o MarkValid()
michael@0 2844 NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
michael@0 2845
michael@0 2846 #if DEBUG
michael@0 2847 // verify no ACCESS_WRITE requests(shouldn't have any of these)
michael@0 2848 while (request != &entry->mRequestQ) {
michael@0 2849 NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
michael@0 2850 "ACCESS_WRITE request should have been given a new entry");
michael@0 2851 request = (nsCacheRequest *)PR_NEXT_LINK(request);
michael@0 2852 }
michael@0 2853 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
michael@0 2854 #endif
michael@0 2855 // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
michael@0 2856 while (request != &entry->mRequestQ) {
michael@0 2857 if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
michael@0 2858 newWriter = true;
michael@0 2859 CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request));
michael@0 2860 break;
michael@0 2861 }
michael@0 2862
michael@0 2863 request = (nsCacheRequest *)PR_NEXT_LINK(request);
michael@0 2864 }
michael@0 2865
michael@0 2866 if (request == &entry->mRequestQ) // no requests asked for ACCESS_READ_WRITE, back to top
michael@0 2867 request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
michael@0 2868
michael@0 2869 // XXX what should we do if there are only READ requests in queue?
michael@0 2870 // XXX serialize their accesses, give them only read access, but force them to check validate flag?
michael@0 2871 // XXX or do readers simply presume the entry is valid
michael@0 2872 // See fix for bug #467392 below
michael@0 2873 }
michael@0 2874
michael@0 2875 nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
michael@0 2876
michael@0 2877 while (request != &entry->mRequestQ) {
michael@0 2878 nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
michael@0 2879 CACHE_LOG_DEBUG((" %sync request %p for %p\n",
michael@0 2880 (request->mListener?"As":"S"), request, entry));
michael@0 2881
michael@0 2882 if (request->mListener) {
michael@0 2883
michael@0 2884 // Async request
michael@0 2885 PR_REMOVE_AND_INIT_LINK(request);
michael@0 2886
michael@0 2887 if (entry->IsDoomed()) {
michael@0 2888 rv = ProcessRequest(request, false, nullptr);
michael@0 2889 if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
michael@0 2890 rv = NS_OK;
michael@0 2891 else
michael@0 2892 delete request;
michael@0 2893
michael@0 2894 if (NS_FAILED(rv)) {
michael@0 2895 // XXX what to do?
michael@0 2896 }
michael@0 2897 } else if (entry->IsValid() || newWriter) {
michael@0 2898 rv = entry->RequestAccess(request, &accessGranted);
michael@0 2899 NS_ASSERTION(NS_SUCCEEDED(rv),
michael@0 2900 "if entry is valid, RequestAccess must succeed.");
michael@0 2901 // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
michael@0 2902
michael@0 2903 // entry->CreateDescriptor dequeues request, and queues descriptor
michael@0 2904 nsICacheEntryDescriptor *descriptor = nullptr;
michael@0 2905 rv = entry->CreateDescriptor(request,
michael@0 2906 accessGranted,
michael@0 2907 &descriptor);
michael@0 2908
michael@0 2909 // post call to listener to report error or descriptor
michael@0 2910 rv = NotifyListener(request, descriptor, accessGranted, rv);
michael@0 2911 delete request;
michael@0 2912 if (NS_FAILED(rv)) {
michael@0 2913 // XXX what to do?
michael@0 2914 }
michael@0 2915
michael@0 2916 } else {
michael@0 2917 // read-only request to an invalid entry - need to wait for
michael@0 2918 // the entry to become valid so we post an event to process
michael@0 2919 // the request again later (bug #467392)
michael@0 2920 nsCOMPtr<nsIRunnable> ev =
michael@0 2921 new nsProcessRequestEvent(request);
michael@0 2922 rv = DispatchToCacheIOThread(ev);
michael@0 2923 if (NS_FAILED(rv)) {
michael@0 2924 delete request; // avoid leak
michael@0 2925 }
michael@0 2926 }
michael@0 2927 } else {
michael@0 2928
michael@0 2929 // Synchronous request
michael@0 2930 request->WakeUp();
michael@0 2931 }
michael@0 2932 if (newWriter) break; // process remaining requests after validation
michael@0 2933 request = nextRequest;
michael@0 2934 }
michael@0 2935
michael@0 2936 return NS_OK;
michael@0 2937 }
michael@0 2938
michael@0 2939 bool
michael@0 2940 nsCacheService::IsDoomListEmpty()
michael@0 2941 {
michael@0 2942 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
michael@0 2943 return &mDoomedEntries == entry;
michael@0 2944 }
michael@0 2945
michael@0 2946 void
michael@0 2947 nsCacheService::ClearDoomList()
michael@0 2948 {
michael@0 2949 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
michael@0 2950
michael@0 2951 while (entry != &mDoomedEntries) {
michael@0 2952 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
michael@0 2953
michael@0 2954 entry->DetachDescriptors();
michael@0 2955 DeactivateEntry(entry);
michael@0 2956 entry = next;
michael@0 2957 }
michael@0 2958 }
michael@0 2959
michael@0 2960 PLDHashOperator
michael@0 2961 nsCacheService::GetActiveEntries(PLDHashTable * table,
michael@0 2962 PLDHashEntryHdr * hdr,
michael@0 2963 uint32_t number,
michael@0 2964 void * arg)
michael@0 2965 {
michael@0 2966 static_cast<nsVoidArray *>(arg)->AppendElement(
michael@0 2967 ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry);
michael@0 2968 return PL_DHASH_NEXT;
michael@0 2969 }
michael@0 2970
michael@0 2971 struct ActiveEntryArgs
michael@0 2972 {
michael@0 2973 nsTArray<nsCacheEntry*>* mActiveArray;
michael@0 2974 nsCacheService::DoomCheckFn mCheckFn;
michael@0 2975 };
michael@0 2976
michael@0 2977 void
michael@0 2978 nsCacheService::DoomActiveEntries(DoomCheckFn check)
michael@0 2979 {
michael@0 2980 nsAutoTArray<nsCacheEntry*, 8> array;
michael@0 2981 ActiveEntryArgs args = { &array, check };
michael@0 2982
michael@0 2983 mActiveEntries.VisitEntries(RemoveActiveEntry, &args);
michael@0 2984
michael@0 2985 uint32_t count = array.Length();
michael@0 2986 for (uint32_t i=0; i < count; ++i)
michael@0 2987 DoomEntry_Internal(array[i], true);
michael@0 2988 }
michael@0 2989
michael@0 2990 PLDHashOperator
michael@0 2991 nsCacheService::RemoveActiveEntry(PLDHashTable * table,
michael@0 2992 PLDHashEntryHdr * hdr,
michael@0 2993 uint32_t number,
michael@0 2994 void * arg)
michael@0 2995 {
michael@0 2996 nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry;
michael@0 2997 NS_ASSERTION(entry, "### active entry = nullptr!");
michael@0 2998
michael@0 2999 ActiveEntryArgs* args = static_cast<ActiveEntryArgs*>(arg);
michael@0 3000 if (args->mCheckFn && !args->mCheckFn(entry))
michael@0 3001 return PL_DHASH_NEXT;
michael@0 3002
michael@0 3003 NS_ASSERTION(args->mActiveArray, "### array = nullptr!");
michael@0 3004 args->mActiveArray->AppendElement(entry);
michael@0 3005
michael@0 3006 // entry is being removed from the active entry list
michael@0 3007 entry->MarkInactive();
michael@0 3008 return PL_DHASH_REMOVE; // and continue enumerating
michael@0 3009 }
michael@0 3010
michael@0 3011
michael@0 3012 void
michael@0 3013 nsCacheService::CloseAllStreams()
michael@0 3014 {
michael@0 3015 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
michael@0 3016 nsTArray<nsRefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
michael@0 3017
michael@0 3018 {
michael@0 3019 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS));
michael@0 3020
michael@0 3021 nsVoidArray entries;
michael@0 3022
michael@0 3023 #if DEBUG
michael@0 3024 // make sure there is no active entry
michael@0 3025 mActiveEntries.VisitEntries(GetActiveEntries, &entries);
michael@0 3026 NS_ASSERTION(entries.Count() == 0, "Bad state");
michael@0 3027 #endif
michael@0 3028
michael@0 3029 // Get doomed entries
michael@0 3030 nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
michael@0 3031 while (entry != &mDoomedEntries) {
michael@0 3032 nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
michael@0 3033 entries.AppendElement(entry);
michael@0 3034 entry = next;
michael@0 3035 }
michael@0 3036
michael@0 3037 // Iterate through all entries and collect input and output streams
michael@0 3038 for (int32_t i = 0 ; i < entries.Count() ; i++) {
michael@0 3039 entry = static_cast<nsCacheEntry *>(entries.ElementAt(i));
michael@0 3040
michael@0 3041 nsTArray<nsRefPtr<nsCacheEntryDescriptor> > descs;
michael@0 3042 entry->GetDescriptors(descs);
michael@0 3043
michael@0 3044 for (uint32_t j = 0 ; j < descs.Length() ; j++) {
michael@0 3045 if (descs[j]->mOutputWrapper)
michael@0 3046 outputs.AppendElement(descs[j]->mOutputWrapper);
michael@0 3047
michael@0 3048 for (int32_t k = 0 ; k < descs[j]->mInputWrappers.Count() ; k++)
michael@0 3049 inputs.AppendElement(static_cast<
michael@0 3050 nsCacheEntryDescriptor::nsInputStreamWrapper *>(
michael@0 3051 descs[j]->mInputWrappers[k]));
michael@0 3052 }
michael@0 3053 }
michael@0 3054 }
michael@0 3055
michael@0 3056 uint32_t i;
michael@0 3057 for (i = 0 ; i < inputs.Length() ; i++)
michael@0 3058 inputs[i]->Close();
michael@0 3059
michael@0 3060 for (i = 0 ; i < outputs.Length() ; i++)
michael@0 3061 outputs[i]->Close();
michael@0 3062 }
michael@0 3063
michael@0 3064
michael@0 3065 bool
michael@0 3066 nsCacheService::GetClearingEntries()
michael@0 3067 {
michael@0 3068 AssertOwnsLock();
michael@0 3069 return gService->mClearingEntries;
michael@0 3070 }
michael@0 3071
michael@0 3072 // static
michael@0 3073 void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
michael@0 3074 {
michael@0 3075 *result = nullptr;
michael@0 3076 if (!gService || !gService->mObserver)
michael@0 3077 return;
michael@0 3078
michael@0 3079 nsCOMPtr<nsIFile> directory =
michael@0 3080 gService->mObserver->DiskCacheParentDirectory();
michael@0 3081 if (!directory)
michael@0 3082 return;
michael@0 3083
michael@0 3084 directory->Clone(result);
michael@0 3085 }
michael@0 3086
michael@0 3087 // static
michael@0 3088 void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
michael@0 3089 {
michael@0 3090 nsCOMPtr<nsIFile> directory;
michael@0 3091 GetCacheBaseDirectoty(getter_AddRefs(directory));
michael@0 3092 if (!directory)
michael@0 3093 return;
michael@0 3094
michael@0 3095 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
michael@0 3096 if (NS_FAILED(rv))
michael@0 3097 return;
michael@0 3098
michael@0 3099 directory.forget(result);
michael@0 3100 }
michael@0 3101
michael@0 3102 // static
michael@0 3103 void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
michael@0 3104 {
michael@0 3105 nsCOMPtr<nsIFile> directory;
michael@0 3106 GetCacheBaseDirectoty(getter_AddRefs(directory));
michael@0 3107 if (!directory)
michael@0 3108 return;
michael@0 3109
michael@0 3110 nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
michael@0 3111 if (NS_FAILED(rv))
michael@0 3112 return;
michael@0 3113
michael@0 3114 directory.forget(result);
michael@0 3115 }
michael@0 3116
michael@0 3117
michael@0 3118 #if defined(PR_LOGGING)
michael@0 3119 void
michael@0 3120 nsCacheService::LogCacheStatistics()
michael@0 3121 {
michael@0 3122 uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
michael@0 3123 ((double)(mCacheHits + mCacheMisses))) * 100);
michael@0 3124 CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
michael@0 3125 CACHE_LOG_ALWAYS((" TotalEntries = %d\n", mTotalEntries));
michael@0 3126 CACHE_LOG_ALWAYS((" Cache Hits = %d\n", mCacheHits));
michael@0 3127 CACHE_LOG_ALWAYS((" Cache Misses = %d\n", mCacheMisses));
michael@0 3128 CACHE_LOG_ALWAYS((" Cache Hit %% = %d%%\n", hitPercentage));
michael@0 3129 CACHE_LOG_ALWAYS((" Max Key Length = %d\n", mMaxKeyLength));
michael@0 3130 CACHE_LOG_ALWAYS((" Max Meta Size = %d\n", mMaxMetaSize));
michael@0 3131 CACHE_LOG_ALWAYS((" Max Data Size = %d\n", mMaxDataSize));
michael@0 3132 CACHE_LOG_ALWAYS(("\n"));
michael@0 3133 CACHE_LOG_ALWAYS((" Deactivate Failures = %d\n",
michael@0 3134 mDeactivateFailures));
michael@0 3135 CACHE_LOG_ALWAYS((" Deactivated Unbound Entries = %d\n",
michael@0 3136 mDeactivatedUnboundEntries));
michael@0 3137 }
michael@0 3138 #endif
michael@0 3139
michael@0 3140 nsresult
michael@0 3141 nsCacheService::SetDiskSmartSize()
michael@0 3142 {
michael@0 3143 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE));
michael@0 3144
michael@0 3145 if (!gService) return NS_ERROR_NOT_AVAILABLE;
michael@0 3146
michael@0 3147 return gService->SetDiskSmartSize_Locked();
michael@0 3148 }
michael@0 3149
michael@0 3150 nsresult
michael@0 3151 nsCacheService::SetDiskSmartSize_Locked()
michael@0 3152 {
michael@0 3153 nsresult rv;
michael@0 3154
michael@0 3155 if (mozilla::net::CacheObserver::UseNewCache()) {
michael@0 3156 return NS_ERROR_NOT_AVAILABLE;
michael@0 3157 }
michael@0 3158
michael@0 3159 if (!mObserver->DiskCacheParentDirectory())
michael@0 3160 return NS_ERROR_NOT_AVAILABLE;
michael@0 3161
michael@0 3162 if (!mDiskDevice)
michael@0 3163 return NS_ERROR_NOT_AVAILABLE;
michael@0 3164
michael@0 3165 if (!mObserver->SmartSizeEnabled())
michael@0 3166 return NS_ERROR_NOT_AVAILABLE;
michael@0 3167
michael@0 3168 nsAutoString cachePath;
michael@0 3169 rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
michael@0 3170 if (NS_SUCCEEDED(rv)) {
michael@0 3171 nsCOMPtr<nsIRunnable> event =
michael@0 3172 new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
michael@0 3173 mObserver->ShouldUseOldMaxSmartSize());
michael@0 3174 DispatchToCacheIOThread(event);
michael@0 3175 } else {
michael@0 3176 return NS_ERROR_FAILURE;
michael@0 3177 }
michael@0 3178
michael@0 3179 return NS_OK;
michael@0 3180 }
michael@0 3181
michael@0 3182 void
michael@0 3183 nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
michael@0 3184 nsIFile *aNewCacheDir,
michael@0 3185 const char *aCacheSubdir)
michael@0 3186 {
michael@0 3187 bool same;
michael@0 3188 if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
michael@0 3189 return;
michael@0 3190
michael@0 3191 nsCOMPtr<nsIFile> aOldCacheSubdir;
michael@0 3192 aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
michael@0 3193
michael@0 3194 nsresult rv = aOldCacheSubdir->AppendNative(
michael@0 3195 nsDependentCString(aCacheSubdir));
michael@0 3196 if (NS_FAILED(rv))
michael@0 3197 return;
michael@0 3198
michael@0 3199 bool exists;
michael@0 3200 if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
michael@0 3201 return;
michael@0 3202
michael@0 3203 nsCOMPtr<nsIFile> aNewCacheSubdir;
michael@0 3204 aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
michael@0 3205
michael@0 3206 rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
michael@0 3207 if (NS_FAILED(rv))
michael@0 3208 return;
michael@0 3209
michael@0 3210 nsAutoCString newPath;
michael@0 3211 rv = aNewCacheSubdir->GetNativePath(newPath);
michael@0 3212 if (NS_FAILED(rv))
michael@0 3213 return;
michael@0 3214
michael@0 3215 if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
michael@0 3216 // New cache directory does not exist, try to move the old one here
michael@0 3217 // rename needs an empty target directory
michael@0 3218
michael@0 3219 // Make sure the parent of the target sub-dir exists
michael@0 3220 rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
michael@0 3221 if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
michael@0 3222 nsAutoCString oldPath;
michael@0 3223 rv = aOldCacheSubdir->GetNativePath(oldPath);
michael@0 3224 if (NS_FAILED(rv))
michael@0 3225 return;
michael@0 3226 if (rename(oldPath.get(), newPath.get()) == 0)
michael@0 3227 return;
michael@0 3228 }
michael@0 3229 }
michael@0 3230
michael@0 3231 // Delay delete by 1 minute to avoid IO thrash on startup.
michael@0 3232 nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
michael@0 3233 }
michael@0 3234
michael@0 3235 static bool
michael@0 3236 IsEntryPrivate(nsCacheEntry* entry)
michael@0 3237 {
michael@0 3238 return entry->IsPrivate();
michael@0 3239 }
michael@0 3240
michael@0 3241 void
michael@0 3242 nsCacheService::LeavePrivateBrowsing()
michael@0 3243 {
michael@0 3244 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHESERVICE_LEAVEPRIVATEBROWSING));
michael@0 3245
michael@0 3246 gService->DoomActiveEntries(IsEntryPrivate);
michael@0 3247
michael@0 3248 if (gService->mMemoryDevice) {
michael@0 3249 // clear memory cache
michael@0 3250 gService->mMemoryDevice->EvictPrivateEntries();
michael@0 3251 }
michael@0 3252 }
michael@0 3253
michael@0 3254 MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
michael@0 3255
michael@0 3256 NS_IMETHODIMP
michael@0 3257 nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 3258 nsISupports* aData)
michael@0 3259 {
michael@0 3260 size_t disk = 0;
michael@0 3261 if (mDiskDevice) {
michael@0 3262 nsCacheServiceAutoLock
michael@0 3263 lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));
michael@0 3264 disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
michael@0 3265 }
michael@0 3266
michael@0 3267 size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
michael@0 3268
michael@0 3269 #define REPORT(_path, _amount, _desc) \
michael@0 3270 do { \
michael@0 3271 nsresult rv; \
michael@0 3272 rv = aHandleReport->Callback(EmptyCString(), \
michael@0 3273 NS_LITERAL_CSTRING(_path), \
michael@0 3274 KIND_HEAP, UNITS_BYTES, _amount, \
michael@0 3275 NS_LITERAL_CSTRING(_desc), aData); \
michael@0 3276 NS_ENSURE_SUCCESS(rv, rv); \
michael@0 3277 } while (0)
michael@0 3278
michael@0 3279 REPORT("explicit/network/disk-cache", disk,
michael@0 3280 "Memory used by the network disk cache.");
michael@0 3281
michael@0 3282 REPORT("explicit/network/memory-cache", memory,
michael@0 3283 "Memory used by the network memory cache.");
michael@0 3284
michael@0 3285 return NS_OK;
michael@0 3286 }

mercurial