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