Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "CacheLog.h"
8 : #include "CacheStorageService.h"
9 : #include "CacheFileIOManager.h"
10 : #include "CacheObserver.h"
11 : #include "CacheIndex.h"
12 : #include "CacheIndexIterator.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 "nsICacheStorageVisitor.h"
23 : #include "nsIObserverService.h"
24 : #include "nsIFile.h"
25 : #include "nsIURI.h"
26 : #include "nsCOMPtr.h"
27 : #include "nsAutoPtr.h"
28 : #include "nsNetCID.h"
29 : #include "nsNetUtil.h"
30 : #include "nsServiceManagerUtils.h"
31 : #include "nsWeakReference.h"
32 : #include "mozilla/TimeStamp.h"
33 : #include "mozilla/DebugOnly.h"
34 : #include "mozilla/Services.h"
35 : #include "mozilla/IntegerPrintfMacros.h"
36 : #include "mozilla/SizePrintfMacros.h"
37 :
38 : namespace mozilla {
39 : namespace net {
40 :
41 : namespace {
42 :
43 5 : void AppendMemoryStorageID(nsAutoCString &key)
44 : {
45 5 : key.Append('/');
46 5 : key.Append('M');
47 5 : }
48 :
49 : } // namespace
50 :
51 : // Not defining as static or class member of CacheStorageService since
52 : // it would otherwise need to include CacheEntry.h and that then would
53 : // need to be exported to make nsNetModule.cpp compilable.
54 : typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable>
55 : GlobalEntryTables;
56 :
57 : /**
58 : * Keeps tables of entries. There is one entries table for each distinct load
59 : * context type. The distinction is based on following load context info states:
60 : * <isPrivate|isAnon|appId|inIsolatedMozBrowser> which builds a mapping key.
61 : *
62 : * Thread-safe to access, protected by the service mutex.
63 : */
64 : static GlobalEntryTables* sGlobalEntryTables;
65 :
66 9 : CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags)
67 : : mReportedMemoryConsumption(0)
68 9 : , mFlags(aFlags)
69 : {
70 9 : }
71 :
72 : void
73 38 : CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
74 : {
75 38 : if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) {
76 26 : CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
77 : }
78 38 : }
79 :
80 2 : CacheStorageService::MemoryPool::MemoryPool(EType aType)
81 : : mType(aType)
82 2 : , mMemorySize(0)
83 : {
84 2 : }
85 :
86 0 : CacheStorageService::MemoryPool::~MemoryPool()
87 : {
88 0 : if (mMemorySize != 0) {
89 0 : NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?");
90 : }
91 0 : }
92 :
93 : uint32_t
94 15 : CacheStorageService::MemoryPool::Limit() const
95 : {
96 15 : switch (mType) {
97 : case DISK:
98 15 : return CacheObserver::MetadataMemoryLimit();
99 : case MEMORY:
100 0 : return CacheObserver::MemoryCacheCapacity();
101 : }
102 :
103 0 : MOZ_CRASH("Bad pool type");
104 : return 0;
105 : }
106 :
107 225 : NS_IMPL_ISUPPORTS(CacheStorageService,
108 : nsICacheStorageService,
109 : nsIMemoryReporter,
110 : nsITimerCallback,
111 : nsICacheTesting)
112 :
113 : CacheStorageService* CacheStorageService::sSelf = nullptr;
114 :
115 1 : CacheStorageService::CacheStorageService()
116 : : mLock("CacheStorageService.mLock")
117 : , mForcedValidEntriesLock("CacheStorageService.mForcedValidEntriesLock")
118 : , mShutdown(false)
119 : , mDiskPool(MemoryPool::DISK)
120 1 : , mMemoryPool(MemoryPool::MEMORY)
121 : {
122 1 : CacheFileIOManager::Init();
123 :
124 1 : MOZ_ASSERT(!sSelf);
125 :
126 1 : sSelf = this;
127 1 : sGlobalEntryTables = new GlobalEntryTables();
128 :
129 1 : RegisterStrongMemoryReporter(this);
130 1 : }
131 :
132 0 : CacheStorageService::~CacheStorageService()
133 : {
134 0 : LOG(("CacheStorageService::~CacheStorageService"));
135 0 : sSelf = nullptr;
136 0 : }
137 :
138 0 : void CacheStorageService::Shutdown()
139 : {
140 0 : mozilla::MutexAutoLock lock(mLock);
141 :
142 0 : if (mShutdown)
143 0 : return;
144 :
145 0 : LOG(("CacheStorageService::Shutdown - start"));
146 :
147 0 : mShutdown = true;
148 :
149 : nsCOMPtr<nsIRunnable> event =
150 0 : NewRunnableMethod("net::CacheStorageService::ShutdownBackground",
151 : this,
152 0 : &CacheStorageService::ShutdownBackground);
153 0 : Dispatch(event);
154 :
155 : #ifdef NS_FREE_PERMANENT_DATA
156 0 : sGlobalEntryTables->Clear();
157 0 : delete sGlobalEntryTables;
158 : #endif
159 0 : sGlobalEntryTables = nullptr;
160 :
161 0 : LOG(("CacheStorageService::Shutdown - done"));
162 : }
163 :
164 0 : void CacheStorageService::ShutdownBackground()
165 : {
166 0 : LOG(("CacheStorageService::ShutdownBackground - start"));
167 :
168 0 : MOZ_ASSERT(IsOnManagementThread());
169 :
170 : {
171 0 : mozilla::MutexAutoLock lock(mLock);
172 :
173 : // Cancel purge timer to avoid leaking.
174 0 : if (mPurgeTimer) {
175 0 : LOG((" freeing the timer"));
176 0 : mPurgeTimer->Cancel();
177 : }
178 : }
179 :
180 : #ifdef NS_FREE_PERMANENT_DATA
181 0 : Pool(false).mFrecencyArray.Clear();
182 0 : Pool(false).mExpirationArray.Clear();
183 0 : Pool(true).mFrecencyArray.Clear();
184 0 : Pool(true).mExpirationArray.Clear();
185 : #endif
186 :
187 0 : LOG(("CacheStorageService::ShutdownBackground - done"));
188 0 : }
189 :
190 : // Internal management methods
191 :
192 : namespace {
193 :
194 : // WalkCacheRunnable
195 : // Base class for particular storage entries visiting
196 : class WalkCacheRunnable : public Runnable
197 : , public CacheStorageService::EntryInfoCallback
198 : {
199 : protected:
200 0 : WalkCacheRunnable(nsICacheStorageVisitor* aVisitor, bool aVisitEntries)
201 0 : : Runnable("net::WalkCacheRunnable")
202 : , mService(CacheStorageService::Self())
203 : , mCallback(aVisitor)
204 : , mSize(0)
205 : , mNotifyStorage(true)
206 : , mVisitEntries(aVisitEntries)
207 0 : , mCancel(false)
208 : {
209 0 : MOZ_ASSERT(NS_IsMainThread());
210 0 : }
211 :
212 0 : virtual ~WalkCacheRunnable()
213 0 : {
214 0 : if (mCallback) {
215 0 : ProxyReleaseMainThread(
216 0 : "WalkCacheRunnable::mCallback", mCallback);
217 : }
218 0 : }
219 :
220 : RefPtr<CacheStorageService> mService;
221 : nsCOMPtr<nsICacheStorageVisitor> mCallback;
222 :
223 : uint64_t mSize;
224 :
225 : bool mNotifyStorage : 1;
226 : bool mVisitEntries : 1;
227 :
228 : Atomic<bool> mCancel;
229 : };
230 :
231 : // WalkMemoryCacheRunnable
232 : // Responsible to visit memory storage and walk
233 : // all entries on it asynchronously.
234 : class WalkMemoryCacheRunnable : public WalkCacheRunnable
235 : {
236 : public:
237 0 : WalkMemoryCacheRunnable(nsILoadContextInfo *aLoadInfo,
238 : bool aVisitEntries,
239 : nsICacheStorageVisitor* aVisitor)
240 0 : : WalkCacheRunnable(aVisitor, aVisitEntries)
241 : {
242 0 : CacheFileUtils::AppendKeyPrefix(aLoadInfo, mContextKey);
243 0 : MOZ_ASSERT(NS_IsMainThread());
244 0 : }
245 :
246 0 : nsresult Walk()
247 : {
248 0 : return mService->Dispatch(this);
249 : }
250 :
251 : private:
252 0 : NS_IMETHOD Run() override
253 : {
254 0 : if (CacheStorageService::IsOnManagementThread()) {
255 0 : LOG(("WalkMemoryCacheRunnable::Run - collecting [this=%p]", this));
256 : // First, walk, count and grab all entries from the storage
257 :
258 0 : mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock());
259 :
260 0 : if (!CacheStorageService::IsRunning())
261 0 : return NS_ERROR_NOT_INITIALIZED;
262 :
263 : CacheEntryTable* entries;
264 0 : if (sGlobalEntryTables->Get(mContextKey, &entries)) {
265 0 : for (auto iter = entries->Iter(); !iter.Done(); iter.Next()) {
266 0 : CacheEntry* entry = iter.UserData();
267 :
268 : // Ignore disk entries
269 0 : if (entry->IsUsingDisk()) {
270 0 : continue;
271 : }
272 :
273 0 : mSize += entry->GetMetadataMemoryConsumption();
274 :
275 : int64_t size;
276 0 : if (NS_SUCCEEDED(entry->GetDataSize(&size))) {
277 0 : mSize += size;
278 : }
279 0 : mEntryArray.AppendElement(entry);
280 : }
281 : }
282 :
283 : // Next, we dispatch to the main thread
284 0 : } else if (NS_IsMainThread()) {
285 0 : LOG(("WalkMemoryCacheRunnable::Run - notifying [this=%p]", this));
286 :
287 0 : if (mNotifyStorage) {
288 0 : LOG((" storage"));
289 :
290 : // Second, notify overall storage info
291 0 : mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize,
292 0 : CacheObserver::MemoryCacheCapacity(), nullptr);
293 0 : if (!mVisitEntries)
294 0 : return NS_OK; // done
295 :
296 0 : mNotifyStorage = false;
297 :
298 : } else {
299 0 : LOG((" entry [left=%" PRIuSIZE ", canceled=%d]", mEntryArray.Length(), (bool)mCancel));
300 :
301 : // Third, notify each entry until depleted or canceled
302 0 : if (!mEntryArray.Length() || mCancel) {
303 0 : mCallback->OnCacheEntryVisitCompleted();
304 0 : return NS_OK; // done
305 : }
306 :
307 : // Grab the next entry
308 0 : RefPtr<CacheEntry> entry = mEntryArray[0];
309 0 : mEntryArray.RemoveElementAt(0);
310 :
311 : // Invokes this->OnEntryInfo, that calls the callback with all
312 : // information of the entry.
313 0 : CacheStorageService::GetCacheEntryInfo(entry, this);
314 : }
315 : } else {
316 0 : MOZ_CRASH("Bad thread");
317 : return NS_ERROR_FAILURE;
318 : }
319 :
320 0 : NS_DispatchToMainThread(this);
321 0 : return NS_OK;
322 : }
323 :
324 0 : virtual ~WalkMemoryCacheRunnable()
325 0 : {
326 0 : if (mCallback)
327 0 : ProxyReleaseMainThread(
328 0 : "WalkMemoryCacheRunnable::mCallback", mCallback);
329 0 : }
330 :
331 0 : virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
332 : int64_t aDataSize, int32_t aFetchCount,
333 : uint32_t aLastModifiedTime, uint32_t aExpirationTime,
334 : bool aPinned, nsILoadContextInfo* aInfo) override
335 : {
336 : nsresult rv;
337 :
338 0 : nsCOMPtr<nsIURI> uri;
339 0 : rv = NS_NewURI(getter_AddRefs(uri), aURISpec);
340 0 : if (NS_FAILED(rv)) {
341 0 : return;
342 : }
343 :
344 0 : rv = mCallback->OnCacheEntryInfo(uri, aIdEnhance, aDataSize, aFetchCount,
345 : aLastModifiedTime, aExpirationTime,
346 0 : aPinned, aInfo);
347 0 : if (NS_FAILED(rv)) {
348 0 : LOG((" callback failed, canceling the walk"));
349 0 : mCancel = true;
350 : }
351 : }
352 :
353 : private:
354 : nsCString mContextKey;
355 : nsTArray<RefPtr<CacheEntry> > mEntryArray;
356 : };
357 :
358 : // WalkDiskCacheRunnable
359 : // Using the cache index information to get the list of files per context.
360 0 : class WalkDiskCacheRunnable : public WalkCacheRunnable
361 : {
362 : public:
363 0 : WalkDiskCacheRunnable(nsILoadContextInfo *aLoadInfo,
364 : bool aVisitEntries,
365 : nsICacheStorageVisitor* aVisitor)
366 0 : : WalkCacheRunnable(aVisitor, aVisitEntries)
367 : , mLoadInfo(aLoadInfo)
368 0 : , mPass(COLLECT_STATS)
369 : {
370 0 : }
371 :
372 0 : nsresult Walk()
373 : {
374 : // TODO, bug 998693
375 : // Initial index build should be forced here so that about:cache soon
376 : // after startup gives some meaningfull results.
377 :
378 : // Dispatch to the INDEX level in hope that very recent cache entries
379 : // information gets to the index list before we grab the index iterator
380 : // for the first time. This tries to avoid miss of entries that has
381 : // been created right before the visit is required.
382 0 : RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
383 0 : NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
384 :
385 0 : return thread->Dispatch(this, CacheIOThread::INDEX);
386 : }
387 :
388 : private:
389 : // Invokes OnCacheEntryInfo callback for each single found entry.
390 : // There is one instance of this class per one entry.
391 0 : class OnCacheEntryInfoRunnable : public Runnable
392 : {
393 : public:
394 0 : explicit OnCacheEntryInfoRunnable(WalkDiskCacheRunnable* aWalker)
395 0 : : Runnable("net::WalkDiskCacheRunnable::OnCacheEntryInfoRunnable")
396 0 : , mWalker(aWalker)
397 : {
398 0 : }
399 :
400 0 : NS_IMETHOD Run() override
401 : {
402 0 : MOZ_ASSERT(NS_IsMainThread());
403 :
404 : nsresult rv;
405 :
406 0 : nsCOMPtr<nsIURI> uri;
407 0 : rv = NS_NewURI(getter_AddRefs(uri), mURISpec);
408 0 : if (NS_FAILED(rv)) {
409 0 : return NS_OK;
410 : }
411 :
412 0 : rv = mWalker->mCallback->OnCacheEntryInfo(
413 : uri, mIdEnhance, mDataSize, mFetchCount,
414 0 : mLastModifiedTime, mExpirationTime, mPinned, mInfo);
415 0 : if (NS_FAILED(rv)) {
416 0 : mWalker->mCancel = true;
417 : }
418 :
419 0 : return NS_OK;
420 : }
421 :
422 : RefPtr<WalkDiskCacheRunnable> mWalker;
423 :
424 : nsCString mURISpec;
425 : nsCString mIdEnhance;
426 : int64_t mDataSize;
427 : int32_t mFetchCount;
428 : uint32_t mLastModifiedTime;
429 : uint32_t mExpirationTime;
430 : bool mPinned;
431 : nsCOMPtr<nsILoadContextInfo> mInfo;
432 : };
433 :
434 0 : NS_IMETHOD Run() override
435 : {
436 : // The main loop
437 : nsresult rv;
438 :
439 0 : if (CacheStorageService::IsOnManagementThread()) {
440 0 : switch (mPass) {
441 : case COLLECT_STATS:
442 : // Get quickly the cache stats.
443 : uint32_t size;
444 0 : rv = CacheIndex::GetCacheStats(mLoadInfo, &size, &mCount);
445 0 : if (NS_FAILED(rv)) {
446 0 : if (mVisitEntries) {
447 : // both onStorageInfo and onCompleted are expected
448 0 : NS_DispatchToMainThread(this);
449 : }
450 0 : return NS_DispatchToMainThread(this);
451 : }
452 :
453 0 : mSize = static_cast<uint64_t>(size) << 10;
454 :
455 : // Invoke onCacheStorageInfo with valid information.
456 0 : NS_DispatchToMainThread(this);
457 :
458 0 : if (!mVisitEntries) {
459 0 : return NS_OK; // done
460 : }
461 :
462 0 : mPass = ITERATE_METADATA;
463 : MOZ_FALLTHROUGH;
464 :
465 : case ITERATE_METADATA:
466 : // Now grab the context iterator.
467 0 : if (!mIter) {
468 0 : rv = CacheIndex::GetIterator(mLoadInfo, true, getter_AddRefs(mIter));
469 0 : if (NS_FAILED(rv)) {
470 : // Invoke onCacheEntryVisitCompleted now
471 0 : return NS_DispatchToMainThread(this);
472 : }
473 : }
474 :
475 0 : while (!mCancel && !CacheObserver::ShuttingDown()) {
476 0 : if (CacheIOThread::YieldAndRerun())
477 0 : return NS_OK;
478 :
479 : SHA1Sum::Hash hash;
480 0 : rv = mIter->GetNextHash(&hash);
481 0 : if (NS_FAILED(rv))
482 0 : break; // done (or error?)
483 :
484 : // This synchronously invokes OnEntryInfo on this class where we
485 : // redispatch to the main thread for the consumer callback.
486 0 : CacheFileIOManager::GetEntryInfo(&hash, this);
487 : }
488 :
489 : // Invoke onCacheEntryVisitCompleted on the main thread
490 0 : NS_DispatchToMainThread(this);
491 : }
492 0 : } else if (NS_IsMainThread()) {
493 0 : if (mNotifyStorage) {
494 0 : nsCOMPtr<nsIFile> dir;
495 0 : CacheFileIOManager::GetCacheDirectory(getter_AddRefs(dir));
496 0 : mCallback->OnCacheStorageInfo(mCount, mSize, CacheObserver::DiskCacheCapacity(), dir);
497 0 : mNotifyStorage = false;
498 : } else {
499 0 : mCallback->OnCacheEntryVisitCompleted();
500 : }
501 : } else {
502 0 : MOZ_CRASH("Bad thread");
503 : return NS_ERROR_FAILURE;
504 : }
505 :
506 0 : return NS_OK;
507 : }
508 :
509 0 : virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
510 : int64_t aDataSize, int32_t aFetchCount,
511 : uint32_t aLastModifiedTime, uint32_t aExpirationTime,
512 : bool aPinned, nsILoadContextInfo* aInfo) override
513 : {
514 : // Called directly from CacheFileIOManager::GetEntryInfo.
515 :
516 : // Invoke onCacheEntryInfo on the main thread for this entry.
517 0 : RefPtr<OnCacheEntryInfoRunnable> info = new OnCacheEntryInfoRunnable(this);
518 0 : info->mURISpec = aURISpec;
519 0 : info->mIdEnhance = aIdEnhance;
520 0 : info->mDataSize = aDataSize;
521 0 : info->mFetchCount = aFetchCount;
522 0 : info->mLastModifiedTime = aLastModifiedTime;
523 0 : info->mExpirationTime = aExpirationTime;
524 0 : info->mPinned = aPinned;
525 0 : info->mInfo = aInfo;
526 :
527 0 : NS_DispatchToMainThread(info);
528 0 : }
529 :
530 : RefPtr<nsILoadContextInfo> mLoadInfo;
531 : enum {
532 : // First, we collect stats for the load context.
533 : COLLECT_STATS,
534 :
535 : // Second, if demanded, we iterate over the entries gethered
536 : // from the iterator and call CacheFileIOManager::GetEntryInfo
537 : // for each found entry.
538 : ITERATE_METADATA,
539 : } mPass;
540 :
541 : RefPtr<CacheIndexIterator> mIter;
542 : uint32_t mCount;
543 : };
544 :
545 : } // namespace
546 :
547 0 : void CacheStorageService::DropPrivateBrowsingEntries()
548 : {
549 0 : mozilla::MutexAutoLock lock(mLock);
550 :
551 0 : if (mShutdown)
552 0 : return;
553 :
554 0 : nsTArray<nsCString> keys;
555 0 : for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
556 0 : const nsACString& key = iter.Key();
557 0 : nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(key);
558 0 : if (info && info->IsPrivate()) {
559 0 : keys.AppendElement(key);
560 : }
561 : }
562 :
563 0 : for (uint32_t i = 0; i < keys.Length(); ++i) {
564 0 : DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
565 : }
566 : }
567 :
568 : namespace {
569 :
570 : class CleaupCacheDirectoriesRunnable : public Runnable
571 : {
572 : public:
573 : NS_DECL_NSIRUNNABLE
574 : static bool Post(uint32_t aVersion, uint32_t aActive);
575 :
576 : private:
577 1 : CleaupCacheDirectoriesRunnable(uint32_t aVersion, uint32_t aActive)
578 1 : : Runnable("net::CleaupCacheDirectoriesRunnable")
579 : , mVersion(aVersion)
580 1 : , mActive(aActive)
581 : {
582 1 : nsCacheService::GetDiskCacheDirectory(getter_AddRefs(mCache1Dir));
583 1 : CacheFileIOManager::GetCacheDirectory(getter_AddRefs(mCache2Dir));
584 : #if defined(MOZ_WIDGET_ANDROID)
585 : CacheFileIOManager::GetProfilelessCacheDirectory(getter_AddRefs(mCache2Profileless));
586 : #endif
587 1 : }
588 :
589 3 : virtual ~CleaupCacheDirectoriesRunnable() {}
590 : uint32_t mVersion, mActive;
591 : nsCOMPtr<nsIFile> mCache1Dir, mCache2Dir;
592 : #if defined(MOZ_WIDGET_ANDROID)
593 : nsCOMPtr<nsIFile> mCache2Profileless;
594 : #endif
595 : };
596 :
597 : // static
598 1 : bool CleaupCacheDirectoriesRunnable::Post(uint32_t aVersion, uint32_t aActive)
599 : {
600 : // CleaupCacheDirectories is called regardless what cache version is set up to use.
601 : // To obtain the cache1 directory we must unfortunately instantiate the old cache
602 : // service despite it may not be used at all... This also initialize nsDeleteDir.
603 2 : nsCOMPtr<nsICacheService> service = do_GetService(NS_CACHESERVICE_CONTRACTID);
604 1 : if (!service)
605 0 : return false;
606 :
607 2 : nsCOMPtr<nsIEventTarget> thread;
608 1 : service->GetCacheIOTarget(getter_AddRefs(thread));
609 1 : if (!thread)
610 0 : return false;
611 :
612 : RefPtr<CleaupCacheDirectoriesRunnable> r =
613 2 : new CleaupCacheDirectoriesRunnable(aVersion, aActive);
614 1 : thread->Dispatch(r, NS_DISPATCH_NORMAL);
615 1 : return true;
616 : }
617 :
618 1 : NS_IMETHODIMP CleaupCacheDirectoriesRunnable::Run()
619 : {
620 1 : MOZ_ASSERT(!NS_IsMainThread());
621 :
622 1 : if (mCache1Dir) {
623 1 : nsDeleteDir::RemoveOldTrashes(mCache1Dir);
624 : }
625 1 : if (mCache2Dir) {
626 1 : nsDeleteDir::RemoveOldTrashes(mCache2Dir);
627 : }
628 : #if defined(MOZ_WIDGET_ANDROID)
629 : if (mCache2Profileless) {
630 : nsDeleteDir::RemoveOldTrashes(mCache2Profileless);
631 : // Always delete the profileless cache on Android
632 : nsDeleteDir::DeleteDir(mCache2Profileless, true, 30000);
633 : }
634 : #endif
635 :
636 : // Delete the non-active version cache data right now
637 1 : if (mVersion == mActive) {
638 0 : return NS_OK;
639 : }
640 :
641 1 : switch (mVersion) {
642 : case 0:
643 1 : if (mCache1Dir) {
644 1 : nsDeleteDir::DeleteDir(mCache1Dir, true, 30000);
645 : }
646 1 : break;
647 : case 1:
648 0 : if (mCache2Dir) {
649 0 : nsDeleteDir::DeleteDir(mCache2Dir, true, 30000);
650 : }
651 0 : break;
652 : }
653 :
654 1 : return NS_OK;
655 : }
656 :
657 : } // namespace
658 :
659 : // static
660 1 : void CacheStorageService::CleaupCacheDirectories(uint32_t aVersion, uint32_t aActive)
661 : {
662 : // Make sure we schedule just once in case CleaupCacheDirectories gets called
663 : // multiple times from some reason.
664 1 : static bool runOnce = CleaupCacheDirectoriesRunnable::Post(aVersion, aActive);
665 1 : if (!runOnce) {
666 0 : NS_WARNING("Could not start cache trashes cleanup");
667 : }
668 1 : }
669 :
670 : // Helper methods
671 :
672 : // static
673 72 : bool CacheStorageService::IsOnManagementThread()
674 : {
675 144 : RefPtr<CacheStorageService> service = Self();
676 72 : if (!service)
677 0 : return false;
678 :
679 144 : nsCOMPtr<nsIEventTarget> target = service->Thread();
680 72 : if (!target)
681 0 : return false;
682 :
683 : bool currentThread;
684 72 : nsresult rv = target->IsOnCurrentThread(¤tThread);
685 72 : return NS_SUCCEEDED(rv) && currentThread;
686 : }
687 :
688 72 : already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const
689 : {
690 72 : return CacheFileIOManager::IOTarget();
691 : }
692 :
693 13 : nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent)
694 : {
695 26 : RefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread();
696 13 : if (!cacheIOThread)
697 0 : return NS_ERROR_NOT_AVAILABLE;
698 :
699 13 : return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT);
700 : }
701 :
702 : // nsICacheStorageService
703 :
704 0 : NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo,
705 : nsICacheStorage * *_retval)
706 : {
707 0 : NS_ENSURE_ARG(aLoadContextInfo);
708 0 : NS_ENSURE_ARG(_retval);
709 :
710 0 : nsCOMPtr<nsICacheStorage> storage;
711 0 : if (CacheObserver::UseNewCache()) {
712 0 : storage = new CacheStorage(aLoadContextInfo, false, false, false, false);
713 : }
714 : else {
715 0 : storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
716 : }
717 :
718 0 : storage.forget(_retval);
719 0 : return NS_OK;
720 : }
721 :
722 10 : NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo,
723 : bool aLookupAppCache,
724 : nsICacheStorage * *_retval)
725 : {
726 10 : NS_ENSURE_ARG(aLoadContextInfo);
727 10 : NS_ENSURE_ARG(_retval);
728 :
729 : // TODO save some heap granularity - cache commonly used storages.
730 :
731 : // When disk cache is disabled, still provide a storage, but just keep stuff
732 : // in memory.
733 10 : bool useDisk = CacheObserver::UseDiskCache();
734 :
735 20 : nsCOMPtr<nsICacheStorage> storage;
736 10 : if (CacheObserver::UseNewCache()) {
737 10 : storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false /* size limit */, false /* don't pin */);
738 : }
739 : else {
740 0 : storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr);
741 : }
742 :
743 10 : storage.forget(_retval);
744 10 : return NS_OK;
745 : }
746 :
747 0 : NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo,
748 : nsICacheStorage * *_retval)
749 : {
750 0 : NS_ENSURE_ARG(aLoadContextInfo);
751 0 : NS_ENSURE_ARG(_retval);
752 :
753 0 : if (!CacheObserver::UseNewCache()) {
754 0 : return NS_ERROR_NOT_IMPLEMENTED;
755 : }
756 :
757 : // When disk cache is disabled don't pretend we cache.
758 0 : if (!CacheObserver::UseDiskCache()) {
759 0 : return NS_ERROR_NOT_AVAILABLE;
760 : }
761 :
762 : nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
763 0 : aLoadContextInfo, true /* use disk */, false /* no appcache */, true /* ignore size checks */, true /* pin */);
764 0 : storage.forget(_retval);
765 0 : return NS_OK;
766 : }
767 :
768 0 : NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
769 : nsIApplicationCache *aApplicationCache,
770 : nsICacheStorage * *_retval)
771 : {
772 0 : NS_ENSURE_ARG(aLoadContextInfo);
773 0 : NS_ENSURE_ARG(_retval);
774 :
775 0 : nsCOMPtr<nsICacheStorage> storage;
776 0 : if (CacheObserver::UseNewCache()) {
777 : // Using classification since cl believes we want to instantiate this method
778 : // having the same name as the desired class...
779 0 : storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache);
780 : }
781 : else {
782 0 : storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
783 : }
784 :
785 0 : storage.forget(_retval);
786 0 : return NS_OK;
787 : }
788 :
789 0 : NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo,
790 : nsICacheStorage * *_retval)
791 : {
792 0 : NS_ENSURE_ARG(aLoadContextInfo);
793 0 : NS_ENSURE_ARG(_retval);
794 :
795 0 : nsCOMPtr<nsICacheStorage> storage;
796 0 : if (CacheObserver::UseNewCache()) {
797 0 : storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */, false /* no pinning */);
798 : }
799 : else {
800 0 : storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
801 : }
802 :
803 0 : storage.forget(_retval);
804 0 : return NS_OK;
805 : }
806 :
807 0 : NS_IMETHODIMP CacheStorageService::Clear()
808 : {
809 : nsresult rv;
810 :
811 0 : if (CacheObserver::UseNewCache()) {
812 : // Tell the index to block notification to AsyncGetDiskConsumption.
813 : // Will be allowed again from CacheFileContextEvictor::EvictEntries()
814 : // when all the context have been removed from disk.
815 0 : CacheIndex::OnAsyncEviction(true);
816 :
817 : {
818 0 : mozilla::MutexAutoLock lock(mLock);
819 :
820 : {
821 0 : mozilla::MutexAutoLock forcedValidEntriesLock(mForcedValidEntriesLock);
822 0 : mForcedValidEntries.Clear();
823 : }
824 :
825 0 : NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
826 :
827 0 : nsTArray<nsCString> keys;
828 0 : for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
829 0 : keys.AppendElement(iter.Key());
830 : }
831 :
832 0 : for (uint32_t i = 0; i < keys.Length(); ++i) {
833 0 : DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
834 : }
835 :
836 : // Passing null as a load info means to evict all contexts.
837 : // EvictByContext() respects the entry pinning. EvictAll() does not.
838 0 : rv = CacheFileIOManager::EvictByContext(nullptr, false);
839 0 : NS_ENSURE_SUCCESS(rv, rv);
840 : }
841 : } else {
842 : nsCOMPtr<nsICacheService> serv =
843 0 : do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
844 0 : NS_ENSURE_SUCCESS(rv, rv);
845 :
846 0 : rv = serv->EvictEntries(nsICache::STORE_ANYWHERE);
847 0 : NS_ENSURE_SUCCESS(rv, rv);
848 : }
849 :
850 0 : return NS_OK;
851 : }
852 :
853 0 : NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
854 : {
855 : uint32_t what;
856 :
857 0 : switch (aWhat) {
858 : case PURGE_DISK_DATA_ONLY:
859 0 : what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED;
860 0 : break;
861 :
862 : case PURGE_DISK_ALL:
863 0 : what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED;
864 0 : break;
865 :
866 : case PURGE_EVERYTHING:
867 0 : what = CacheEntry::PURGE_WHOLE;
868 0 : break;
869 :
870 : default:
871 0 : return NS_ERROR_INVALID_ARG;
872 : }
873 :
874 : nsCOMPtr<nsIRunnable> event =
875 0 : new PurgeFromMemoryRunnable(this, what);
876 :
877 0 : return Dispatch(event);
878 : }
879 :
880 0 : NS_IMETHODIMP CacheStorageService::PurgeFromMemoryRunnable::Run()
881 : {
882 0 : if (NS_IsMainThread()) {
883 : nsCOMPtr<nsIObserverService> observerService =
884 0 : mozilla::services::GetObserverService();
885 0 : if (observerService) {
886 0 : observerService->NotifyObservers(nullptr, "cacheservice:purge-memory-pools", nullptr);
887 : }
888 :
889 0 : return NS_OK;
890 : }
891 :
892 0 : if (mService) {
893 : // TODO not all flags apply to both pools
894 0 : mService->Pool(true).PurgeAll(mWhat);
895 0 : mService->Pool(false).PurgeAll(mWhat);
896 0 : mService = nullptr;
897 : }
898 :
899 0 : NS_DispatchToMainThread(this);
900 0 : return NS_OK;
901 : }
902 :
903 0 : NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
904 : nsICacheStorageConsumptionObserver* aObserver)
905 : {
906 0 : NS_ENSURE_ARG(aObserver);
907 :
908 : nsresult rv;
909 :
910 0 : if (CacheObserver::UseNewCache()) {
911 0 : rv = CacheIndex::AsyncGetDiskConsumption(aObserver);
912 0 : NS_ENSURE_SUCCESS(rv, rv);
913 : } else {
914 0 : rv = _OldGetDiskConsumption::Get(aObserver);
915 0 : NS_ENSURE_SUCCESS(rv, rv);
916 : }
917 :
918 0 : return NS_OK;
919 : }
920 :
921 0 : NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget)
922 : {
923 0 : NS_ENSURE_ARG(aEventTarget);
924 :
925 0 : if (CacheObserver::UseNewCache()) {
926 0 : nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
927 0 : ioTarget.forget(aEventTarget);
928 : }
929 : else {
930 : nsresult rv;
931 :
932 : nsCOMPtr<nsICacheService> serv =
933 0 : do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
934 0 : NS_ENSURE_SUCCESS(rv, rv);
935 :
936 0 : rv = serv->GetCacheIOTarget(aEventTarget);
937 0 : NS_ENSURE_SUCCESS(rv, rv);
938 : }
939 :
940 0 : return NS_OK;
941 : }
942 :
943 0 : NS_IMETHODIMP CacheStorageService::AsyncVisitAllStorages(
944 : nsICacheStorageVisitor* aVisitor,
945 : bool aVisitEntries)
946 : {
947 0 : LOG(("CacheStorageService::AsyncVisitAllStorages [cb=%p]", aVisitor));
948 0 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
949 :
950 : // Walking the disk cache also walks the memory cache.
951 : RefPtr<WalkDiskCacheRunnable> event =
952 0 : new WalkDiskCacheRunnable(nullptr, aVisitEntries, aVisitor);
953 0 : return event->Walk();
954 :
955 : return NS_OK;
956 : }
957 :
958 : // Methods used by CacheEntry for management of in-memory structures.
959 :
960 : namespace {
961 :
962 : class FrecencyComparator
963 : {
964 : public:
965 0 : bool Equals(CacheEntry* a, CacheEntry* b) const {
966 0 : return a->GetFrecency() == b->GetFrecency();
967 : }
968 0 : bool LessThan(CacheEntry* a, CacheEntry* b) const {
969 0 : return a->GetFrecency() < b->GetFrecency();
970 : }
971 : };
972 :
973 : class ExpirationComparator
974 : {
975 : public:
976 0 : bool Equals(CacheEntry* a, CacheEntry* b) const {
977 0 : return a->GetExpirationTime() == b->GetExpirationTime();
978 : }
979 0 : bool LessThan(CacheEntry* a, CacheEntry* b) const {
980 0 : return a->GetExpirationTime() < b->GetExpirationTime();
981 : }
982 : };
983 :
984 : } // namespace
985 :
986 : void
987 5 : CacheStorageService::RegisterEntry(CacheEntry* aEntry)
988 : {
989 5 : MOZ_ASSERT(IsOnManagementThread());
990 :
991 5 : if (mShutdown || !aEntry->CanRegister())
992 0 : return;
993 :
994 5 : TelemetryRecordEntryCreation(aEntry);
995 :
996 5 : LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry));
997 :
998 5 : MemoryPool& pool = Pool(aEntry->IsUsingDisk());
999 5 : pool.mFrecencyArray.AppendElement(aEntry);
1000 5 : pool.mExpirationArray.AppendElement(aEntry);
1001 :
1002 5 : aEntry->SetRegistered(true);
1003 : }
1004 :
1005 : void
1006 0 : CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
1007 : {
1008 0 : MOZ_ASSERT(IsOnManagementThread());
1009 :
1010 0 : if (!aEntry->IsRegistered())
1011 0 : return;
1012 :
1013 0 : TelemetryRecordEntryRemoval(aEntry);
1014 :
1015 0 : LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry));
1016 :
1017 0 : MemoryPool& pool = Pool(aEntry->IsUsingDisk());
1018 0 : mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry);
1019 0 : mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry);
1020 :
1021 0 : MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration));
1022 :
1023 : // Note: aEntry->CanRegister() since now returns false
1024 0 : aEntry->SetRegistered(false);
1025 : }
1026 :
1027 : static bool
1028 0 : AddExactEntry(CacheEntryTable* aEntries,
1029 : nsACString const& aKey,
1030 : CacheEntry* aEntry,
1031 : bool aOverwrite)
1032 : {
1033 0 : RefPtr<CacheEntry> existingEntry;
1034 0 : if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
1035 0 : bool equals = existingEntry == aEntry;
1036 0 : LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals));
1037 0 : return equals; // Already there...
1038 : }
1039 :
1040 0 : LOG(("AddExactEntry [entry=%p put]", aEntry));
1041 0 : aEntries->Put(aKey, aEntry);
1042 0 : return true;
1043 : }
1044 :
1045 : static bool
1046 0 : RemoveExactEntry(CacheEntryTable* aEntries,
1047 : nsACString const& aKey,
1048 : CacheEntry* aEntry,
1049 : bool aOverwrite)
1050 : {
1051 0 : RefPtr<CacheEntry> existingEntry;
1052 0 : if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
1053 0 : LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
1054 0 : return false; // Already removed...
1055 : }
1056 :
1057 0 : if (!aOverwrite && existingEntry != aEntry) {
1058 0 : LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
1059 0 : return false; // Already replaced...
1060 : }
1061 :
1062 0 : LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
1063 0 : aEntries->Remove(aKey);
1064 0 : return true;
1065 : }
1066 :
1067 : bool
1068 0 : CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
1069 : {
1070 0 : LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry));
1071 :
1072 0 : nsAutoCString entryKey;
1073 0 : nsresult rv = aEntry->HashingKey(entryKey);
1074 0 : if (NS_FAILED(rv)) {
1075 0 : NS_ERROR("aEntry->HashingKey() failed?");
1076 0 : return false;
1077 : }
1078 :
1079 0 : mozilla::MutexAutoLock lock(mLock);
1080 :
1081 0 : if (mShutdown) {
1082 0 : LOG((" after shutdown"));
1083 0 : return false;
1084 : }
1085 :
1086 0 : if (aOnlyUnreferenced) {
1087 0 : if (aEntry->IsReferenced()) {
1088 0 : LOG((" still referenced, not removing"));
1089 0 : return false;
1090 : }
1091 :
1092 0 : if (!aEntry->IsUsingDisk() && IsForcedValidEntry(aEntry->GetStorageID(), entryKey)) {
1093 0 : LOG((" forced valid, not removing"));
1094 0 : return false;
1095 : }
1096 : }
1097 :
1098 : CacheEntryTable* entries;
1099 0 : if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
1100 0 : RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
1101 :
1102 0 : nsAutoCString memoryStorageID(aEntry->GetStorageID());
1103 0 : AppendMemoryStorageID(memoryStorageID);
1104 :
1105 0 : if (sGlobalEntryTables->Get(memoryStorageID, &entries))
1106 0 : RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
1107 :
1108 0 : return true;
1109 : }
1110 :
1111 : void
1112 5 : CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry,
1113 : bool aOnlyInMemory,
1114 : bool aOverwrite)
1115 : {
1116 5 : LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]",
1117 : aEntry, aOnlyInMemory, aOverwrite));
1118 : // This method is responsible to put this entry to a special record hashtable
1119 : // that contains only entries that are stored in memory.
1120 : // Keep in mind that every entry, regardless of whether is in-memory-only or not
1121 : // is always recorded in the storage master hash table, the one identified by
1122 : // CacheEntry.StorageID().
1123 :
1124 5 : mLock.AssertCurrentThreadOwns();
1125 :
1126 5 : if (mShutdown) {
1127 0 : LOG((" after shutdown"));
1128 5 : return;
1129 : }
1130 :
1131 : nsresult rv;
1132 :
1133 5 : nsAutoCString entryKey;
1134 5 : rv = aEntry->HashingKey(entryKey);
1135 5 : if (NS_FAILED(rv)) {
1136 0 : NS_ERROR("aEntry->HashingKey() failed?");
1137 0 : return;
1138 : }
1139 :
1140 5 : CacheEntryTable* entries = nullptr;
1141 5 : nsAutoCString memoryStorageID(aEntry->GetStorageID());
1142 5 : AppendMemoryStorageID(memoryStorageID);
1143 :
1144 5 : if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
1145 5 : if (!aOnlyInMemory) {
1146 5 : LOG((" not recorded as memory only"));
1147 5 : return;
1148 : }
1149 :
1150 0 : entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
1151 0 : sGlobalEntryTables->Put(memoryStorageID, entries);
1152 0 : LOG((" new memory-only storage table for %s", memoryStorageID.get()));
1153 : }
1154 :
1155 0 : if (aOnlyInMemory) {
1156 0 : AddExactEntry(entries, entryKey, aEntry, aOverwrite);
1157 : }
1158 : else {
1159 0 : RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
1160 : }
1161 : }
1162 :
1163 : // Checks if a cache entry is forced valid (will be loaded directly from cache
1164 : // without further validation) - see nsICacheEntry.idl for further details
1165 4 : bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextKey,
1166 : nsACString const &aEntryKey)
1167 : {
1168 4 : return IsForcedValidEntry(aContextKey + aEntryKey);
1169 : }
1170 :
1171 4 : bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextEntryKey)
1172 : {
1173 8 : mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1174 :
1175 4 : TimeStamp validUntil;
1176 :
1177 4 : if (!mForcedValidEntries.Get(aContextEntryKey, &validUntil)) {
1178 4 : return false;
1179 : }
1180 :
1181 0 : if (validUntil.IsNull()) {
1182 0 : return false;
1183 : }
1184 :
1185 : // Entry timeout not reached yet
1186 0 : if (TimeStamp::NowLoRes() <= validUntil) {
1187 0 : return true;
1188 : }
1189 :
1190 : // Entry timeout has been reached
1191 0 : mForcedValidEntries.Remove(aContextEntryKey);
1192 0 : return false;
1193 : }
1194 :
1195 : // Allows a cache entry to be loaded directly from cache without further
1196 : // validation - see nsICacheEntry.idl for further details
1197 0 : void CacheStorageService::ForceEntryValidFor(nsACString const &aContextKey,
1198 : nsACString const &aEntryKey,
1199 : uint32_t aSecondsToTheFuture)
1200 : {
1201 0 : mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1202 :
1203 0 : TimeStamp now = TimeStamp::NowLoRes();
1204 0 : ForcedValidEntriesPrune(now);
1205 :
1206 : // This will be the timeout
1207 0 : TimeStamp validUntil = now + TimeDuration::FromSeconds(aSecondsToTheFuture);
1208 :
1209 0 : mForcedValidEntries.Put(aContextKey + aEntryKey, validUntil);
1210 0 : }
1211 :
1212 1 : void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey,
1213 : nsACString const &aEntryKey)
1214 : {
1215 2 : mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1216 :
1217 1 : LOG(("CacheStorageService::RemoveEntryForceValid context='%s' entryKey=%s",
1218 : aContextKey.BeginReading(), aEntryKey.BeginReading()));
1219 1 : mForcedValidEntries.Remove(aContextKey + aEntryKey);
1220 1 : }
1221 :
1222 : // Cleans out the old entries in mForcedValidEntries
1223 0 : void CacheStorageService::ForcedValidEntriesPrune(TimeStamp &now)
1224 : {
1225 0 : static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
1226 0 : static TimeStamp dontPruneUntil = now + oneMinute;
1227 0 : if (now < dontPruneUntil)
1228 0 : return;
1229 :
1230 0 : for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
1231 0 : if (iter.Data() < now) {
1232 0 : iter.Remove();
1233 : }
1234 : }
1235 0 : dontPruneUntil = now + oneMinute;
1236 : }
1237 :
1238 : void
1239 26 : CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer,
1240 : uint32_t aCurrentMemoryConsumption)
1241 : {
1242 26 : LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]",
1243 : aConsumer, aCurrentMemoryConsumption));
1244 :
1245 26 : uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption;
1246 26 : if (savedMemorySize == aCurrentMemoryConsumption)
1247 35 : return;
1248 :
1249 : // Exchange saved size with current one.
1250 17 : aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption;
1251 :
1252 17 : bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY);
1253 17 : bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange(
1254 17 : savedMemorySize, aCurrentMemoryConsumption);
1255 :
1256 17 : if (!overLimit)
1257 17 : return;
1258 :
1259 : // It's likely the timer has already been set when we get here,
1260 : // check outside the lock to save resources.
1261 0 : if (mPurgeTimer)
1262 0 : return;
1263 :
1264 : // We don't know if this is called under the service lock or not,
1265 : // hence rather dispatch.
1266 0 : RefPtr<nsIEventTarget> cacheIOTarget = Thread();
1267 0 : if (!cacheIOTarget)
1268 0 : return;
1269 :
1270 : // Dispatch as a priority task, we want to set the purge timer
1271 : // ASAP to prevent vain redispatch of this event.
1272 : nsCOMPtr<nsIRunnable> event =
1273 0 : NewRunnableMethod("net::CacheStorageService::SchedulePurgeOverMemoryLimit",
1274 : this,
1275 0 : &CacheStorageService::SchedulePurgeOverMemoryLimit);
1276 0 : cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1277 : }
1278 :
1279 : bool
1280 17 : CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize,
1281 : uint32_t aCurrentMemoryConsumption)
1282 : {
1283 17 : mMemorySize -= aSavedMemorySize;
1284 17 : mMemorySize += aCurrentMemoryConsumption;
1285 :
1286 17 : LOG((" mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize));
1287 :
1288 : // Bypass purging when memory has not grew up significantly
1289 17 : if (aCurrentMemoryConsumption <= aSavedMemorySize)
1290 2 : return false;
1291 :
1292 15 : return mMemorySize > Limit();
1293 : }
1294 :
1295 : void
1296 0 : CacheStorageService::SchedulePurgeOverMemoryLimit()
1297 : {
1298 0 : LOG(("CacheStorageService::SchedulePurgeOverMemoryLimit"));
1299 :
1300 0 : mozilla::MutexAutoLock lock(mLock);
1301 :
1302 0 : if (mShutdown) {
1303 0 : LOG((" past shutdown"));
1304 0 : return;
1305 : }
1306 :
1307 0 : if (mPurgeTimer) {
1308 0 : LOG((" timer already up"));
1309 0 : return;
1310 : }
1311 :
1312 0 : mPurgeTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
1313 0 : if (mPurgeTimer) {
1314 : nsresult rv;
1315 0 : rv = mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
1316 0 : LOG((" timer init rv=0x%08" PRIx32, static_cast<uint32_t>(rv)));
1317 : }
1318 : }
1319 :
1320 : NS_IMETHODIMP
1321 0 : CacheStorageService::Notify(nsITimer* aTimer)
1322 : {
1323 0 : LOG(("CacheStorageService::Notify"));
1324 :
1325 0 : mozilla::MutexAutoLock lock(mLock);
1326 :
1327 0 : if (aTimer == mPurgeTimer) {
1328 0 : mPurgeTimer = nullptr;
1329 :
1330 : nsCOMPtr<nsIRunnable> event =
1331 0 : NewRunnableMethod("net::CacheStorageService::PurgeOverMemoryLimit",
1332 : this,
1333 0 : &CacheStorageService::PurgeOverMemoryLimit);
1334 0 : Dispatch(event);
1335 : }
1336 :
1337 0 : return NS_OK;
1338 : }
1339 :
1340 : void
1341 0 : CacheStorageService::PurgeOverMemoryLimit()
1342 : {
1343 0 : MOZ_ASSERT(IsOnManagementThread());
1344 :
1345 0 : LOG(("CacheStorageService::PurgeOverMemoryLimit"));
1346 :
1347 0 : static TimeDuration const kFourSeconds = TimeDuration::FromSeconds(4);
1348 0 : TimeStamp now = TimeStamp::NowLoRes();
1349 :
1350 0 : if (!mLastPurgeTime.IsNull() && now - mLastPurgeTime < kFourSeconds) {
1351 0 : LOG((" bypassed, too soon"));
1352 0 : return;
1353 : }
1354 :
1355 0 : mLastPurgeTime = now;
1356 :
1357 0 : Pool(true).PurgeOverMemoryLimit();
1358 0 : Pool(false).PurgeOverMemoryLimit();
1359 : }
1360 :
1361 : void
1362 0 : CacheStorageService::MemoryPool::PurgeOverMemoryLimit()
1363 : {
1364 0 : TimeStamp start(TimeStamp::Now());
1365 :
1366 0 : uint32_t const memoryLimit = Limit();
1367 0 : if (mMemorySize > memoryLimit) {
1368 0 : LOG((" memory data consumption over the limit, abandon expired entries"));
1369 0 : PurgeExpired();
1370 : }
1371 :
1372 0 : bool frecencyNeedsSort = true;
1373 :
1374 : // No longer makes sense since:
1375 : // Memory entries are never purged partially, only as a whole when the memory
1376 : // cache limit is overreached.
1377 : // Disk entries throw the data away ASAP so that only metadata are kept.
1378 : // TODO when this concept of two separate pools is found working, the code should
1379 : // clean up.
1380 : #if 0
1381 : if (mMemorySize > memoryLimit) {
1382 : LOG((" memory data consumption over the limit, abandon disk backed data"));
1383 : PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED);
1384 : }
1385 :
1386 : if (mMemorySize > memoryLimit) {
1387 : LOG((" metadata consumtion over the limit, abandon disk backed entries"));
1388 : PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED);
1389 : }
1390 : #endif
1391 :
1392 0 : if (mMemorySize > memoryLimit) {
1393 0 : LOG((" memory data consumption over the limit, abandon any entry"));
1394 0 : PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
1395 : }
1396 :
1397 0 : LOG((" purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
1398 0 : }
1399 :
1400 : void
1401 0 : CacheStorageService::MemoryPool::PurgeExpired()
1402 : {
1403 0 : MOZ_ASSERT(IsOnManagementThread());
1404 :
1405 0 : mExpirationArray.Sort(ExpirationComparator());
1406 0 : uint32_t now = NowInSeconds();
1407 :
1408 0 : uint32_t const memoryLimit = Limit();
1409 :
1410 0 : for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
1411 0 : if (CacheIOThread::YieldAndRerun())
1412 0 : return;
1413 :
1414 0 : RefPtr<CacheEntry> entry = mExpirationArray[i];
1415 :
1416 0 : uint32_t expirationTime = entry->GetExpirationTime();
1417 0 : if (expirationTime > 0 && expirationTime <= now &&
1418 0 : entry->Purge(CacheEntry::PURGE_WHOLE)) {
1419 0 : LOG((" purged expired, entry=%p, exptime=%u (now=%u)",
1420 : entry.get(), entry->GetExpirationTime(), now));
1421 0 : continue;
1422 : }
1423 :
1424 : // not purged, move to the next one
1425 0 : ++i;
1426 : }
1427 : }
1428 :
1429 : void
1430 0 : CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat)
1431 : {
1432 0 : MOZ_ASSERT(IsOnManagementThread());
1433 :
1434 0 : if (aFrecencyNeedsSort) {
1435 0 : mFrecencyArray.Sort(FrecencyComparator());
1436 0 : aFrecencyNeedsSort = false;
1437 : }
1438 :
1439 0 : uint32_t const memoryLimit = Limit();
1440 :
1441 0 : for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
1442 0 : if (CacheIOThread::YieldAndRerun())
1443 0 : return;
1444 :
1445 0 : RefPtr<CacheEntry> entry = mFrecencyArray[i];
1446 :
1447 0 : if (entry->Purge(aWhat)) {
1448 0 : LOG((" abandoned (%d), entry=%p, frecency=%1.10f",
1449 : aWhat, entry.get(), entry->GetFrecency()));
1450 0 : continue;
1451 : }
1452 :
1453 : // not purged, move to the next one
1454 0 : ++i;
1455 : }
1456 : }
1457 :
1458 : void
1459 0 : CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat)
1460 : {
1461 0 : LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat));
1462 0 : MOZ_ASSERT(IsOnManagementThread());
1463 :
1464 0 : for (uint32_t i = 0; i < mFrecencyArray.Length();) {
1465 0 : if (CacheIOThread::YieldAndRerun())
1466 0 : return;
1467 :
1468 0 : RefPtr<CacheEntry> entry = mFrecencyArray[i];
1469 :
1470 0 : if (entry->Purge(aWhat)) {
1471 0 : LOG((" abandoned entry=%p", entry.get()));
1472 0 : continue;
1473 : }
1474 :
1475 : // not purged, move to the next one
1476 0 : ++i;
1477 : }
1478 : }
1479 :
1480 : // Methods exposed to and used by CacheStorage.
1481 :
1482 : nsresult
1483 13 : CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
1484 : const nsACString & aURI,
1485 : const nsACString & aIdExtension,
1486 : bool aReplace,
1487 : CacheEntryHandle** aResult)
1488 : {
1489 13 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1490 :
1491 13 : NS_ENSURE_ARG(aStorage);
1492 :
1493 26 : nsAutoCString contextKey;
1494 13 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1495 :
1496 52 : return AddStorageEntry(contextKey, aURI, aIdExtension,
1497 13 : aStorage->WriteToDisk(),
1498 13 : aStorage->SkipSizeCheck(),
1499 13 : aStorage->Pinning(),
1500 : aReplace,
1501 13 : aResult);
1502 : }
1503 :
1504 : nsresult
1505 13 : CacheStorageService::AddStorageEntry(const nsACString& aContextKey,
1506 : const nsACString & aURI,
1507 : const nsACString & aIdExtension,
1508 : bool aWriteToDisk,
1509 : bool aSkipSizeCheck,
1510 : bool aPin,
1511 : bool aReplace,
1512 : CacheEntryHandle** aResult)
1513 : {
1514 : nsresult rv;
1515 :
1516 26 : nsAutoCString entryKey;
1517 13 : rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1518 13 : NS_ENSURE_SUCCESS(rv, rv);
1519 :
1520 13 : LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
1521 : entryKey.get(), aContextKey.BeginReading()));
1522 :
1523 26 : RefPtr<CacheEntry> entry;
1524 26 : RefPtr<CacheEntryHandle> handle;
1525 :
1526 : {
1527 26 : mozilla::MutexAutoLock lock(mLock);
1528 :
1529 13 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1530 :
1531 : // Ensure storage table
1532 : CacheEntryTable* entries;
1533 13 : if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
1534 1 : entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
1535 1 : sGlobalEntryTables->Put(aContextKey, entries);
1536 1 : LOG((" new storage entries table for context '%s'", aContextKey.BeginReading()));
1537 : }
1538 :
1539 13 : bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
1540 :
1541 13 : if (entryExists && !aReplace) {
1542 : // check whether we want to turn this entry to a memory-only.
1543 8 : if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
1544 0 : LOG((" entry is persistent but we want mem-only, replacing it"));
1545 0 : aReplace = true;
1546 : }
1547 : }
1548 :
1549 : // If truncate is demanded, delete and doom the current entry
1550 13 : if (entryExists && aReplace) {
1551 0 : entries->Remove(entryKey);
1552 :
1553 0 : LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
1554 : // On purpose called under the lock to prevent races of doom and open on I/O thread
1555 : // No need to remove from both memory-only and all-entries tables. The new entry
1556 : // will overwrite the shadow entry in its ctor.
1557 0 : entry->DoomAlreadyRemoved();
1558 :
1559 0 : entry = nullptr;
1560 0 : entryExists = false;
1561 :
1562 : // Would only lead to deleting force-valid timestamp again. We don't need the
1563 : // replace information anymore after this point anyway.
1564 0 : aReplace = false;
1565 : }
1566 :
1567 : // Ensure entry for the particular URL
1568 13 : if (!entryExists) {
1569 : // When replacing with a new entry, always remove the current force-valid timestamp,
1570 : // this is the only place to do it.
1571 5 : if (aReplace) {
1572 0 : RemoveEntryForceValid(aContextKey, entryKey);
1573 : }
1574 :
1575 : // Entry is not in the hashtable or has just been truncated...
1576 5 : entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
1577 5 : entries->Put(entryKey, entry);
1578 5 : LOG((" new entry %p for %s", entry.get(), entryKey.get()));
1579 : }
1580 :
1581 13 : if (entry) {
1582 : // Here, if this entry was not for a long time referenced by any consumer,
1583 : // gets again first 'handles count' reference.
1584 13 : handle = entry->NewHandle();
1585 : }
1586 : }
1587 :
1588 13 : handle.forget(aResult);
1589 13 : return NS_OK;
1590 : }
1591 :
1592 : nsresult
1593 0 : CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage,
1594 : const nsACString & aURI,
1595 : const nsACString & aIdExtension,
1596 : bool* aResult)
1597 : {
1598 : nsresult rv;
1599 :
1600 0 : nsAutoCString contextKey;
1601 0 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1602 :
1603 0 : if (!aStorage->WriteToDisk()) {
1604 0 : AppendMemoryStorageID(contextKey);
1605 : }
1606 :
1607 0 : LOG(("CacheStorageService::CheckStorageEntry [uri=%s, eid=%s, contextKey=%s]",
1608 : aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
1609 :
1610 : {
1611 0 : mozilla::MutexAutoLock lock(mLock);
1612 :
1613 0 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1614 :
1615 0 : nsAutoCString entryKey;
1616 0 : rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1617 0 : NS_ENSURE_SUCCESS(rv, rv);
1618 :
1619 : CacheEntryTable* entries;
1620 0 : if ((*aResult = sGlobalEntryTables->Get(contextKey, &entries)) &&
1621 0 : entries->GetWeak(entryKey, aResult)) {
1622 0 : LOG((" found in hash tables"));
1623 0 : return NS_OK;
1624 : }
1625 : }
1626 :
1627 0 : if (!aStorage->WriteToDisk()) {
1628 : // Memory entry, nothing more to do.
1629 0 : LOG((" not found in hash tables"));
1630 0 : return NS_OK;
1631 : }
1632 :
1633 : // Disk entry, not found in the hashtable, check the index.
1634 0 : nsAutoCString fileKey;
1635 0 : rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
1636 :
1637 : CacheIndex::EntryStatus status;
1638 0 : rv = CacheIndex::HasEntry(fileKey, &status);
1639 0 : if (NS_FAILED(rv) || status == CacheIndex::DO_NOT_KNOW) {
1640 0 : LOG((" index doesn't know, rv=0x%08" PRIx32, static_cast<uint32_t>(rv)));
1641 0 : return NS_ERROR_NOT_AVAILABLE;
1642 : }
1643 :
1644 0 : *aResult = status == CacheIndex::EXISTS;
1645 0 : LOG((" %sfound in index", *aResult ? "" : "not "));
1646 0 : return NS_OK;
1647 : }
1648 :
1649 : nsresult
1650 5 : CacheStorageService::GetCacheIndexEntryAttrs(CacheStorage const* aStorage,
1651 : const nsACString &aURI,
1652 : const nsACString &aIdExtension,
1653 : bool *aHasAltData,
1654 : uint32_t *aFileSizeKb)
1655 : {
1656 : nsresult rv;
1657 :
1658 10 : nsAutoCString contextKey;
1659 5 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1660 :
1661 5 : LOG(("CacheStorageService::GetCacheIndexEntryAttrs [uri=%s, eid=%s, contextKey=%s]",
1662 : aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
1663 :
1664 10 : nsAutoCString fileKey;
1665 5 : rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
1666 5 : if (NS_FAILED(rv)) {
1667 0 : return rv;
1668 : }
1669 :
1670 5 : *aHasAltData = false;
1671 5 : *aFileSizeKb = 0;
1672 4 : auto closure = [&aHasAltData, &aFileSizeKb](const CacheIndexEntry *entry) {
1673 2 : *aHasAltData = entry->GetHasAltData();
1674 2 : *aFileSizeKb = entry->GetFileSize();
1675 7 : };
1676 :
1677 : CacheIndex::EntryStatus status;
1678 5 : rv = CacheIndex::HasEntry(fileKey, &status, closure);
1679 5 : if (NS_FAILED(rv)) {
1680 0 : return rv;
1681 : }
1682 :
1683 5 : if (status != CacheIndex::EXISTS) {
1684 3 : return NS_ERROR_CACHE_KEY_NOT_FOUND;
1685 : }
1686 :
1687 2 : return NS_OK;
1688 : }
1689 :
1690 :
1691 : namespace {
1692 :
1693 : class CacheEntryDoomByKeyCallback : public CacheFileIOListener
1694 : , public nsIRunnable
1695 : {
1696 : public:
1697 : NS_DECL_THREADSAFE_ISUPPORTS
1698 : NS_DECL_NSIRUNNABLE
1699 :
1700 1 : explicit CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback)
1701 1 : : mCallback(aCallback) { }
1702 :
1703 : private:
1704 : virtual ~CacheEntryDoomByKeyCallback();
1705 :
1706 0 : NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1707 0 : NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) override { return NS_OK; }
1708 0 : NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override { return NS_OK; }
1709 : NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
1710 0 : NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1711 0 : NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1712 :
1713 : nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1714 : nsresult mResult;
1715 : };
1716 :
1717 3 : CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback()
1718 : {
1719 1 : if (mCallback)
1720 0 : ProxyReleaseMainThread(
1721 0 : "CacheEntryDoomByKeyCallback::mCallback", mCallback);
1722 3 : }
1723 :
1724 1 : NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
1725 : nsresult aResult)
1726 : {
1727 1 : if (!mCallback)
1728 1 : return NS_OK;
1729 :
1730 0 : mResult = aResult;
1731 0 : if (NS_IsMainThread()) {
1732 0 : Run();
1733 : } else {
1734 0 : NS_DispatchToMainThread(this);
1735 : }
1736 :
1737 0 : return NS_OK;
1738 : }
1739 :
1740 0 : NS_IMETHODIMP CacheEntryDoomByKeyCallback::Run()
1741 : {
1742 0 : mCallback->OnCacheEntryDoomed(mResult);
1743 0 : return NS_OK;
1744 : }
1745 :
1746 7 : NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener, nsIRunnable);
1747 :
1748 : } // namespace
1749 :
1750 : nsresult
1751 1 : CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage,
1752 : const nsACString & aURI,
1753 : const nsACString & aIdExtension,
1754 : nsICacheEntryDoomCallback* aCallback)
1755 : {
1756 1 : LOG(("CacheStorageService::DoomStorageEntry"));
1757 :
1758 1 : NS_ENSURE_ARG(aStorage);
1759 :
1760 2 : nsAutoCString contextKey;
1761 1 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1762 :
1763 2 : nsAutoCString entryKey;
1764 1 : nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1765 1 : NS_ENSURE_SUCCESS(rv, rv);
1766 :
1767 2 : RefPtr<CacheEntry> entry;
1768 : {
1769 2 : mozilla::MutexAutoLock lock(mLock);
1770 :
1771 1 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1772 :
1773 : CacheEntryTable* entries;
1774 1 : if (sGlobalEntryTables->Get(contextKey, &entries)) {
1775 0 : if (entries->Get(entryKey, getter_AddRefs(entry))) {
1776 0 : if (aStorage->WriteToDisk() || !entry->IsUsingDisk()) {
1777 : // When evicting from disk storage, purge
1778 : // When evicting from memory storage and the entry is memory-only, purge
1779 0 : LOG((" purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
1780 : entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
1781 0 : entries->Remove(entryKey);
1782 : }
1783 : else {
1784 : // Otherwise, leave it
1785 0 : LOG((" leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
1786 : entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
1787 0 : entry = nullptr;
1788 : }
1789 : }
1790 : }
1791 :
1792 1 : if (!entry) {
1793 1 : RemoveEntryForceValid(contextKey, entryKey);
1794 : }
1795 : }
1796 :
1797 1 : if (entry) {
1798 0 : LOG((" dooming entry %p for %s", entry.get(), entryKey.get()));
1799 0 : return entry->AsyncDoom(aCallback);
1800 : }
1801 :
1802 1 : LOG((" no entry loaded for %s", entryKey.get()));
1803 :
1804 1 : if (aStorage->WriteToDisk()) {
1805 2 : nsAutoCString contextKey;
1806 1 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1807 :
1808 1 : rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey);
1809 1 : NS_ENSURE_SUCCESS(rv, rv);
1810 :
1811 1 : LOG((" dooming file only for %s", entryKey.get()));
1812 :
1813 : RefPtr<CacheEntryDoomByKeyCallback> callback(
1814 2 : new CacheEntryDoomByKeyCallback(aCallback));
1815 1 : rv = CacheFileIOManager::DoomFileByKey(entryKey, callback);
1816 1 : NS_ENSURE_SUCCESS(rv, rv);
1817 :
1818 1 : return NS_OK;
1819 : }
1820 :
1821 0 : class Callback : public Runnable
1822 : {
1823 : public:
1824 0 : explicit Callback(nsICacheEntryDoomCallback* aCallback)
1825 0 : : mozilla::Runnable("Callback")
1826 0 : , mCallback(aCallback)
1827 : {
1828 0 : }
1829 0 : NS_IMETHOD Run() override
1830 : {
1831 0 : mCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
1832 0 : return NS_OK;
1833 : }
1834 : nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1835 : };
1836 :
1837 0 : if (aCallback) {
1838 0 : RefPtr<Runnable> callback = new Callback(aCallback);
1839 0 : return NS_DispatchToMainThread(callback);
1840 : }
1841 :
1842 0 : return NS_OK;
1843 : }
1844 :
1845 : nsresult
1846 0 : CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
1847 : nsICacheEntryDoomCallback* aCallback)
1848 : {
1849 0 : LOG(("CacheStorageService::DoomStorageEntries"));
1850 :
1851 0 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1852 0 : NS_ENSURE_ARG(aStorage);
1853 :
1854 0 : nsAutoCString contextKey;
1855 0 : CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1856 :
1857 0 : mozilla::MutexAutoLock lock(mLock);
1858 :
1859 0 : return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
1860 0 : aStorage->WriteToDisk(), aStorage->Pinning(),
1861 0 : aCallback);
1862 : }
1863 :
1864 : nsresult
1865 0 : CacheStorageService::DoomStorageEntries(const nsACString& aContextKey,
1866 : nsILoadContextInfo* aContext,
1867 : bool aDiskStorage,
1868 : bool aPinned,
1869 : nsICacheEntryDoomCallback* aCallback)
1870 : {
1871 0 : LOG(("CacheStorageService::DoomStorageEntries [context=%s]", aContextKey.BeginReading()));
1872 :
1873 0 : mLock.AssertCurrentThreadOwns();
1874 :
1875 0 : NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
1876 :
1877 0 : nsAutoCString memoryStorageID(aContextKey);
1878 0 : AppendMemoryStorageID(memoryStorageID);
1879 :
1880 0 : if (aDiskStorage) {
1881 0 : LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading()));
1882 :
1883 : // Walk one by one and remove entries according their pin status
1884 : CacheEntryTable *diskEntries, *memoryEntries;
1885 0 : if (sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
1886 0 : sGlobalEntryTables->Get(memoryStorageID, &memoryEntries);
1887 :
1888 0 : for (auto iter = diskEntries->Iter(); !iter.Done(); iter.Next()) {
1889 0 : auto entry = iter.Data();
1890 0 : if (entry->DeferOrBypassRemovalOnPinStatus(aPinned)) {
1891 0 : continue;
1892 : }
1893 :
1894 0 : if (memoryEntries) {
1895 0 : RemoveExactEntry(memoryEntries, iter.Key(), entry, false);
1896 : }
1897 0 : iter.Remove();
1898 : }
1899 : }
1900 :
1901 0 : if (aContext && !aContext->IsPrivate()) {
1902 0 : LOG((" dooming disk entries"));
1903 0 : CacheFileIOManager::EvictByContext(aContext, aPinned);
1904 : }
1905 : } else {
1906 0 : LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));
1907 :
1908 : // Remove the memory entries table from the global tables.
1909 : // Since we store memory entries also in the disk entries table
1910 : // we need to remove the memory entries from the disk table one
1911 : // by one manually.
1912 0 : nsAutoPtr<CacheEntryTable> memoryEntries;
1913 0 : sGlobalEntryTables->Remove(memoryStorageID, &memoryEntries);
1914 :
1915 : CacheEntryTable* diskEntries;
1916 0 : if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
1917 0 : for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
1918 0 : auto entry = iter.Data();
1919 0 : RemoveExactEntry(diskEntries, iter.Key(), entry, false);
1920 : }
1921 : }
1922 : }
1923 :
1924 : {
1925 0 : mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1926 :
1927 0 : if (aContext) {
1928 0 : for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
1929 : bool matches;
1930 0 : DebugOnly<nsresult> rv = CacheFileUtils::KeyMatchesLoadContextInfo(
1931 0 : iter.Key(), aContext, &matches);
1932 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1933 :
1934 0 : if (matches) {
1935 0 : iter.Remove();
1936 : }
1937 : }
1938 : } else {
1939 0 : mForcedValidEntries.Clear();
1940 : }
1941 : }
1942 :
1943 : // An artificial callback. This is a candidate for removal tho. In the new
1944 : // cache any 'doom' or 'evict' function ensures that the entry or entries
1945 : // being doomed is/are not accessible after the function returns. So there is
1946 : // probably no need for a callback - has no meaning. But for compatibility
1947 : // with the old cache that is still in the tree we keep the API similar to be
1948 : // able to make tests as well as other consumers work for now.
1949 0 : class Callback : public Runnable
1950 : {
1951 : public:
1952 0 : explicit Callback(nsICacheEntryDoomCallback* aCallback)
1953 0 : : mozilla::Runnable("Callback")
1954 0 : , mCallback(aCallback)
1955 : {
1956 0 : }
1957 0 : NS_IMETHOD Run() override
1958 : {
1959 0 : mCallback->OnCacheEntryDoomed(NS_OK);
1960 0 : return NS_OK;
1961 : }
1962 : nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1963 : };
1964 :
1965 0 : if (aCallback) {
1966 0 : RefPtr<Runnable> callback = new Callback(aCallback);
1967 0 : return NS_DispatchToMainThread(callback);
1968 : }
1969 :
1970 0 : return NS_OK;
1971 : }
1972 :
1973 : nsresult
1974 0 : CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
1975 : bool aVisitEntries,
1976 : nsICacheStorageVisitor* aVisitor)
1977 : {
1978 0 : LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries));
1979 0 : NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1980 :
1981 0 : NS_ENSURE_ARG(aStorage);
1982 :
1983 0 : if (aStorage->WriteToDisk()) {
1984 : RefPtr<WalkDiskCacheRunnable> event =
1985 0 : new WalkDiskCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
1986 0 : return event->Walk();
1987 : }
1988 :
1989 : RefPtr<WalkMemoryCacheRunnable> event =
1990 0 : new WalkMemoryCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
1991 0 : return event->Walk();
1992 : }
1993 :
1994 : void
1995 0 : CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
1996 : const nsACString & aIdExtension,
1997 : const nsACString & aURISpec)
1998 : {
1999 0 : nsAutoCString contextKey;
2000 0 : CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
2001 :
2002 0 : nsAutoCString entryKey;
2003 0 : CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
2004 :
2005 0 : mozilla::MutexAutoLock lock(mLock);
2006 :
2007 0 : if (mShutdown) {
2008 0 : return;
2009 : }
2010 :
2011 : CacheEntryTable* entries;
2012 0 : RefPtr<CacheEntry> entry;
2013 :
2014 0 : if (sGlobalEntryTables->Get(contextKey, &entries) &&
2015 0 : entries->Get(entryKey, getter_AddRefs(entry))) {
2016 0 : if (entry->IsFileDoomed()) {
2017 : // Need to remove under the lock to avoid possible race leading
2018 : // to duplication of the entry per its key.
2019 0 : RemoveExactEntry(entries, entryKey, entry, false);
2020 0 : entry->DoomAlreadyRemoved();
2021 : }
2022 :
2023 : // Entry found, but it's not the entry that has been found doomed
2024 : // by the lower eviction layer. Just leave everything unchanged.
2025 0 : return;
2026 : }
2027 :
2028 0 : RemoveEntryForceValid(contextKey, entryKey);
2029 : }
2030 :
2031 : bool
2032 0 : CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo,
2033 : const nsACString & aIdExtension,
2034 : const nsACString & aURISpec,
2035 : EntryInfoCallback *aCallback)
2036 : {
2037 0 : nsAutoCString contextKey;
2038 0 : CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
2039 :
2040 0 : nsAutoCString entryKey;
2041 0 : CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
2042 :
2043 0 : RefPtr<CacheEntry> entry;
2044 : {
2045 0 : mozilla::MutexAutoLock lock(mLock);
2046 :
2047 0 : if (mShutdown) {
2048 0 : return false;
2049 : }
2050 :
2051 : CacheEntryTable* entries;
2052 0 : if (!sGlobalEntryTables->Get(contextKey, &entries)) {
2053 0 : return false;
2054 : }
2055 :
2056 0 : if (!entries->Get(entryKey, getter_AddRefs(entry))) {
2057 0 : return false;
2058 : }
2059 : }
2060 :
2061 0 : GetCacheEntryInfo(entry, aCallback);
2062 0 : return true;
2063 : }
2064 :
2065 : // static
2066 : void
2067 0 : CacheStorageService::GetCacheEntryInfo(CacheEntry* aEntry,
2068 : EntryInfoCallback *aCallback)
2069 : {
2070 0 : nsCString const uriSpec = aEntry->GetURI();
2071 0 : nsCString const enhanceId = aEntry->GetEnhanceID();
2072 :
2073 0 : nsAutoCString entryKey;
2074 0 : aEntry->HashingKeyWithStorage(entryKey);
2075 :
2076 : nsCOMPtr<nsILoadContextInfo> info =
2077 0 : CacheFileUtils::ParseKey(entryKey);
2078 :
2079 : uint32_t dataSize;
2080 0 : if (NS_FAILED(aEntry->GetStorageDataSize(&dataSize))) {
2081 0 : dataSize = 0;
2082 : }
2083 : int32_t fetchCount;
2084 0 : if (NS_FAILED(aEntry->GetFetchCount(&fetchCount))) {
2085 0 : fetchCount = 0;
2086 : }
2087 : uint32_t lastModified;
2088 0 : if (NS_FAILED(aEntry->GetLastModified(&lastModified))) {
2089 0 : lastModified = 0;
2090 : }
2091 : uint32_t expirationTime;
2092 0 : if (NS_FAILED(aEntry->GetExpirationTime(&expirationTime))) {
2093 0 : expirationTime = 0;
2094 : }
2095 :
2096 0 : aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize,
2097 : fetchCount, lastModified, expirationTime,
2098 0 : aEntry->IsPinned(), info);
2099 0 : }
2100 :
2101 : // static
2102 5 : uint32_t CacheStorageService::CacheQueueSize(bool highPriority)
2103 : {
2104 10 : RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2105 5 : MOZ_ASSERT(thread);
2106 10 : return thread->QueueSize(highPriority);
2107 : }
2108 :
2109 : // Telemetry collection
2110 :
2111 : namespace {
2112 :
2113 5 : bool TelemetryEntryKey(CacheEntry const* entry, nsAutoCString& key)
2114 : {
2115 10 : nsAutoCString entryKey;
2116 5 : nsresult rv = entry->HashingKey(entryKey);
2117 5 : if (NS_FAILED(rv))
2118 0 : return false;
2119 :
2120 5 : if (entry->GetStorageID().IsEmpty()) {
2121 : // Hopefully this will be const-copied, saves some memory
2122 5 : key = entryKey;
2123 : } else {
2124 0 : key.Assign(entry->GetStorageID());
2125 0 : key.Append(':');
2126 0 : key.Append(entryKey);
2127 : }
2128 :
2129 5 : return true;
2130 : }
2131 :
2132 : } // namespace
2133 :
2134 : void
2135 5 : CacheStorageService::TelemetryPrune(TimeStamp &now)
2136 : {
2137 5 : static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
2138 5 : static TimeStamp dontPruneUntil = now + oneMinute;
2139 5 : if (now < dontPruneUntil)
2140 5 : return;
2141 :
2142 0 : static TimeDuration const fifteenMinutes = TimeDuration::FromSeconds(900);
2143 0 : for (auto iter = mPurgeTimeStamps.Iter(); !iter.Done(); iter.Next()) {
2144 0 : if (now - iter.Data() > fifteenMinutes) {
2145 : // We are not interested in resurrection of entries after 15 minutes
2146 : // of time. This is also the limit for the telemetry.
2147 0 : iter.Remove();
2148 : }
2149 : }
2150 0 : dontPruneUntil = now + oneMinute;
2151 : }
2152 :
2153 : void
2154 5 : CacheStorageService::TelemetryRecordEntryCreation(CacheEntry const* entry)
2155 : {
2156 5 : MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
2157 :
2158 5 : nsAutoCString key;
2159 5 : if (!TelemetryEntryKey(entry, key))
2160 0 : return;
2161 :
2162 5 : TimeStamp now = TimeStamp::NowLoRes();
2163 5 : TelemetryPrune(now);
2164 :
2165 : // When an entry is craeted (registered actually) we check if there is
2166 : // a timestamp marked when this very same cache entry has been removed
2167 : // (deregistered) because of over-memory-limit purging. If there is such
2168 : // a timestamp found accumulate telemetry on how long the entry was away.
2169 5 : TimeStamp timeStamp;
2170 5 : if (!mPurgeTimeStamps.Get(key, &timeStamp))
2171 5 : return;
2172 :
2173 0 : mPurgeTimeStamps.Remove(key);
2174 :
2175 0 : Telemetry::AccumulateTimeDelta(Telemetry::HTTP_CACHE_ENTRY_RELOAD_TIME,
2176 0 : timeStamp, TimeStamp::NowLoRes());
2177 : }
2178 :
2179 : void
2180 0 : CacheStorageService::TelemetryRecordEntryRemoval(CacheEntry const* entry)
2181 : {
2182 0 : MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
2183 :
2184 : // Doomed entries must not be considered, we are only interested in purged
2185 : // entries. Note that the mIsDoomed flag is always set before deregistration
2186 : // happens.
2187 0 : if (entry->IsDoomed())
2188 0 : return;
2189 :
2190 0 : nsAutoCString key;
2191 0 : if (!TelemetryEntryKey(entry, key))
2192 0 : return;
2193 :
2194 : // When an entry is removed (deregistered actually) we put a timestamp for this
2195 : // entry to the hashtable so that when the entry is created (registered) again
2196 : // we know how long it was away. Also accumulate number of AsyncOpen calls on
2197 : // the entry, this tells us how efficiently the pool actually works.
2198 :
2199 0 : TimeStamp now = TimeStamp::NowLoRes();
2200 0 : TelemetryPrune(now);
2201 0 : mPurgeTimeStamps.Put(key, now);
2202 :
2203 0 : Telemetry::Accumulate(Telemetry::HTTP_CACHE_ENTRY_REUSE_COUNT, entry->UseCount());
2204 0 : Telemetry::AccumulateTimeDelta(Telemetry::HTTP_CACHE_ENTRY_ALIVE_TIME,
2205 0 : entry->LoadStart(), TimeStamp::NowLoRes());
2206 : }
2207 :
2208 : // nsIMemoryReporter
2209 :
2210 : size_t
2211 0 : CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
2212 : {
2213 0 : CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
2214 :
2215 0 : size_t n = 0;
2216 : // The elemets are referenced by sGlobalEntryTables and are reported from there
2217 0 : n += Pool(true).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2218 0 : n += Pool(true).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2219 0 : n += Pool(false).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2220 0 : n += Pool(false).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2221 : // Entries reported manually in CacheStorageService::CollectReports callback
2222 0 : if (sGlobalEntryTables) {
2223 0 : n += sGlobalEntryTables->ShallowSizeOfIncludingThis(mallocSizeOf);
2224 : }
2225 :
2226 0 : return n;
2227 : }
2228 :
2229 : size_t
2230 0 : CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
2231 : {
2232 0 : return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
2233 : }
2234 :
2235 : NS_IMETHODIMP
2236 0 : CacheStorageService::CollectReports(nsIHandleReportCallback* aHandleReport,
2237 : nsISupports* aData, bool aAnonymize)
2238 : {
2239 0 : MOZ_COLLECT_REPORT(
2240 : "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
2241 : CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
2242 0 : "Memory used by the cache IO manager.");
2243 :
2244 0 : MOZ_COLLECT_REPORT(
2245 : "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
2246 : CacheIndex::SizeOfIncludingThis(MallocSizeOf),
2247 0 : "Memory used by the cache index.");
2248 :
2249 0 : MutexAutoLock lock(mLock);
2250 :
2251 : // Report the service instance, this doesn't report entries, done lower
2252 0 : MOZ_COLLECT_REPORT(
2253 : "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
2254 : SizeOfIncludingThis(MallocSizeOf),
2255 0 : "Memory used by the cache storage service.");
2256 :
2257 : // Report all entries, each storage separately (by the context key)
2258 : //
2259 : // References are:
2260 : // sGlobalEntryTables to N CacheEntryTable
2261 : // CacheEntryTable to N CacheEntry
2262 : // CacheEntry to 1 CacheFile
2263 : // CacheFile to
2264 : // N CacheFileChunk (keeping the actual data)
2265 : // 1 CacheFileMetadata (keeping http headers etc.)
2266 : // 1 CacheFileOutputStream
2267 : // N CacheFileInputStream
2268 0 : if (sGlobalEntryTables) {
2269 0 : for (auto iter1 = sGlobalEntryTables->Iter(); !iter1.Done(); iter1.Next()) {
2270 0 : CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
2271 :
2272 0 : CacheEntryTable* table = iter1.UserData();
2273 :
2274 0 : size_t size = 0;
2275 0 : mozilla::MallocSizeOf mallocSizeOf = CacheStorageService::MallocSizeOf;
2276 :
2277 0 : size += table->ShallowSizeOfIncludingThis(mallocSizeOf);
2278 0 : for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
2279 0 : size += iter2.Key().SizeOfExcludingThisIfUnshared(mallocSizeOf);
2280 :
2281 : // Bypass memory-only entries, those will be reported when iterating the
2282 : // memory only table. Memory-only entries are stored in both ALL_ENTRIES
2283 : // and MEMORY_ONLY hashtables.
2284 0 : RefPtr<mozilla::net::CacheEntry> const& entry = iter2.Data();
2285 0 : if (table->Type() == CacheEntryTable::MEMORY_ONLY ||
2286 0 : entry->IsUsingDisk()) {
2287 0 : size += entry->SizeOfIncludingThis(mallocSizeOf);
2288 : }
2289 : }
2290 :
2291 : // These key names are not privacy-sensitive.
2292 0 : aHandleReport->Callback(
2293 0 : EmptyCString(),
2294 0 : nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
2295 0 : table->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
2296 0 : iter1.Key().BeginReading()),
2297 : nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, size,
2298 0 : NS_LITERAL_CSTRING("Memory used by the cache storage."),
2299 0 : aData);
2300 : }
2301 : }
2302 :
2303 0 : return NS_OK;
2304 : }
2305 :
2306 : // nsICacheTesting
2307 :
2308 : NS_IMETHODIMP
2309 0 : CacheStorageService::IOThreadSuspender::Run()
2310 : {
2311 0 : MonitorAutoLock mon(mMon);
2312 0 : while (!mSignaled) {
2313 0 : mon.Wait();
2314 : }
2315 0 : return NS_OK;
2316 : }
2317 :
2318 : void
2319 0 : CacheStorageService::IOThreadSuspender::Notify()
2320 : {
2321 0 : MonitorAutoLock mon(mMon);
2322 0 : mSignaled = true;
2323 0 : mon.Notify();
2324 0 : }
2325 :
2326 : NS_IMETHODIMP
2327 0 : CacheStorageService::SuspendCacheIOThread(uint32_t aLevel)
2328 : {
2329 0 : RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2330 0 : if (!thread) {
2331 0 : return NS_ERROR_NOT_AVAILABLE;
2332 : }
2333 :
2334 0 : MOZ_ASSERT(!mActiveIOSuspender);
2335 0 : mActiveIOSuspender = new IOThreadSuspender();
2336 0 : return thread->Dispatch(mActiveIOSuspender, aLevel);
2337 : }
2338 :
2339 : NS_IMETHODIMP
2340 0 : CacheStorageService::ResumeCacheIOThread()
2341 : {
2342 0 : MOZ_ASSERT(mActiveIOSuspender);
2343 :
2344 0 : RefPtr<IOThreadSuspender> suspender;
2345 0 : suspender.swap(mActiveIOSuspender);
2346 0 : suspender->Notify();
2347 0 : return NS_OK;
2348 : }
2349 :
2350 : NS_IMETHODIMP
2351 0 : CacheStorageService::Flush(nsIObserver* aObserver)
2352 : {
2353 0 : RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2354 0 : if (!thread) {
2355 0 : return NS_ERROR_NOT_AVAILABLE;
2356 : }
2357 :
2358 : nsCOMPtr<nsIObserverService> observerService =
2359 0 : mozilla::services::GetObserverService();
2360 0 : if (!observerService) {
2361 0 : return NS_ERROR_NOT_AVAILABLE;
2362 : }
2363 :
2364 : // Adding as weak, the consumer is responsible to keep the reference
2365 : // until notified.
2366 0 : observerService->AddObserver(aObserver, "cacheservice:purge-memory-pools", false);
2367 :
2368 : // This runnable will do the purging and when done, notifies the above observer.
2369 : // We dispatch it to the CLOSE level, so all data writes scheduled up to this time
2370 : // will be done before this purging happens.
2371 : RefPtr<CacheStorageService::PurgeFromMemoryRunnable> r =
2372 0 : new CacheStorageService::PurgeFromMemoryRunnable(this, CacheEntry::PURGE_WHOLE);
2373 :
2374 0 : return thread->Dispatch(r, CacheIOThread::WRITE);
2375 : }
2376 :
2377 : } // namespace net
2378 : } // namespace mozilla
|