Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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 | } |