1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheStorageService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1444 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheLog.h" 1.9 +#include "CacheStorageService.h" 1.10 +#include "CacheFileIOManager.h" 1.11 +#include "CacheObserver.h" 1.12 +#include "CacheIndex.h" 1.13 + 1.14 +#include "nsICacheStorageVisitor.h" 1.15 +#include "nsIObserverService.h" 1.16 +#include "CacheStorage.h" 1.17 +#include "AppCacheStorage.h" 1.18 +#include "CacheEntry.h" 1.19 +#include "CacheFileUtils.h" 1.20 + 1.21 +#include "OldWrappers.h" 1.22 +#include "nsCacheService.h" 1.23 +#include "nsDeleteDir.h" 1.24 + 1.25 +#include "nsIFile.h" 1.26 +#include "nsIURI.h" 1.27 +#include "nsCOMPtr.h" 1.28 +#include "nsAutoPtr.h" 1.29 +#include "nsNetCID.h" 1.30 +#include "nsServiceManagerUtils.h" 1.31 +#include "mozilla/TimeStamp.h" 1.32 +#include "mozilla/DebugOnly.h" 1.33 +#include "mozilla/VisualEventTracer.h" 1.34 +#include "mozilla/Services.h" 1.35 + 1.36 +namespace mozilla { 1.37 +namespace net { 1.38 + 1.39 +namespace { 1.40 + 1.41 +void AppendMemoryStorageID(nsAutoCString &key) 1.42 +{ 1.43 + key.Append('/'); 1.44 + key.Append('M'); 1.45 +} 1.46 + 1.47 +} 1.48 + 1.49 +// Not defining as static or class member of CacheStorageService since 1.50 +// it would otherwise need to include CacheEntry.h and that then would 1.51 +// need to be exported to make nsNetModule.cpp compilable. 1.52 +typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable> 1.53 + GlobalEntryTables; 1.54 + 1.55 +/** 1.56 + * Keeps tables of entries. There is one entries table for each distinct load 1.57 + * context type. The distinction is based on following load context info states: 1.58 + * <isPrivate|isAnon|appId|inBrowser> which builds a mapping key. 1.59 + * 1.60 + * Thread-safe to access, protected by the service mutex. 1.61 + */ 1.62 +static GlobalEntryTables* sGlobalEntryTables; 1.63 + 1.64 +CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags) 1.65 +: mReportedMemoryConsumption(0) 1.66 +, mFlags(aFlags) 1.67 +{ 1.68 +} 1.69 + 1.70 +void 1.71 +CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize) 1.72 +{ 1.73 + if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) { 1.74 + CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize); 1.75 + } 1.76 +} 1.77 + 1.78 +CacheStorageService::MemoryPool::MemoryPool(EType aType) 1.79 +: mType(aType) 1.80 +, mMemorySize(0) 1.81 +{ 1.82 +} 1.83 + 1.84 +CacheStorageService::MemoryPool::~MemoryPool() 1.85 +{ 1.86 + if (mMemorySize != 0) { 1.87 + NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?"); 1.88 + } 1.89 +} 1.90 + 1.91 +uint32_t const 1.92 +CacheStorageService::MemoryPool::Limit() const 1.93 +{ 1.94 + switch (mType) { 1.95 + case DISK: 1.96 + return CacheObserver::MetadataMemoryLimit(); 1.97 + case MEMORY: 1.98 + return CacheObserver::MemoryCacheCapacity(); 1.99 + } 1.100 + 1.101 + MOZ_CRASH("Bad pool type"); 1.102 + return 0; 1.103 +} 1.104 + 1.105 +NS_IMPL_ISUPPORTS(CacheStorageService, 1.106 + nsICacheStorageService, 1.107 + nsIMemoryReporter, 1.108 + nsITimerCallback) 1.109 + 1.110 +CacheStorageService* CacheStorageService::sSelf = nullptr; 1.111 + 1.112 +CacheStorageService::CacheStorageService() 1.113 +: mLock("CacheStorageService") 1.114 +, mShutdown(false) 1.115 +, mDiskPool(MemoryPool::DISK) 1.116 +, mMemoryPool(MemoryPool::MEMORY) 1.117 +{ 1.118 + CacheFileIOManager::Init(); 1.119 + 1.120 + MOZ_ASSERT(!sSelf); 1.121 + 1.122 + sSelf = this; 1.123 + sGlobalEntryTables = new GlobalEntryTables(); 1.124 + 1.125 + RegisterStrongMemoryReporter(this); 1.126 +} 1.127 + 1.128 +CacheStorageService::~CacheStorageService() 1.129 +{ 1.130 + LOG(("CacheStorageService::~CacheStorageService")); 1.131 + sSelf = nullptr; 1.132 +} 1.133 + 1.134 +void CacheStorageService::Shutdown() 1.135 +{ 1.136 + if (mShutdown) 1.137 + return; 1.138 + 1.139 + LOG(("CacheStorageService::Shutdown - start")); 1.140 + 1.141 + mShutdown = true; 1.142 + 1.143 + nsCOMPtr<nsIRunnable> event = 1.144 + NS_NewRunnableMethod(this, &CacheStorageService::ShutdownBackground); 1.145 + Dispatch(event); 1.146 + 1.147 + mozilla::MutexAutoLock lock(mLock); 1.148 + sGlobalEntryTables->Clear(); 1.149 + delete sGlobalEntryTables; 1.150 + sGlobalEntryTables = nullptr; 1.151 + 1.152 + LOG(("CacheStorageService::Shutdown - done")); 1.153 +} 1.154 + 1.155 +void CacheStorageService::ShutdownBackground() 1.156 +{ 1.157 + MOZ_ASSERT(IsOnManagementThread()); 1.158 + 1.159 + Pool(false).mFrecencyArray.Clear(); 1.160 + Pool(false).mExpirationArray.Clear(); 1.161 + Pool(true).mFrecencyArray.Clear(); 1.162 + Pool(true).mExpirationArray.Clear(); 1.163 +} 1.164 + 1.165 +// Internal management methods 1.166 + 1.167 +namespace { // anon 1.168 + 1.169 +// WalkRunnable 1.170 +// Responsible to visit the storage and walk all entries on it asynchronously 1.171 + 1.172 +class WalkRunnable : public nsRunnable 1.173 +{ 1.174 +public: 1.175 + WalkRunnable(nsCSubstring const & aContextKey, bool aVisitEntries, 1.176 + bool aUsingDisk, 1.177 + nsICacheStorageVisitor* aVisitor) 1.178 + : mContextKey(aContextKey) 1.179 + , mCallback(aVisitor) 1.180 + , mSize(0) 1.181 + , mNotifyStorage(true) 1.182 + , mVisitEntries(aVisitEntries) 1.183 + , mUsingDisk(aUsingDisk) 1.184 + { 1.185 + MOZ_ASSERT(NS_IsMainThread()); 1.186 + } 1.187 + 1.188 +private: 1.189 + NS_IMETHODIMP Run() 1.190 + { 1.191 + if (CacheStorageService::IsOnManagementThread()) { 1.192 + LOG(("WalkRunnable::Run - collecting [this=%p, disk=%d]", this, (bool)mUsingDisk)); 1.193 + // First, walk, count and grab all entries from the storage 1.194 + // TODO 1.195 + // - walk files on disk, when the storage is not private 1.196 + // - should create representative entries only for the time 1.197 + // of need 1.198 + 1.199 + mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock()); 1.200 + 1.201 + if (!CacheStorageService::IsRunning()) 1.202 + return NS_ERROR_NOT_INITIALIZED; 1.203 + 1.204 + CacheEntryTable* entries; 1.205 + if (sGlobalEntryTables->Get(mContextKey, &entries)) 1.206 + entries->EnumerateRead(&WalkRunnable::WalkStorage, this); 1.207 + 1.208 + // Next, we dispatch to the main thread 1.209 + } 1.210 + else if (NS_IsMainThread()) { 1.211 + LOG(("WalkRunnable::Run - notifying [this=%p, disk=%d]", this, (bool)mUsingDisk)); 1.212 + if (mNotifyStorage) { 1.213 + LOG((" storage")); 1.214 + // Second, notify overall storage info 1.215 + mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize); 1.216 + if (!mVisitEntries) 1.217 + return NS_OK; // done 1.218 + 1.219 + mNotifyStorage = false; 1.220 + } 1.221 + else { 1.222 + LOG((" entry [left=%d]", mEntryArray.Length())); 1.223 + // Third, notify each entry until depleted. 1.224 + if (!mEntryArray.Length()) { 1.225 + mCallback->OnCacheEntryVisitCompleted(); 1.226 + return NS_OK; // done 1.227 + } 1.228 + 1.229 + mCallback->OnCacheEntryInfo(mEntryArray[0]); 1.230 + mEntryArray.RemoveElementAt(0); 1.231 + 1.232 + // Dispatch to the main thread again 1.233 + } 1.234 + } 1.235 + else { 1.236 + MOZ_ASSERT(false); 1.237 + return NS_ERROR_FAILURE; 1.238 + } 1.239 + 1.240 + NS_DispatchToMainThread(this); 1.241 + return NS_OK; 1.242 + } 1.243 + 1.244 + virtual ~WalkRunnable() 1.245 + { 1.246 + if (mCallback) 1.247 + ProxyReleaseMainThread(mCallback); 1.248 + } 1.249 + 1.250 + static PLDHashOperator 1.251 + WalkStorage(const nsACString& aKey, 1.252 + CacheEntry* aEntry, 1.253 + void* aClosure) 1.254 + { 1.255 + WalkRunnable* walker = static_cast<WalkRunnable*>(aClosure); 1.256 + 1.257 + if (!walker->mUsingDisk && aEntry->IsUsingDiskLocked()) 1.258 + return PL_DHASH_NEXT; 1.259 + 1.260 + walker->mSize += aEntry->GetMetadataMemoryConsumption(); 1.261 + 1.262 + int64_t size; 1.263 + if (NS_SUCCEEDED(aEntry->GetDataSize(&size))) 1.264 + walker->mSize += size; 1.265 + 1.266 + walker->mEntryArray.AppendElement(aEntry); 1.267 + return PL_DHASH_NEXT; 1.268 + } 1.269 + 1.270 + nsCString mContextKey; 1.271 + nsCOMPtr<nsICacheStorageVisitor> mCallback; 1.272 + nsTArray<nsRefPtr<CacheEntry> > mEntryArray; 1.273 + 1.274 + uint64_t mSize; 1.275 + 1.276 + bool mNotifyStorage : 1; 1.277 + bool mVisitEntries : 1; 1.278 + bool mUsingDisk : 1; 1.279 +}; 1.280 + 1.281 +PLDHashOperator CollectPrivateContexts(const nsACString& aKey, 1.282 + CacheEntryTable* aTable, 1.283 + void* aClosure) 1.284 +{ 1.285 + nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey); 1.286 + if (info && info->IsPrivate()) { 1.287 + nsTArray<nsCString>* keys = static_cast<nsTArray<nsCString>*>(aClosure); 1.288 + keys->AppendElement(aKey); 1.289 + } 1.290 + 1.291 + return PL_DHASH_NEXT; 1.292 +} 1.293 + 1.294 +PLDHashOperator CollectContexts(const nsACString& aKey, 1.295 + CacheEntryTable* aTable, 1.296 + void* aClosure) 1.297 +{ 1.298 + nsTArray<nsCString>* keys = static_cast<nsTArray<nsCString>*>(aClosure); 1.299 + keys->AppendElement(aKey); 1.300 + 1.301 + return PL_DHASH_NEXT; 1.302 +} 1.303 + 1.304 +} // anon 1.305 + 1.306 +void CacheStorageService::DropPrivateBrowsingEntries() 1.307 +{ 1.308 + mozilla::MutexAutoLock lock(mLock); 1.309 + 1.310 + if (mShutdown) 1.311 + return; 1.312 + 1.313 + nsTArray<nsCString> keys; 1.314 + sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys); 1.315 + 1.316 + for (uint32_t i = 0; i < keys.Length(); ++i) 1.317 + DoomStorageEntries(keys[i], nullptr, true, nullptr); 1.318 +} 1.319 + 1.320 +// static 1.321 +void CacheStorageService::WipeCacheDirectory(uint32_t aVersion) 1.322 +{ 1.323 + nsCOMPtr<nsIFile> cacheDir; 1.324 + switch (aVersion) { 1.325 + case 0: 1.326 + nsCacheService::GetDiskCacheDirectory(getter_AddRefs(cacheDir)); 1.327 + break; 1.328 + case 1: 1.329 + CacheFileIOManager::GetCacheDirectory(getter_AddRefs(cacheDir)); 1.330 + break; 1.331 + } 1.332 + 1.333 + if (!cacheDir) 1.334 + return; 1.335 + 1.336 + nsDeleteDir::DeleteDir(cacheDir, true, 30000); 1.337 +} 1.338 + 1.339 +// Helper methods 1.340 + 1.341 +// static 1.342 +bool CacheStorageService::IsOnManagementThread() 1.343 +{ 1.344 + nsRefPtr<CacheStorageService> service = Self(); 1.345 + if (!service) 1.346 + return false; 1.347 + 1.348 + nsCOMPtr<nsIEventTarget> target = service->Thread(); 1.349 + if (!target) 1.350 + return false; 1.351 + 1.352 + bool currentThread; 1.353 + nsresult rv = target->IsOnCurrentThread(¤tThread); 1.354 + return NS_SUCCEEDED(rv) && currentThread; 1.355 +} 1.356 + 1.357 +already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const 1.358 +{ 1.359 + return CacheFileIOManager::IOTarget(); 1.360 +} 1.361 + 1.362 +nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent) 1.363 +{ 1.364 + nsRefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread(); 1.365 + if (!cacheIOThread) 1.366 + return NS_ERROR_NOT_AVAILABLE; 1.367 + 1.368 + return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT); 1.369 +} 1.370 + 1.371 +// nsICacheStorageService 1.372 + 1.373 +NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo, 1.374 + nsICacheStorage * *_retval) 1.375 +{ 1.376 + NS_ENSURE_ARG(aLoadContextInfo); 1.377 + NS_ENSURE_ARG(_retval); 1.378 + 1.379 + nsCOMPtr<nsICacheStorage> storage; 1.380 + if (CacheObserver::UseNewCache()) { 1.381 + storage = new CacheStorage(aLoadContextInfo, false, false); 1.382 + } 1.383 + else { 1.384 + storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr); 1.385 + } 1.386 + 1.387 + storage.forget(_retval); 1.388 + return NS_OK; 1.389 +} 1.390 + 1.391 +NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo, 1.392 + bool aLookupAppCache, 1.393 + nsICacheStorage * *_retval) 1.394 +{ 1.395 + NS_ENSURE_ARG(aLoadContextInfo); 1.396 + NS_ENSURE_ARG(_retval); 1.397 + 1.398 + // TODO save some heap granularity - cache commonly used storages. 1.399 + 1.400 + // When disk cache is disabled, still provide a storage, but just keep stuff 1.401 + // in memory. 1.402 + bool useDisk = CacheObserver::UseDiskCache(); 1.403 + 1.404 + nsCOMPtr<nsICacheStorage> storage; 1.405 + if (CacheObserver::UseNewCache()) { 1.406 + storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache); 1.407 + } 1.408 + else { 1.409 + storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr); 1.410 + } 1.411 + 1.412 + storage.forget(_retval); 1.413 + return NS_OK; 1.414 +} 1.415 + 1.416 +NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo, 1.417 + nsIApplicationCache *aApplicationCache, 1.418 + nsICacheStorage * *_retval) 1.419 +{ 1.420 + NS_ENSURE_ARG(aLoadContextInfo); 1.421 + NS_ENSURE_ARG(_retval); 1.422 + 1.423 + nsCOMPtr<nsICacheStorage> storage; 1.424 + if (CacheObserver::UseNewCache()) { 1.425 + // Using classification since cl believes we want to instantiate this method 1.426 + // having the same name as the desired class... 1.427 + storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache); 1.428 + } 1.429 + else { 1.430 + storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache); 1.431 + } 1.432 + 1.433 + storage.forget(_retval); 1.434 + return NS_OK; 1.435 +} 1.436 + 1.437 +NS_IMETHODIMP CacheStorageService::Clear() 1.438 +{ 1.439 + nsresult rv; 1.440 + 1.441 + if (CacheObserver::UseNewCache()) { 1.442 + { 1.443 + mozilla::MutexAutoLock lock(mLock); 1.444 + 1.445 + NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED); 1.446 + 1.447 + nsTArray<nsCString> keys; 1.448 + sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys); 1.449 + 1.450 + for (uint32_t i = 0; i < keys.Length(); ++i) 1.451 + DoomStorageEntries(keys[i], nullptr, true, nullptr); 1.452 + } 1.453 + 1.454 + rv = CacheFileIOManager::EvictAll(); 1.455 + NS_ENSURE_SUCCESS(rv, rv); 1.456 + } else { 1.457 + nsCOMPtr<nsICacheService> serv = 1.458 + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); 1.459 + NS_ENSURE_SUCCESS(rv, rv); 1.460 + 1.461 + rv = serv->EvictEntries(nsICache::STORE_ANYWHERE); 1.462 + NS_ENSURE_SUCCESS(rv, rv); 1.463 + } 1.464 + 1.465 + return NS_OK; 1.466 +} 1.467 + 1.468 +NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat) 1.469 +{ 1.470 + uint32_t what; 1.471 + 1.472 + switch (aWhat) { 1.473 + case PURGE_DISK_DATA_ONLY: 1.474 + what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED; 1.475 + break; 1.476 + 1.477 + case PURGE_DISK_ALL: 1.478 + what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED; 1.479 + break; 1.480 + 1.481 + case PURGE_EVERYTHING: 1.482 + what = CacheEntry::PURGE_WHOLE; 1.483 + break; 1.484 + 1.485 + default: 1.486 + return NS_ERROR_INVALID_ARG; 1.487 + } 1.488 + 1.489 + nsCOMPtr<nsIRunnable> event = 1.490 + new PurgeFromMemoryRunnable(this, what); 1.491 + 1.492 + return Dispatch(event); 1.493 +} 1.494 + 1.495 +NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption( 1.496 + nsICacheStorageConsumptionObserver* aObserver) 1.497 +{ 1.498 + NS_ENSURE_ARG(aObserver); 1.499 + 1.500 + nsresult rv; 1.501 + 1.502 + if (CacheObserver::UseNewCache()) { 1.503 + rv = CacheIndex::AsyncGetDiskConsumption(aObserver); 1.504 + NS_ENSURE_SUCCESS(rv, rv); 1.505 + } else { 1.506 + rv = _OldGetDiskConsumption::Get(aObserver); 1.507 + NS_ENSURE_SUCCESS(rv, rv); 1.508 + } 1.509 + 1.510 + return NS_OK; 1.511 +} 1.512 + 1.513 +NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget) 1.514 +{ 1.515 + NS_ENSURE_ARG(aEventTarget); 1.516 + 1.517 + if (CacheObserver::UseNewCache()) { 1.518 + nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget(); 1.519 + ioTarget.forget(aEventTarget); 1.520 + } 1.521 + else { 1.522 + nsresult rv; 1.523 + 1.524 + nsCOMPtr<nsICacheService> serv = 1.525 + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); 1.526 + NS_ENSURE_SUCCESS(rv, rv); 1.527 + 1.528 + rv = serv->GetCacheIOTarget(aEventTarget); 1.529 + NS_ENSURE_SUCCESS(rv, rv); 1.530 + } 1.531 + 1.532 + return NS_OK; 1.533 +} 1.534 + 1.535 +// Methods used by CacheEntry for management of in-memory structures. 1.536 + 1.537 +namespace { // anon 1.538 + 1.539 +class FrecencyComparator 1.540 +{ 1.541 +public: 1.542 + bool Equals(CacheEntry* a, CacheEntry* b) const { 1.543 + return a->GetFrecency() == b->GetFrecency(); 1.544 + } 1.545 + bool LessThan(CacheEntry* a, CacheEntry* b) const { 1.546 + return a->GetFrecency() < b->GetFrecency(); 1.547 + } 1.548 +}; 1.549 + 1.550 +class ExpirationComparator 1.551 +{ 1.552 +public: 1.553 + bool Equals(CacheEntry* a, CacheEntry* b) const { 1.554 + return a->GetExpirationTime() == b->GetExpirationTime(); 1.555 + } 1.556 + bool LessThan(CacheEntry* a, CacheEntry* b) const { 1.557 + return a->GetExpirationTime() < b->GetExpirationTime(); 1.558 + } 1.559 +}; 1.560 + 1.561 +} // anon 1.562 + 1.563 +void 1.564 +CacheStorageService::RegisterEntry(CacheEntry* aEntry) 1.565 +{ 1.566 + MOZ_ASSERT(IsOnManagementThread()); 1.567 + 1.568 + if (mShutdown || !aEntry->CanRegister()) 1.569 + return; 1.570 + 1.571 + LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry)); 1.572 + 1.573 + MemoryPool& pool = Pool(aEntry->IsUsingDisk()); 1.574 + pool.mFrecencyArray.InsertElementSorted(aEntry, FrecencyComparator()); 1.575 + pool.mExpirationArray.InsertElementSorted(aEntry, ExpirationComparator()); 1.576 + 1.577 + aEntry->SetRegistered(true); 1.578 +} 1.579 + 1.580 +void 1.581 +CacheStorageService::UnregisterEntry(CacheEntry* aEntry) 1.582 +{ 1.583 + MOZ_ASSERT(IsOnManagementThread()); 1.584 + 1.585 + if (!aEntry->IsRegistered()) 1.586 + return; 1.587 + 1.588 + LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry)); 1.589 + 1.590 + MemoryPool& pool = Pool(aEntry->IsUsingDisk()); 1.591 + mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry); 1.592 + mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry); 1.593 + 1.594 + MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration)); 1.595 + 1.596 + // Note: aEntry->CanRegister() since now returns false 1.597 + aEntry->SetRegistered(false); 1.598 +} 1.599 + 1.600 +static bool 1.601 +AddExactEntry(CacheEntryTable* aEntries, 1.602 + nsCString const& aKey, 1.603 + CacheEntry* aEntry, 1.604 + bool aOverwrite) 1.605 +{ 1.606 + nsRefPtr<CacheEntry> existingEntry; 1.607 + if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) { 1.608 + bool equals = existingEntry == aEntry; 1.609 + LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals)); 1.610 + return equals; // Already there... 1.611 + } 1.612 + 1.613 + LOG(("AddExactEntry [entry=%p put]", aEntry)); 1.614 + aEntries->Put(aKey, aEntry); 1.615 + return true; 1.616 +} 1.617 + 1.618 +static bool 1.619 +RemoveExactEntry(CacheEntryTable* aEntries, 1.620 + nsCString const& aKey, 1.621 + CacheEntry* aEntry, 1.622 + bool aOverwrite) 1.623 +{ 1.624 + nsRefPtr<CacheEntry> existingEntry; 1.625 + if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) { 1.626 + LOG(("RemoveExactEntry [entry=%p already gone]", aEntry)); 1.627 + return false; // Already removed... 1.628 + } 1.629 + 1.630 + if (!aOverwrite && existingEntry != aEntry) { 1.631 + LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry)); 1.632 + return false; // Already replaced... 1.633 + } 1.634 + 1.635 + LOG(("RemoveExactEntry [entry=%p removed]", aEntry)); 1.636 + aEntries->Remove(aKey); 1.637 + return true; 1.638 +} 1.639 + 1.640 +bool 1.641 +CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced) 1.642 +{ 1.643 + LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry)); 1.644 + 1.645 + nsAutoCString entryKey; 1.646 + nsresult rv = aEntry->HashingKey(entryKey); 1.647 + if (NS_FAILED(rv)) { 1.648 + NS_ERROR("aEntry->HashingKey() failed?"); 1.649 + return false; 1.650 + } 1.651 + 1.652 + mozilla::MutexAutoLock lock(mLock); 1.653 + 1.654 + if (mShutdown) { 1.655 + LOG((" after shutdown")); 1.656 + return false; 1.657 + } 1.658 + 1.659 + if (aOnlyUnreferenced && aEntry->IsReferenced()) { 1.660 + LOG((" still referenced, not removing")); 1.661 + return false; 1.662 + } 1.663 + 1.664 + CacheEntryTable* entries; 1.665 + if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries)) 1.666 + RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */); 1.667 + 1.668 + nsAutoCString memoryStorageID(aEntry->GetStorageID()); 1.669 + AppendMemoryStorageID(memoryStorageID); 1.670 + 1.671 + if (sGlobalEntryTables->Get(memoryStorageID, &entries)) 1.672 + RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */); 1.673 + 1.674 + return true; 1.675 +} 1.676 + 1.677 +void 1.678 +CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry, 1.679 + bool aOnlyInMemory, 1.680 + bool aOverwrite) 1.681 +{ 1.682 + LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]", 1.683 + aEntry, aOnlyInMemory, aOverwrite)); 1.684 + // This method is responsible to put this entry to a special record hashtable 1.685 + // that contains only entries that are stored in memory. 1.686 + // Keep in mind that every entry, regardless of whether is in-memory-only or not 1.687 + // is always recorded in the storage master hash table, the one identified by 1.688 + // CacheEntry.StorageID(). 1.689 + 1.690 + mLock.AssertCurrentThreadOwns(); 1.691 + 1.692 + if (mShutdown) { 1.693 + LOG((" after shutdown")); 1.694 + return; 1.695 + } 1.696 + 1.697 + nsresult rv; 1.698 + 1.699 + nsAutoCString entryKey; 1.700 + rv = aEntry->HashingKey(entryKey); 1.701 + if (NS_FAILED(rv)) { 1.702 + NS_ERROR("aEntry->HashingKey() failed?"); 1.703 + return; 1.704 + } 1.705 + 1.706 + CacheEntryTable* entries = nullptr; 1.707 + nsAutoCString memoryStorageID(aEntry->GetStorageID()); 1.708 + AppendMemoryStorageID(memoryStorageID); 1.709 + 1.710 + if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) { 1.711 + if (!aOnlyInMemory) { 1.712 + LOG((" not recorded as memory only")); 1.713 + return; 1.714 + } 1.715 + 1.716 + entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY); 1.717 + sGlobalEntryTables->Put(memoryStorageID, entries); 1.718 + LOG((" new memory-only storage table for %s", memoryStorageID.get())); 1.719 + } 1.720 + 1.721 + if (aOnlyInMemory) { 1.722 + AddExactEntry(entries, entryKey, aEntry, aOverwrite); 1.723 + } 1.724 + else { 1.725 + RemoveExactEntry(entries, entryKey, aEntry, aOverwrite); 1.726 + } 1.727 +} 1.728 + 1.729 +void 1.730 +CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer, 1.731 + uint32_t aCurrentMemoryConsumption) 1.732 +{ 1.733 + LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]", 1.734 + aConsumer, aCurrentMemoryConsumption)); 1.735 + 1.736 + uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption; 1.737 + if (savedMemorySize == aCurrentMemoryConsumption) 1.738 + return; 1.739 + 1.740 + // Exchange saved size with current one. 1.741 + aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption; 1.742 + 1.743 + bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY); 1.744 + bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange( 1.745 + savedMemorySize, aCurrentMemoryConsumption); 1.746 + 1.747 + if (!overLimit) 1.748 + return; 1.749 + 1.750 + // It's likely the timer has already been set when we get here, 1.751 + // check outside the lock to save resources. 1.752 + if (mPurgeTimer) 1.753 + return; 1.754 + 1.755 + // We don't know if this is called under the service lock or not, 1.756 + // hence rather dispatch. 1.757 + nsRefPtr<nsIEventTarget> cacheIOTarget = Thread(); 1.758 + if (!cacheIOTarget) 1.759 + return; 1.760 + 1.761 + // Dispatch as a priority task, we want to set the purge timer 1.762 + // ASAP to prevent vain redispatch of this event. 1.763 + nsCOMPtr<nsIRunnable> event = 1.764 + NS_NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit); 1.765 + cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); 1.766 +} 1.767 + 1.768 +bool 1.769 +CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize, 1.770 + uint32_t aCurrentMemoryConsumption) 1.771 +{ 1.772 + mMemorySize -= aSavedMemorySize; 1.773 + mMemorySize += aCurrentMemoryConsumption; 1.774 + 1.775 + LOG((" mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize)); 1.776 + 1.777 + // Bypass purging when memory has not grew up significantly 1.778 + if (aCurrentMemoryConsumption <= aSavedMemorySize) 1.779 + return false; 1.780 + 1.781 + return mMemorySize > Limit(); 1.782 +} 1.783 + 1.784 +void 1.785 +CacheStorageService::SchedulePurgeOverMemoryLimit() 1.786 +{ 1.787 + mozilla::MutexAutoLock lock(mLock); 1.788 + 1.789 + if (mPurgeTimer) 1.790 + return; 1.791 + 1.792 + mPurgeTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.793 + if (mPurgeTimer) 1.794 + mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT); 1.795 +} 1.796 + 1.797 +NS_IMETHODIMP 1.798 +CacheStorageService::Notify(nsITimer* aTimer) 1.799 +{ 1.800 + if (aTimer == mPurgeTimer) { 1.801 + mPurgeTimer = nullptr; 1.802 + 1.803 + nsCOMPtr<nsIRunnable> event = 1.804 + NS_NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit); 1.805 + Dispatch(event); 1.806 + } 1.807 + 1.808 + return NS_OK; 1.809 +} 1.810 + 1.811 +void 1.812 +CacheStorageService::PurgeOverMemoryLimit() 1.813 +{ 1.814 + MOZ_ASSERT(IsOnManagementThread()); 1.815 + 1.816 + LOG(("CacheStorageService::PurgeOverMemoryLimit")); 1.817 + 1.818 + Pool(true).PurgeOverMemoryLimit(); 1.819 + Pool(false).PurgeOverMemoryLimit(); 1.820 +} 1.821 + 1.822 +void 1.823 +CacheStorageService::MemoryPool::PurgeOverMemoryLimit() 1.824 +{ 1.825 +#ifdef PR_LOGGING 1.826 + TimeStamp start(TimeStamp::Now()); 1.827 +#endif 1.828 + 1.829 + uint32_t const memoryLimit = Limit(); 1.830 + if (mMemorySize > memoryLimit) { 1.831 + LOG((" memory data consumption over the limit, abandon expired entries")); 1.832 + PurgeExpired(); 1.833 + } 1.834 + 1.835 + bool frecencyNeedsSort = true; 1.836 + 1.837 + // No longer makes sense since: 1.838 + // Memory entries are never purged partially, only as a whole when the memory 1.839 + // cache limit is overreached. 1.840 + // Disk entries throw the data away ASAP so that only metadata are kept. 1.841 + // TODO when this concept of two separate pools is found working, the code should 1.842 + // clean up. 1.843 +#if 0 1.844 + if (mMemorySize > memoryLimit) { 1.845 + LOG((" memory data consumption over the limit, abandon disk backed data")); 1.846 + PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED); 1.847 + } 1.848 + 1.849 + if (mMemorySize > memoryLimit) { 1.850 + LOG((" metadata consumtion over the limit, abandon disk backed entries")); 1.851 + PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED); 1.852 + } 1.853 +#endif 1.854 + 1.855 + if (mMemorySize > memoryLimit) { 1.856 + LOG((" memory data consumption over the limit, abandon any entry")); 1.857 + PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE); 1.858 + } 1.859 + 1.860 + LOG((" purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds())); 1.861 +} 1.862 + 1.863 +void 1.864 +CacheStorageService::MemoryPool::PurgeExpired() 1.865 +{ 1.866 + MOZ_ASSERT(IsOnManagementThread()); 1.867 + 1.868 + mExpirationArray.Sort(ExpirationComparator()); 1.869 + uint32_t now = NowInSeconds(); 1.870 + 1.871 + uint32_t const memoryLimit = Limit(); 1.872 + 1.873 + for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) { 1.874 + if (CacheIOThread::YieldAndRerun()) 1.875 + return; 1.876 + 1.877 + nsRefPtr<CacheEntry> entry = mExpirationArray[i]; 1.878 + 1.879 + uint32_t expirationTime = entry->GetExpirationTime(); 1.880 + if (expirationTime > 0 && expirationTime <= now) { 1.881 + LOG((" dooming expired entry=%p, exptime=%u (now=%u)", 1.882 + entry.get(), entry->GetExpirationTime(), now)); 1.883 + 1.884 + entry->PurgeAndDoom(); 1.885 + continue; 1.886 + } 1.887 + 1.888 + // not purged, move to the next one 1.889 + ++i; 1.890 + } 1.891 +} 1.892 + 1.893 +void 1.894 +CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat) 1.895 +{ 1.896 + MOZ_ASSERT(IsOnManagementThread()); 1.897 + 1.898 + if (aFrecencyNeedsSort) { 1.899 + mFrecencyArray.Sort(FrecencyComparator()); 1.900 + aFrecencyNeedsSort = false; 1.901 + } 1.902 + 1.903 + uint32_t const memoryLimit = Limit(); 1.904 + 1.905 + for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) { 1.906 + if (CacheIOThread::YieldAndRerun()) 1.907 + return; 1.908 + 1.909 + nsRefPtr<CacheEntry> entry = mFrecencyArray[i]; 1.910 + 1.911 + if (entry->Purge(aWhat)) { 1.912 + LOG((" abandoned (%d), entry=%p, frecency=%1.10f", 1.913 + aWhat, entry.get(), entry->GetFrecency())); 1.914 + continue; 1.915 + } 1.916 + 1.917 + // not purged, move to the next one 1.918 + ++i; 1.919 + } 1.920 +} 1.921 + 1.922 +void 1.923 +CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat) 1.924 +{ 1.925 + LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat)); 1.926 + MOZ_ASSERT(IsOnManagementThread()); 1.927 + 1.928 + for (uint32_t i = 0; i < mFrecencyArray.Length();) { 1.929 + if (CacheIOThread::YieldAndRerun()) 1.930 + return; 1.931 + 1.932 + nsRefPtr<CacheEntry> entry = mFrecencyArray[i]; 1.933 + 1.934 + if (entry->Purge(aWhat)) { 1.935 + LOG((" abandoned entry=%p", entry.get())); 1.936 + continue; 1.937 + } 1.938 + 1.939 + // not purged, move to the next one 1.940 + ++i; 1.941 + } 1.942 +} 1.943 + 1.944 +// Methods exposed to and used by CacheStorage. 1.945 + 1.946 +nsresult 1.947 +CacheStorageService::AddStorageEntry(CacheStorage const* aStorage, 1.948 + nsIURI* aURI, 1.949 + const nsACString & aIdExtension, 1.950 + bool aCreateIfNotExist, 1.951 + bool aReplace, 1.952 + CacheEntryHandle** aResult) 1.953 +{ 1.954 + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); 1.955 + 1.956 + NS_ENSURE_ARG(aStorage); 1.957 + 1.958 + nsAutoCString contextKey; 1.959 + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); 1.960 + 1.961 + return AddStorageEntry(contextKey, aURI, aIdExtension, 1.962 + aStorage->WriteToDisk(), aCreateIfNotExist, aReplace, 1.963 + aResult); 1.964 +} 1.965 + 1.966 +nsresult 1.967 +CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, 1.968 + nsIURI* aURI, 1.969 + const nsACString & aIdExtension, 1.970 + bool aWriteToDisk, 1.971 + bool aCreateIfNotExist, 1.972 + bool aReplace, 1.973 + CacheEntryHandle** aResult) 1.974 +{ 1.975 + NS_ENSURE_ARG(aURI); 1.976 + 1.977 + nsresult rv; 1.978 + 1.979 + nsAutoCString entryKey; 1.980 + rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); 1.981 + NS_ENSURE_SUCCESS(rv, rv); 1.982 + 1.983 + LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]", 1.984 + entryKey.get(), aContextKey.BeginReading())); 1.985 + 1.986 + nsRefPtr<CacheEntry> entry; 1.987 + nsRefPtr<CacheEntryHandle> handle; 1.988 + 1.989 + { 1.990 + mozilla::MutexAutoLock lock(mLock); 1.991 + 1.992 + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); 1.993 + 1.994 + // Ensure storage table 1.995 + CacheEntryTable* entries; 1.996 + if (!sGlobalEntryTables->Get(aContextKey, &entries)) { 1.997 + entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES); 1.998 + sGlobalEntryTables->Put(aContextKey, entries); 1.999 + LOG((" new storage entries table for context %s", aContextKey.BeginReading())); 1.1000 + } 1.1001 + 1.1002 + bool entryExists = entries->Get(entryKey, getter_AddRefs(entry)); 1.1003 + 1.1004 + // check whether the file is already doomed 1.1005 + if (entryExists && entry->IsFileDoomed() && !aReplace) { 1.1006 + LOG((" file already doomed, replacing the entry")); 1.1007 + aReplace = true; 1.1008 + } 1.1009 + 1.1010 + // If truncate is demanded, delete and doom the current entry 1.1011 + if (entryExists && aReplace) { 1.1012 + entries->Remove(entryKey); 1.1013 + 1.1014 + LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get())); 1.1015 + // On purpose called under the lock to prevent races of doom and open on I/O thread 1.1016 + // No need to remove from both memory-only and all-entries tables. The new entry 1.1017 + // will overwrite the shadow entry in its ctor. 1.1018 + entry->DoomAlreadyRemoved(); 1.1019 + 1.1020 + entry = nullptr; 1.1021 + entryExists = false; 1.1022 + } 1.1023 + 1.1024 + if (entryExists && entry->SetUsingDisk(aWriteToDisk)) { 1.1025 + RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */); 1.1026 + } 1.1027 + 1.1028 + // Ensure entry for the particular URL, if not read/only 1.1029 + if (!entryExists && (aCreateIfNotExist || aReplace)) { 1.1030 + // Entry is not in the hashtable or has just been truncated... 1.1031 + entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk); 1.1032 + entries->Put(entryKey, entry); 1.1033 + LOG((" new entry %p for %s", entry.get(), entryKey.get())); 1.1034 + } 1.1035 + 1.1036 + if (entry) { 1.1037 + // Here, if this entry was not for a long time referenced by any consumer, 1.1038 + // gets again first 'handles count' reference. 1.1039 + handle = entry->NewHandle(); 1.1040 + } 1.1041 + } 1.1042 + 1.1043 + handle.forget(aResult); 1.1044 + return NS_OK; 1.1045 +} 1.1046 + 1.1047 +namespace { // anon 1.1048 + 1.1049 +class CacheEntryDoomByKeyCallback : public CacheFileIOListener 1.1050 +{ 1.1051 +public: 1.1052 + NS_DECL_THREADSAFE_ISUPPORTS 1.1053 + 1.1054 + CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback) 1.1055 + : mCallback(aCallback) { } 1.1056 + virtual ~CacheEntryDoomByKeyCallback(); 1.1057 + 1.1058 +private: 1.1059 + NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; } 1.1060 + NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) { return NS_OK; } 1.1061 + NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { return NS_OK; } 1.1062 + NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult); 1.1063 + NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; } 1.1064 + NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) { return NS_OK; } 1.1065 + 1.1066 + nsCOMPtr<nsICacheEntryDoomCallback> mCallback; 1.1067 +}; 1.1068 + 1.1069 +CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback() 1.1070 +{ 1.1071 + if (mCallback) 1.1072 + ProxyReleaseMainThread(mCallback); 1.1073 +} 1.1074 + 1.1075 +NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle, 1.1076 + nsresult aResult) 1.1077 +{ 1.1078 + if (!mCallback) 1.1079 + return NS_OK; 1.1080 + 1.1081 + mCallback->OnCacheEntryDoomed(aResult); 1.1082 + return NS_OK; 1.1083 +} 1.1084 + 1.1085 +NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener); 1.1086 + 1.1087 +} // anon 1.1088 + 1.1089 +nsresult 1.1090 +CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage, 1.1091 + nsIURI *aURI, 1.1092 + const nsACString & aIdExtension, 1.1093 + nsICacheEntryDoomCallback* aCallback) 1.1094 +{ 1.1095 + LOG(("CacheStorageService::DoomStorageEntry")); 1.1096 + 1.1097 + NS_ENSURE_ARG(aStorage); 1.1098 + NS_ENSURE_ARG(aURI); 1.1099 + 1.1100 + nsAutoCString contextKey; 1.1101 + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); 1.1102 + 1.1103 + nsAutoCString entryKey; 1.1104 + nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); 1.1105 + NS_ENSURE_SUCCESS(rv, rv); 1.1106 + 1.1107 + nsRefPtr<CacheEntry> entry; 1.1108 + { 1.1109 + mozilla::MutexAutoLock lock(mLock); 1.1110 + 1.1111 + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); 1.1112 + 1.1113 + CacheEntryTable* entries; 1.1114 + if (sGlobalEntryTables->Get(contextKey, &entries)) { 1.1115 + if (entries->Get(entryKey, getter_AddRefs(entry))) { 1.1116 + if (aStorage->WriteToDisk() || !entry->IsUsingDiskLocked()) { 1.1117 + // When evicting from disk storage, purge 1.1118 + // When evicting from memory storage and the entry is memory-only, purge 1.1119 + LOG((" purging entry %p for %s [storage use disk=%d, entry use disk=%d]", 1.1120 + entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked())); 1.1121 + entries->Remove(entryKey); 1.1122 + } 1.1123 + else { 1.1124 + // Otherwise, leave it 1.1125 + LOG((" leaving entry %p for %s [storage use disk=%d, entry use disk=%d]", 1.1126 + entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked())); 1.1127 + entry = nullptr; 1.1128 + } 1.1129 + } 1.1130 + } 1.1131 + } 1.1132 + 1.1133 + if (entry) { 1.1134 + LOG((" dooming entry %p for %s", entry.get(), entryKey.get())); 1.1135 + return entry->AsyncDoom(aCallback); 1.1136 + } 1.1137 + 1.1138 + LOG((" no entry loaded for %s", entryKey.get())); 1.1139 + 1.1140 + if (aStorage->WriteToDisk()) { 1.1141 + nsAutoCString contextKey; 1.1142 + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); 1.1143 + 1.1144 + rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey); 1.1145 + NS_ENSURE_SUCCESS(rv, rv); 1.1146 + 1.1147 + LOG((" dooming file only for %s", entryKey.get())); 1.1148 + 1.1149 + nsRefPtr<CacheEntryDoomByKeyCallback> callback( 1.1150 + new CacheEntryDoomByKeyCallback(aCallback)); 1.1151 + rv = CacheFileIOManager::DoomFileByKey(entryKey, callback); 1.1152 + NS_ENSURE_SUCCESS(rv, rv); 1.1153 + 1.1154 + return NS_OK; 1.1155 + } 1.1156 + 1.1157 + if (aCallback) 1.1158 + aCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE); 1.1159 + 1.1160 + return NS_OK; 1.1161 +} 1.1162 + 1.1163 +nsresult 1.1164 +CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage, 1.1165 + nsICacheEntryDoomCallback* aCallback) 1.1166 +{ 1.1167 + LOG(("CacheStorageService::DoomStorageEntries")); 1.1168 + 1.1169 + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); 1.1170 + NS_ENSURE_ARG(aStorage); 1.1171 + 1.1172 + nsAutoCString contextKey; 1.1173 + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); 1.1174 + 1.1175 + mozilla::MutexAutoLock lock(mLock); 1.1176 + 1.1177 + return DoomStorageEntries(contextKey, aStorage->LoadInfo(), 1.1178 + aStorage->WriteToDisk(), aCallback); 1.1179 +} 1.1180 + 1.1181 +nsresult 1.1182 +CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, 1.1183 + nsILoadContextInfo* aContext, 1.1184 + bool aDiskStorage, 1.1185 + nsICacheEntryDoomCallback* aCallback) 1.1186 +{ 1.1187 + mLock.AssertCurrentThreadOwns(); 1.1188 + 1.1189 + NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED); 1.1190 + 1.1191 + nsAutoCString memoryStorageID(aContextKey); 1.1192 + AppendMemoryStorageID(memoryStorageID); 1.1193 + 1.1194 + if (aDiskStorage) { 1.1195 + LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading())); 1.1196 + 1.1197 + // Just remove all entries, CacheFileIOManager will take care of the files. 1.1198 + sGlobalEntryTables->Remove(aContextKey); 1.1199 + sGlobalEntryTables->Remove(memoryStorageID); 1.1200 + 1.1201 + if (aContext && !aContext->IsPrivate()) { 1.1202 + LOG((" dooming disk entries")); 1.1203 + CacheFileIOManager::EvictByContext(aContext); 1.1204 + } 1.1205 + } else { 1.1206 + LOG((" dooming memory-only storage of %s", aContextKey.BeginReading())); 1.1207 + 1.1208 + class MemoryEntriesRemoval { 1.1209 + public: 1.1210 + static PLDHashOperator EvictEntry(const nsACString& aKey, 1.1211 + CacheEntry* aEntry, 1.1212 + void* aClosure) 1.1213 + { 1.1214 + CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure); 1.1215 + nsCString key(aKey); 1.1216 + RemoveExactEntry(entries, key, aEntry, false); 1.1217 + return PL_DHASH_NEXT; 1.1218 + } 1.1219 + }; 1.1220 + 1.1221 + // Remove the memory entries table from the global tables. 1.1222 + // Since we store memory entries also in the disk entries table 1.1223 + // we need to remove the memory entries from the disk table one 1.1224 + // by one manually. 1.1225 + nsAutoPtr<CacheEntryTable> memoryEntries; 1.1226 + sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries); 1.1227 + 1.1228 + CacheEntryTable* entries; 1.1229 + sGlobalEntryTables->Get(aContextKey, &entries); 1.1230 + if (memoryEntries && entries) 1.1231 + memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries); 1.1232 + } 1.1233 + 1.1234 + // An artificial callback. This is a candidate for removal tho. In the new 1.1235 + // cache any 'doom' or 'evict' function ensures that the entry or entries 1.1236 + // being doomed is/are not accessible after the function returns. So there is 1.1237 + // probably no need for a callback - has no meaning. But for compatibility 1.1238 + // with the old cache that is still in the tree we keep the API similar to be 1.1239 + // able to make tests as well as other consumers work for now. 1.1240 + class Callback : public nsRunnable 1.1241 + { 1.1242 + public: 1.1243 + Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { } 1.1244 + NS_IMETHODIMP Run() 1.1245 + { 1.1246 + mCallback->OnCacheEntryDoomed(NS_OK); 1.1247 + return NS_OK; 1.1248 + } 1.1249 + nsCOMPtr<nsICacheEntryDoomCallback> mCallback; 1.1250 + }; 1.1251 + 1.1252 + if (aCallback) { 1.1253 + nsRefPtr<nsRunnable> callback = new Callback(aCallback); 1.1254 + return NS_DispatchToCurrentThread(callback); 1.1255 + } 1.1256 + 1.1257 + return NS_OK; 1.1258 +} 1.1259 + 1.1260 +nsresult 1.1261 +CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage, 1.1262 + bool aVisitEntries, 1.1263 + nsICacheStorageVisitor* aVisitor) 1.1264 +{ 1.1265 + LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries)); 1.1266 + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); 1.1267 + 1.1268 + NS_ENSURE_ARG(aStorage); 1.1269 + 1.1270 + nsAutoCString contextKey; 1.1271 + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); 1.1272 + 1.1273 + nsRefPtr<WalkRunnable> event = new WalkRunnable( 1.1274 + contextKey, aVisitEntries, aStorage->WriteToDisk(), aVisitor); 1.1275 + return Dispatch(event); 1.1276 +} 1.1277 + 1.1278 +void 1.1279 +CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo, 1.1280 + const nsACString & aIdExtension, 1.1281 + const nsACString & aURISpec) 1.1282 +{ 1.1283 + nsAutoCString contextKey; 1.1284 + CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey); 1.1285 + 1.1286 + nsAutoCString entryKey; 1.1287 + CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey); 1.1288 + 1.1289 + mozilla::MutexAutoLock lock(mLock); 1.1290 + 1.1291 + if (mShutdown) 1.1292 + return; 1.1293 + 1.1294 + CacheEntryTable* entries; 1.1295 + if (!sGlobalEntryTables->Get(contextKey, &entries)) 1.1296 + return; 1.1297 + 1.1298 + nsRefPtr<CacheEntry> entry; 1.1299 + if (!entries->Get(entryKey, getter_AddRefs(entry))) 1.1300 + return; 1.1301 + 1.1302 + if (!entry->IsFileDoomed()) 1.1303 + return; 1.1304 + 1.1305 + if (entry->IsReferenced()) 1.1306 + return; 1.1307 + 1.1308 + // Need to remove under the lock to avoid possible race leading 1.1309 + // to duplication of the entry per its key. 1.1310 + RemoveExactEntry(entries, entryKey, entry, false); 1.1311 + entry->DoomAlreadyRemoved(); 1.1312 +} 1.1313 + 1.1314 +// nsIMemoryReporter 1.1315 + 1.1316 +size_t 1.1317 +CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.1318 +{ 1.1319 + CacheStorageService::Self()->Lock().AssertCurrentThreadOwns(); 1.1320 + 1.1321 + size_t n = 0; 1.1322 + // The elemets are referenced by sGlobalEntryTables and are reported from there 1.1323 + n += Pool(true).mFrecencyArray.SizeOfExcludingThis(mallocSizeOf); 1.1324 + n += Pool(true).mExpirationArray.SizeOfExcludingThis(mallocSizeOf); 1.1325 + n += Pool(false).mFrecencyArray.SizeOfExcludingThis(mallocSizeOf); 1.1326 + n += Pool(false).mExpirationArray.SizeOfExcludingThis(mallocSizeOf); 1.1327 + // Entries reported manually in CacheStorageService::CollectReports callback 1.1328 + if (sGlobalEntryTables) { 1.1329 + n += sGlobalEntryTables->SizeOfIncludingThis(nullptr, mallocSizeOf); 1.1330 + } 1.1331 + 1.1332 + return n; 1.1333 +} 1.1334 + 1.1335 +size_t 1.1336 +CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.1337 +{ 1.1338 + return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); 1.1339 +} 1.1340 + 1.1341 +namespace { // anon 1.1342 + 1.1343 +class ReportStorageMemoryData 1.1344 +{ 1.1345 +public: 1.1346 + nsIMemoryReporterCallback *mHandleReport; 1.1347 + nsISupports *mData; 1.1348 +}; 1.1349 + 1.1350 +size_t CollectEntryMemory(nsACString const & aKey, 1.1351 + nsRefPtr<mozilla::net::CacheEntry> const & aEntry, 1.1352 + mozilla::MallocSizeOf mallocSizeOf, 1.1353 + void * aClosure) 1.1354 +{ 1.1355 + CacheStorageService::Self()->Lock().AssertCurrentThreadOwns(); 1.1356 + 1.1357 + CacheEntryTable* aTable = static_cast<CacheEntryTable*>(aClosure); 1.1358 + 1.1359 + size_t n = 0; 1.1360 + n += aKey.SizeOfExcludingThisIfUnshared(mallocSizeOf); 1.1361 + 1.1362 + // Bypass memory-only entries, those will be reported when iterating 1.1363 + // the memory only table. Memory-only entries are stored in both ALL_ENTRIES 1.1364 + // and MEMORY_ONLY hashtables. 1.1365 + if (aTable->Type() == CacheEntryTable::MEMORY_ONLY || aEntry->IsUsingDiskLocked()) 1.1366 + n += aEntry->SizeOfIncludingThis(mallocSizeOf); 1.1367 + 1.1368 + return n; 1.1369 +} 1.1370 + 1.1371 +PLDHashOperator ReportStorageMemory(const nsACString& aKey, 1.1372 + CacheEntryTable* aTable, 1.1373 + void* aClosure) 1.1374 +{ 1.1375 + CacheStorageService::Self()->Lock().AssertCurrentThreadOwns(); 1.1376 + 1.1377 + size_t size = aTable->SizeOfIncludingThis(&CollectEntryMemory, 1.1378 + CacheStorageService::MallocSizeOf, 1.1379 + aTable); 1.1380 + 1.1381 + ReportStorageMemoryData& data = *static_cast<ReportStorageMemoryData*>(aClosure); 1.1382 + data.mHandleReport->Callback( 1.1383 + EmptyCString(), 1.1384 + nsPrintfCString("explicit/network/cache2/%s-storage(%s)", 1.1385 + aTable->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk", 1.1386 + aKey.BeginReading()), 1.1387 + nsIMemoryReporter::KIND_HEAP, 1.1388 + nsIMemoryReporter::UNITS_BYTES, 1.1389 + size, 1.1390 + NS_LITERAL_CSTRING("Memory used by the cache storage."), 1.1391 + data.mData); 1.1392 + 1.1393 + return PL_DHASH_NEXT; 1.1394 +} 1.1395 + 1.1396 +} // anon 1.1397 + 1.1398 +NS_IMETHODIMP 1.1399 +CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport, nsISupports* aData) 1.1400 +{ 1.1401 + nsresult rv; 1.1402 + 1.1403 + rv = MOZ_COLLECT_REPORT( 1.1404 + "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES, 1.1405 + CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf), 1.1406 + "Memory used by the cache IO manager."); 1.1407 + if (NS_WARN_IF(NS_FAILED(rv))) 1.1408 + return rv; 1.1409 + 1.1410 + rv = MOZ_COLLECT_REPORT( 1.1411 + "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES, 1.1412 + CacheIndex::SizeOfIncludingThis(MallocSizeOf), 1.1413 + "Memory used by the cache index."); 1.1414 + if (NS_WARN_IF(NS_FAILED(rv))) 1.1415 + return rv; 1.1416 + 1.1417 + MutexAutoLock lock(mLock); 1.1418 + 1.1419 + // Report the service instance, this doesn't report entries, done lower 1.1420 + rv = MOZ_COLLECT_REPORT( 1.1421 + "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES, 1.1422 + SizeOfIncludingThis(MallocSizeOf), 1.1423 + "Memory used by the cache storage service."); 1.1424 + if (NS_WARN_IF(NS_FAILED(rv))) 1.1425 + return rv; 1.1426 + 1.1427 + // Report all entries, each storage separately (by the context key) 1.1428 + // 1.1429 + // References are: 1.1430 + // sGlobalEntryTables to N CacheEntryTable 1.1431 + // CacheEntryTable to N CacheEntry 1.1432 + // CacheEntry to 1 CacheFile 1.1433 + // CacheFile to 1.1434 + // N CacheFileChunk (keeping the actual data) 1.1435 + // 1 CacheFileMetadata (keeping http headers etc.) 1.1436 + // 1 CacheFileOutputStream 1.1437 + // N CacheFileInputStream 1.1438 + ReportStorageMemoryData data; 1.1439 + data.mHandleReport = aHandleReport; 1.1440 + data.mData = aData; 1.1441 + sGlobalEntryTables->EnumerateRead(&ReportStorageMemory, &data); 1.1442 + 1.1443 + return NS_OK; 1.1444 +} 1.1445 + 1.1446 +} // net 1.1447 +} // mozilla