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