12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "CacheLog.h"
- #include "CacheStorageService.h"
- #include "CacheFileIOManager.h"
- #include "CacheObserver.h"
- #include "CacheIndex.h"
- #include "CacheIndexIterator.h"
- #include "CacheStorage.h"
- #include "AppCacheStorage.h"
- #include "CacheEntry.h"
- #include "CacheFileUtils.h"
- #include "OldWrappers.h"
- #include "nsCacheService.h"
- #include "nsDeleteDir.h"
- #include "nsICacheStorageVisitor.h"
- #include "nsIObserverService.h"
- #include "nsIFile.h"
- #include "nsIURI.h"
- #include "nsCOMPtr.h"
- #include "nsAutoPtr.h"
- #include "nsNetCID.h"
- #include "nsNetUtil.h"
- #include "nsServiceManagerUtils.h"
- #include "nsWeakReference.h"
- #include "mozilla/TimeStamp.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/Services.h"
- namespace mozilla {
- namespace net {
- namespace {
- void AppendMemoryStorageID(nsAutoCString &key)
- {
- key.Append('/');
- key.Append('M');
- }
- } // namespace
- // Not defining as static or class member of CacheStorageService since
- // it would otherwise need to include CacheEntry.h and that then would
- // need to be exported to make nsNetModule.cpp compilable.
- typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable>
- GlobalEntryTables;
- /**
- * Keeps tables of entries. There is one entries table for each distinct load
- * context type. The distinction is based on following load context info states:
- * <isPrivate|isAnon|appId|inIsolatedMozBrowser> which builds a mapping key.
- *
- * Thread-safe to access, protected by the service mutex.
- */
- static GlobalEntryTables* sGlobalEntryTables;
- CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags)
- : mReportedMemoryConsumption(0)
- , mFlags(aFlags)
- {
- }
- void
- CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
- {
- if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) {
- CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
- }
- }
- CacheStorageService::MemoryPool::MemoryPool(EType aType)
- : mType(aType)
- , mMemorySize(0)
- {
- }
- CacheStorageService::MemoryPool::~MemoryPool()
- {
- if (mMemorySize != 0) {
- NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?");
- }
- }
- uint32_t
- CacheStorageService::MemoryPool::Limit() const
- {
- switch (mType) {
- case DISK:
- return CacheObserver::MetadataMemoryLimit();
- case MEMORY:
- return CacheObserver::MemoryCacheCapacity();
- }
- MOZ_CRASH("Bad pool type");
- return 0;
- }
- NS_IMPL_ISUPPORTS(CacheStorageService,
- nsICacheStorageService,
- nsIMemoryReporter,
- nsITimerCallback,
- nsICacheTesting)
- CacheStorageService* CacheStorageService::sSelf = nullptr;
- CacheStorageService::CacheStorageService()
- : mLock("CacheStorageService.mLock")
- , mForcedValidEntriesLock("CacheStorageService.mForcedValidEntriesLock")
- , mShutdown(false)
- , mDiskPool(MemoryPool::DISK)
- , mMemoryPool(MemoryPool::MEMORY)
- {
- CacheFileIOManager::Init();
- MOZ_ASSERT(!sSelf);
- sSelf = this;
- sGlobalEntryTables = new GlobalEntryTables();
- RegisterStrongMemoryReporter(this);
- }
- CacheStorageService::~CacheStorageService()
- {
- LOG(("CacheStorageService::~CacheStorageService"));
- sSelf = nullptr;
- }
- void CacheStorageService::Shutdown()
- {
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown)
- return;
- LOG(("CacheStorageService::Shutdown - start"));
- mShutdown = true;
- nsCOMPtr<nsIRunnable> event =
- NewRunnableMethod(this, &CacheStorageService::ShutdownBackground);
- Dispatch(event);
- #ifdef NS_FREE_PERMANENT_DATA
- sGlobalEntryTables->Clear();
- delete sGlobalEntryTables;
- #endif
- sGlobalEntryTables = nullptr;
- LOG(("CacheStorageService::Shutdown - done"));
- }
- void CacheStorageService::ShutdownBackground()
- {
- LOG(("CacheStorageService::ShutdownBackground - start"));
- MOZ_ASSERT(IsOnManagementThread());
- {
- mozilla::MutexAutoLock lock(mLock);
- // Cancel purge timer to avoid leaking.
- if (mPurgeTimer) {
- LOG((" freeing the timer"));
- mPurgeTimer->Cancel();
- }
- }
- #ifdef NS_FREE_PERMANENT_DATA
- Pool(false).mFrecencyArray.Clear();
- Pool(false).mExpirationArray.Clear();
- Pool(true).mFrecencyArray.Clear();
- Pool(true).mExpirationArray.Clear();
- #endif
- LOG(("CacheStorageService::ShutdownBackground - done"));
- }
- // Internal management methods
- namespace {
- // WalkCacheRunnable
- // Base class for particular storage entries visiting
- class WalkCacheRunnable : public Runnable
- , public CacheStorageService::EntryInfoCallback
- {
- protected:
- WalkCacheRunnable(nsICacheStorageVisitor* aVisitor,
- bool aVisitEntries)
- : mService(CacheStorageService::Self())
- , mCallback(aVisitor)
- , mSize(0)
- , mNotifyStorage(true)
- , mVisitEntries(aVisitEntries)
- , mCancel(false)
- {
- MOZ_ASSERT(NS_IsMainThread());
- }
- virtual ~WalkCacheRunnable()
- {
- if (mCallback) {
- ProxyReleaseMainThread(mCallback);
- }
- }
- RefPtr<CacheStorageService> mService;
- nsCOMPtr<nsICacheStorageVisitor> mCallback;
- uint64_t mSize;
- bool mNotifyStorage : 1;
- bool mVisitEntries : 1;
- Atomic<bool> mCancel;
- };
- // WalkMemoryCacheRunnable
- // Responsible to visit memory storage and walk
- // all entries on it asynchronously.
- class WalkMemoryCacheRunnable : public WalkCacheRunnable
- {
- public:
- WalkMemoryCacheRunnable(nsILoadContextInfo *aLoadInfo,
- bool aVisitEntries,
- nsICacheStorageVisitor* aVisitor)
- : WalkCacheRunnable(aVisitor, aVisitEntries)
- {
- CacheFileUtils::AppendKeyPrefix(aLoadInfo, mContextKey);
- MOZ_ASSERT(NS_IsMainThread());
- }
- nsresult Walk()
- {
- return mService->Dispatch(this);
- }
- private:
- NS_IMETHOD Run() override
- {
- if (CacheStorageService::IsOnManagementThread()) {
- LOG(("WalkMemoryCacheRunnable::Run - collecting [this=%p]", this));
- // First, walk, count and grab all entries from the storage
- mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock());
- if (!CacheStorageService::IsRunning())
- return NS_ERROR_NOT_INITIALIZED;
- CacheEntryTable* entries;
- if (sGlobalEntryTables->Get(mContextKey, &entries)) {
- for (auto iter = entries->Iter(); !iter.Done(); iter.Next()) {
- CacheEntry* entry = iter.UserData();
- // Ignore disk entries
- if (entry->IsUsingDisk()) {
- continue;
- }
- mSize += entry->GetMetadataMemoryConsumption();
- int64_t size;
- if (NS_SUCCEEDED(entry->GetDataSize(&size))) {
- mSize += size;
- }
- mEntryArray.AppendElement(entry);
- }
- }
- // Next, we dispatch to the main thread
- } else if (NS_IsMainThread()) {
- LOG(("WalkMemoryCacheRunnable::Run - notifying [this=%p]", this));
- if (mNotifyStorage) {
- LOG((" storage"));
- // Second, notify overall storage info
- mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize,
- CacheObserver::MemoryCacheCapacity(), nullptr);
- if (!mVisitEntries)
- return NS_OK; // done
- mNotifyStorage = false;
- } else {
- LOG((" entry [left=%d, canceled=%d]", mEntryArray.Length(), (bool)mCancel));
- // Third, notify each entry until depleted or canceled
- if (!mEntryArray.Length() || mCancel) {
- mCallback->OnCacheEntryVisitCompleted();
- return NS_OK; // done
- }
- // Grab the next entry
- RefPtr<CacheEntry> entry = mEntryArray[0];
- mEntryArray.RemoveElementAt(0);
- // Invokes this->OnEntryInfo, that calls the callback with all
- // information of the entry.
- CacheStorageService::GetCacheEntryInfo(entry, this);
- }
- } else {
- MOZ_CRASH("Bad thread");
- return NS_ERROR_FAILURE;
- }
- NS_DispatchToMainThread(this);
- return NS_OK;
- }
- virtual ~WalkMemoryCacheRunnable()
- {
- if (mCallback)
- ProxyReleaseMainThread(mCallback);
- }
- virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
- int64_t aDataSize, int32_t aFetchCount,
- uint32_t aLastModifiedTime, uint32_t aExpirationTime,
- bool aPinned) override
- {
- nsresult rv;
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), aURISpec);
- if (NS_FAILED(rv)) {
- return;
- }
- rv = mCallback->OnCacheEntryInfo(uri, aIdEnhance, aDataSize, aFetchCount,
- aLastModifiedTime, aExpirationTime, aPinned);
- if (NS_FAILED(rv)) {
- LOG((" callback failed, canceling the walk"));
- mCancel = true;
- }
- }
- private:
- nsCString mContextKey;
- nsTArray<RefPtr<CacheEntry> > mEntryArray;
- };
- // WalkDiskCacheRunnable
- // Using the cache index information to get the list of files per context.
- class WalkDiskCacheRunnable : public WalkCacheRunnable
- {
- public:
- WalkDiskCacheRunnable(nsILoadContextInfo *aLoadInfo,
- bool aVisitEntries,
- nsICacheStorageVisitor* aVisitor)
- : WalkCacheRunnable(aVisitor, aVisitEntries)
- , mLoadInfo(aLoadInfo)
- , mPass(COLLECT_STATS)
- {
- }
- nsresult Walk()
- {
- // TODO, bug 998693
- // Initial index build should be forced here so that about:cache soon
- // after startup gives some meaningfull results.
- // Dispatch to the INDEX level in hope that very recent cache entries
- // information gets to the index list before we grab the index iterator
- // for the first time. This tries to avoid miss of entries that has
- // been created right before the visit is required.
- RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
- NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
- return thread->Dispatch(this, CacheIOThread::INDEX);
- }
- private:
- // Invokes OnCacheEntryInfo callback for each single found entry.
- // There is one instance of this class per one entry.
- class OnCacheEntryInfoRunnable : public Runnable
- {
- public:
- explicit OnCacheEntryInfoRunnable(WalkDiskCacheRunnable* aWalker)
- : mWalker(aWalker)
- {
- }
- NS_IMETHOD Run() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsresult rv;
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), mURISpec);
- if (NS_FAILED(rv)) {
- return NS_OK;
- }
- rv = mWalker->mCallback->OnCacheEntryInfo(
- uri, mIdEnhance, mDataSize, mFetchCount,
- mLastModifiedTime, mExpirationTime, mPinned);
- if (NS_FAILED(rv)) {
- mWalker->mCancel = true;
- }
- return NS_OK;
- }
- RefPtr<WalkDiskCacheRunnable> mWalker;
- nsCString mURISpec;
- nsCString mIdEnhance;
- int64_t mDataSize;
- int32_t mFetchCount;
- uint32_t mLastModifiedTime;
- uint32_t mExpirationTime;
- bool mPinned;
- };
- NS_IMETHOD Run() override
- {
- // The main loop
- nsresult rv;
- if (CacheStorageService::IsOnManagementThread()) {
- switch (mPass) {
- case COLLECT_STATS:
- // Get quickly the cache stats.
- uint32_t size;
- rv = CacheIndex::GetCacheStats(mLoadInfo, &size, &mCount);
- if (NS_FAILED(rv)) {
- if (mVisitEntries) {
- // both onStorageInfo and onCompleted are expected
- NS_DispatchToMainThread(this);
- }
- return NS_DispatchToMainThread(this);
- }
- mSize = size << 10;
- // Invoke onCacheStorageInfo with valid information.
- NS_DispatchToMainThread(this);
- if (!mVisitEntries) {
- return NS_OK; // done
- }
- mPass = ITERATE_METADATA;
- MOZ_FALLTHROUGH;
- case ITERATE_METADATA:
- // Now grab the context iterator.
- if (!mIter) {
- rv = CacheIndex::GetIterator(mLoadInfo, true, getter_AddRefs(mIter));
- if (NS_FAILED(rv)) {
- // Invoke onCacheEntryVisitCompleted now
- return NS_DispatchToMainThread(this);
- }
- }
- while (!mCancel && !CacheObserver::ShuttingDown()) {
- if (CacheIOThread::YieldAndRerun())
- return NS_OK;
- SHA1Sum::Hash hash;
- rv = mIter->GetNextHash(&hash);
- if (NS_FAILED(rv))
- break; // done (or error?)
- // This synchronously invokes OnEntryInfo on this class where we
- // redispatch to the main thread for the consumer callback.
- CacheFileIOManager::GetEntryInfo(&hash, this);
- }
- // Invoke onCacheEntryVisitCompleted on the main thread
- NS_DispatchToMainThread(this);
- }
- } else if (NS_IsMainThread()) {
- if (mNotifyStorage) {
- nsCOMPtr<nsIFile> dir;
- CacheFileIOManager::GetCacheDirectory(getter_AddRefs(dir));
- mCallback->OnCacheStorageInfo(mCount, mSize, CacheObserver::DiskCacheCapacity(), dir);
- mNotifyStorage = false;
- } else {
- mCallback->OnCacheEntryVisitCompleted();
- }
- } else {
- MOZ_CRASH("Bad thread");
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
- int64_t aDataSize, int32_t aFetchCount,
- uint32_t aLastModifiedTime, uint32_t aExpirationTime,
- bool aPinned) override
- {
- // Called directly from CacheFileIOManager::GetEntryInfo.
- // Invoke onCacheEntryInfo on the main thread for this entry.
- RefPtr<OnCacheEntryInfoRunnable> info = new OnCacheEntryInfoRunnable(this);
- info->mURISpec = aURISpec;
- info->mIdEnhance = aIdEnhance;
- info->mDataSize = aDataSize;
- info->mFetchCount = aFetchCount;
- info->mLastModifiedTime = aLastModifiedTime;
- info->mExpirationTime = aExpirationTime;
- info->mPinned = aPinned;
- NS_DispatchToMainThread(info);
- }
- RefPtr<nsILoadContextInfo> mLoadInfo;
- enum {
- // First, we collect stats for the load context.
- COLLECT_STATS,
- // Second, if demanded, we iterate over the entries gethered
- // from the iterator and call CacheFileIOManager::GetEntryInfo
- // for each found entry.
- ITERATE_METADATA,
- } mPass;
- RefPtr<CacheIndexIterator> mIter;
- uint32_t mCount;
- };
- } // namespace
- void CacheStorageService::DropPrivateBrowsingEntries()
- {
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown)
- return;
- nsTArray<nsCString> keys;
- for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
- const nsACString& key = iter.Key();
- nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(key);
- if (info && info->IsPrivate()) {
- keys.AppendElement(key);
- }
- }
- for (uint32_t i = 0; i < keys.Length(); ++i) {
- DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
- }
- }
- namespace {
- class CleaupCacheDirectoriesRunnable : public Runnable
- {
- public:
- NS_DECL_NSIRUNNABLE
- static bool Post(uint32_t aVersion, uint32_t aActive);
- private:
- CleaupCacheDirectoriesRunnable(uint32_t aVersion, uint32_t aActive)
- : mVersion(aVersion), mActive(aActive)
- {
- nsCacheService::GetDiskCacheDirectory(getter_AddRefs(mCache1Dir));
- CacheFileIOManager::GetCacheDirectory(getter_AddRefs(mCache2Dir));
- }
- virtual ~CleaupCacheDirectoriesRunnable() {}
- uint32_t mVersion, mActive;
- nsCOMPtr<nsIFile> mCache1Dir, mCache2Dir;
- };
- // static
- bool CleaupCacheDirectoriesRunnable::Post(uint32_t aVersion, uint32_t aActive)
- {
- // CleaupCacheDirectories is called regardless what cache version is set up to use.
- // To obtain the cache1 directory we must unfortunatelly instantiate the old cache
- // service despite it may not be used at all... This also initialize nsDeleteDir.
- nsCOMPtr<nsICacheService> service = do_GetService(NS_CACHESERVICE_CONTRACTID);
- if (!service)
- return false;
- nsCOMPtr<nsIEventTarget> thread;
- service->GetCacheIOTarget(getter_AddRefs(thread));
- if (!thread)
- return false;
- RefPtr<CleaupCacheDirectoriesRunnable> r =
- new CleaupCacheDirectoriesRunnable(aVersion, aActive);
- thread->Dispatch(r, NS_DISPATCH_NORMAL);
- return true;
- }
- NS_IMETHODIMP CleaupCacheDirectoriesRunnable::Run()
- {
- MOZ_ASSERT(!NS_IsMainThread());
- if (mCache1Dir) {
- nsDeleteDir::RemoveOldTrashes(mCache1Dir);
- }
- if (mCache2Dir) {
- nsDeleteDir::RemoveOldTrashes(mCache2Dir);
- }
- // Delete the non-active version cache data right now
- if (mVersion == mActive) {
- return NS_OK;
- }
- switch (mVersion) {
- case 0:
- if (mCache1Dir) {
- nsDeleteDir::DeleteDir(mCache1Dir, true, 30000);
- }
- break;
- case 1:
- if (mCache2Dir) {
- nsDeleteDir::DeleteDir(mCache2Dir, true, 30000);
- }
- break;
- }
- return NS_OK;
- }
- } // namespace
- // static
- void CacheStorageService::CleaupCacheDirectories(uint32_t aVersion, uint32_t aActive)
- {
- // Make sure we schedule just once in case CleaupCacheDirectories gets called
- // multiple times from some reason.
- static bool runOnce = CleaupCacheDirectoriesRunnable::Post(aVersion, aActive);
- if (!runOnce) {
- NS_WARNING("Could not start cache trashes cleanup");
- }
- }
- // Helper methods
- // static
- bool CacheStorageService::IsOnManagementThread()
- {
- RefPtr<CacheStorageService> service = Self();
- if (!service)
- return false;
- nsCOMPtr<nsIEventTarget> target = service->Thread();
- if (!target)
- return false;
- bool currentThread;
- nsresult rv = target->IsOnCurrentThread(¤tThread);
- return NS_SUCCEEDED(rv) && currentThread;
- }
- already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const
- {
- return CacheFileIOManager::IOTarget();
- }
- nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent)
- {
- RefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread();
- if (!cacheIOThread)
- return NS_ERROR_NOT_AVAILABLE;
- return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT);
- }
- // nsICacheStorageService
- NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo,
- nsICacheStorage * *_retval)
- {
- NS_ENSURE_ARG(aLoadContextInfo);
- NS_ENSURE_ARG(_retval);
- nsCOMPtr<nsICacheStorage> storage;
- if (CacheObserver::UseNewCache()) {
- storage = new CacheStorage(aLoadContextInfo, false, false, false, false);
- }
- else {
- storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
- }
- storage.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo,
- bool aLookupAppCache,
- nsICacheStorage * *_retval)
- {
- NS_ENSURE_ARG(aLoadContextInfo);
- NS_ENSURE_ARG(_retval);
- // TODO save some heap granularity - cache commonly used storages.
- // When disk cache is disabled, still provide a storage, but just keep stuff
- // in memory.
- bool useDisk = CacheObserver::UseDiskCache();
- nsCOMPtr<nsICacheStorage> storage;
- if (CacheObserver::UseNewCache()) {
- storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false /* size limit */, false /* don't pin */);
- }
- else {
- storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr);
- }
- storage.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo,
- nsICacheStorage * *_retval)
- {
- NS_ENSURE_ARG(aLoadContextInfo);
- NS_ENSURE_ARG(_retval);
- if (!CacheObserver::UseNewCache()) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- // When disk cache is disabled don't pretend we cache.
- if (!CacheObserver::UseDiskCache()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
- aLoadContextInfo, true /* use disk */, false /* no appcache */, true /* ignore size checks */, true /* pin */);
- storage.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
- nsIApplicationCache *aApplicationCache,
- nsICacheStorage * *_retval)
- {
- NS_ENSURE_ARG(aLoadContextInfo);
- NS_ENSURE_ARG(_retval);
- nsCOMPtr<nsICacheStorage> storage;
- if (CacheObserver::UseNewCache()) {
- // Using classification since cl believes we want to instantiate this method
- // having the same name as the desired class...
- storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache);
- }
- else {
- storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
- }
- storage.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo,
- nsICacheStorage * *_retval)
- {
- NS_ENSURE_ARG(aLoadContextInfo);
- NS_ENSURE_ARG(_retval);
- nsCOMPtr<nsICacheStorage> storage;
- if (CacheObserver::UseNewCache()) {
- storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */, false /* no pinning */);
- }
- else {
- storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
- }
- storage.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::Clear()
- {
- nsresult rv;
- if (CacheObserver::UseNewCache()) {
- // Tell the index to block notification to AsyncGetDiskConsumption.
- // Will be allowed again from CacheFileContextEvictor::EvictEntries()
- // when all the context have been removed from disk.
- CacheIndex::OnAsyncEviction(true);
- {
- mozilla::MutexAutoLock lock(mLock);
- {
- mozilla::MutexAutoLock forcedValidEntriesLock(mForcedValidEntriesLock);
- mForcedValidEntries.Clear();
- }
- NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
- nsTArray<nsCString> keys;
- for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
- keys.AppendElement(iter.Key());
- }
- for (uint32_t i = 0; i < keys.Length(); ++i) {
- DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
- }
- // Passing null as a load info means to evict all contexts.
- // EvictByContext() respects the entry pinning. EvictAll() does not.
- rv = CacheFileIOManager::EvictByContext(nullptr, false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- } else {
- nsCOMPtr<nsICacheService> serv =
- do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = serv->EvictEntries(nsICache::STORE_ANYWHERE);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
- {
- uint32_t what;
- switch (aWhat) {
- case PURGE_DISK_DATA_ONLY:
- what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED;
- break;
- case PURGE_DISK_ALL:
- what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED;
- break;
- case PURGE_EVERYTHING:
- what = CacheEntry::PURGE_WHOLE;
- break;
- default:
- return NS_ERROR_INVALID_ARG;
- }
- nsCOMPtr<nsIRunnable> event =
- new PurgeFromMemoryRunnable(this, what);
- return Dispatch(event);
- }
- NS_IMETHODIMP CacheStorageService::PurgeFromMemoryRunnable::Run()
- {
- if (NS_IsMainThread()) {
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (observerService) {
- observerService->NotifyObservers(nullptr, "cacheservice:purge-memory-pools", nullptr);
- }
- return NS_OK;
- }
- if (mService) {
- // TODO not all flags apply to both pools
- mService->Pool(true).PurgeAll(mWhat);
- mService->Pool(false).PurgeAll(mWhat);
- mService = nullptr;
- }
- NS_DispatchToMainThread(this);
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
- nsICacheStorageConsumptionObserver* aObserver)
- {
- NS_ENSURE_ARG(aObserver);
- nsresult rv;
- if (CacheObserver::UseNewCache()) {
- rv = CacheIndex::AsyncGetDiskConsumption(aObserver);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- rv = _OldGetDiskConsumption::Get(aObserver);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget)
- {
- NS_ENSURE_ARG(aEventTarget);
- if (CacheObserver::UseNewCache()) {
- nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
- ioTarget.forget(aEventTarget);
- }
- else {
- nsresult rv;
- nsCOMPtr<nsICacheService> serv =
- do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = serv->GetCacheIOTarget(aEventTarget);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- // Methods used by CacheEntry for management of in-memory structures.
- namespace {
- class FrecencyComparator
- {
- public:
- bool Equals(CacheEntry* a, CacheEntry* b) const {
- return a->GetFrecency() == b->GetFrecency();
- }
- bool LessThan(CacheEntry* a, CacheEntry* b) const {
- return a->GetFrecency() < b->GetFrecency();
- }
- };
- class ExpirationComparator
- {
- public:
- bool Equals(CacheEntry* a, CacheEntry* b) const {
- return a->GetExpirationTime() == b->GetExpirationTime();
- }
- bool LessThan(CacheEntry* a, CacheEntry* b) const {
- return a->GetExpirationTime() < b->GetExpirationTime();
- }
- };
- } // namespace
- void
- CacheStorageService::RegisterEntry(CacheEntry* aEntry)
- {
- MOZ_ASSERT(IsOnManagementThread());
- if (mShutdown || !aEntry->CanRegister())
- return;
- LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry));
- MemoryPool& pool = Pool(aEntry->IsUsingDisk());
- pool.mFrecencyArray.AppendElement(aEntry);
- pool.mExpirationArray.AppendElement(aEntry);
- aEntry->SetRegistered(true);
- }
- void
- CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
- {
- MOZ_ASSERT(IsOnManagementThread());
- if (!aEntry->IsRegistered())
- return;
- LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry));
- MemoryPool& pool = Pool(aEntry->IsUsingDisk());
- mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry);
- mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry);
- MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration));
- // Note: aEntry->CanRegister() since now returns false
- aEntry->SetRegistered(false);
- }
- static bool
- AddExactEntry(CacheEntryTable* aEntries,
- nsACString const& aKey,
- CacheEntry* aEntry,
- bool aOverwrite)
- {
- RefPtr<CacheEntry> existingEntry;
- if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
- bool equals = existingEntry == aEntry;
- LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals));
- return equals; // Already there...
- }
- LOG(("AddExactEntry [entry=%p put]", aEntry));
- aEntries->Put(aKey, aEntry);
- return true;
- }
- static bool
- RemoveExactEntry(CacheEntryTable* aEntries,
- nsACString const& aKey,
- CacheEntry* aEntry,
- bool aOverwrite)
- {
- RefPtr<CacheEntry> existingEntry;
- if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
- LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
- return false; // Already removed...
- }
- if (!aOverwrite && existingEntry != aEntry) {
- LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
- return false; // Already replaced...
- }
- LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
- aEntries->Remove(aKey);
- return true;
- }
- bool
- CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
- {
- LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry));
- nsAutoCString entryKey;
- nsresult rv = aEntry->HashingKey(entryKey);
- if (NS_FAILED(rv)) {
- NS_ERROR("aEntry->HashingKey() failed?");
- return false;
- }
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown) {
- LOG((" after shutdown"));
- return false;
- }
- if (aOnlyUnreferenced) {
- if (aEntry->IsReferenced()) {
- LOG((" still referenced, not removing"));
- return false;
- }
- if (!aEntry->IsUsingDisk() && IsForcedValidEntry(aEntry->GetStorageID(), entryKey)) {
- LOG((" forced valid, not removing"));
- return false;
- }
- }
- CacheEntryTable* entries;
- if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
- RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
- nsAutoCString memoryStorageID(aEntry->GetStorageID());
- AppendMemoryStorageID(memoryStorageID);
- if (sGlobalEntryTables->Get(memoryStorageID, &entries))
- RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
- return true;
- }
- void
- CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry,
- bool aOnlyInMemory,
- bool aOverwrite)
- {
- LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]",
- aEntry, aOnlyInMemory, aOverwrite));
- // This method is responsible to put this entry to a special record hashtable
- // that contains only entries that are stored in memory.
- // Keep in mind that every entry, regardless of whether is in-memory-only or not
- // is always recorded in the storage master hash table, the one identified by
- // CacheEntry.StorageID().
- mLock.AssertCurrentThreadOwns();
- if (mShutdown) {
- LOG((" after shutdown"));
- return;
- }
- nsresult rv;
- nsAutoCString entryKey;
- rv = aEntry->HashingKey(entryKey);
- if (NS_FAILED(rv)) {
- NS_ERROR("aEntry->HashingKey() failed?");
- return;
- }
- CacheEntryTable* entries = nullptr;
- nsAutoCString memoryStorageID(aEntry->GetStorageID());
- AppendMemoryStorageID(memoryStorageID);
- if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
- if (!aOnlyInMemory) {
- LOG((" not recorded as memory only"));
- return;
- }
- entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
- sGlobalEntryTables->Put(memoryStorageID, entries);
- LOG((" new memory-only storage table for %s", memoryStorageID.get()));
- }
- if (aOnlyInMemory) {
- AddExactEntry(entries, entryKey, aEntry, aOverwrite);
- }
- else {
- RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
- }
- }
- // Checks if a cache entry is forced valid (will be loaded directly from cache
- // without further validation) - see nsICacheEntry.idl for further details
- bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextKey,
- nsACString const &aEntryKey)
- {
- return IsForcedValidEntry(aContextKey + aEntryKey);
- }
- bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextEntryKey)
- {
- mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
- TimeStamp validUntil;
- if (!mForcedValidEntries.Get(aContextEntryKey, &validUntil)) {
- return false;
- }
- if (validUntil.IsNull()) {
- return false;
- }
- // Entry timeout not reached yet
- if (TimeStamp::NowLoRes() <= validUntil) {
- return true;
- }
- // Entry timeout has been reached
- mForcedValidEntries.Remove(aContextEntryKey);
- return false;
- }
- // Allows a cache entry to be loaded directly from cache without further
- // validation - see nsICacheEntry.idl for further details
- void CacheStorageService::ForceEntryValidFor(nsACString const &aContextKey,
- nsACString const &aEntryKey,
- uint32_t aSecondsToTheFuture)
- {
- mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
- TimeStamp now = TimeStamp::NowLoRes();
- ForcedValidEntriesPrune(now);
- // This will be the timeout
- TimeStamp validUntil = now + TimeDuration::FromSeconds(aSecondsToTheFuture);
- mForcedValidEntries.Put(aContextKey + aEntryKey, validUntil);
- }
- void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey,
- nsACString const &aEntryKey)
- {
- mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
- LOG(("CacheStorageService::RemoveEntryForceValid context='%s' entryKey=%s",
- aContextKey.BeginReading(), aEntryKey.BeginReading()));
- mForcedValidEntries.Remove(aContextKey + aEntryKey);
- }
- // Cleans out the old entries in mForcedValidEntries
- void CacheStorageService::ForcedValidEntriesPrune(TimeStamp &now)
- {
- static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
- static TimeStamp dontPruneUntil = now + oneMinute;
- if (now < dontPruneUntil)
- return;
- for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
- if (iter.Data() < now) {
- iter.Remove();
- }
- }
- dontPruneUntil = now + oneMinute;
- }
- void
- CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer,
- uint32_t aCurrentMemoryConsumption)
- {
- LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]",
- aConsumer, aCurrentMemoryConsumption));
- uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption;
- if (savedMemorySize == aCurrentMemoryConsumption)
- return;
- // Exchange saved size with current one.
- aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption;
- bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY);
- bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange(
- savedMemorySize, aCurrentMemoryConsumption);
- if (!overLimit)
- return;
- // It's likely the timer has already been set when we get here,
- // check outside the lock to save resources.
- if (mPurgeTimer)
- return;
- // We don't know if this is called under the service lock or not,
- // hence rather dispatch.
- RefPtr<nsIEventTarget> cacheIOTarget = Thread();
- if (!cacheIOTarget)
- return;
- // Dispatch as a priority task, we want to set the purge timer
- // ASAP to prevent vain redispatch of this event.
- nsCOMPtr<nsIRunnable> event =
- NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit);
- cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
- }
- bool
- CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize,
- uint32_t aCurrentMemoryConsumption)
- {
- mMemorySize -= aSavedMemorySize;
- mMemorySize += aCurrentMemoryConsumption;
- LOG((" mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize));
- // Bypass purging when memory has not grew up significantly
- if (aCurrentMemoryConsumption <= aSavedMemorySize)
- return false;
- return mMemorySize > Limit();
- }
- void
- CacheStorageService::SchedulePurgeOverMemoryLimit()
- {
- LOG(("CacheStorageService::SchedulePurgeOverMemoryLimit"));
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown) {
- LOG((" past shutdown"));
- return;
- }
- if (mPurgeTimer) {
- LOG((" timer already up"));
- return;
- }
- mPurgeTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
- if (mPurgeTimer) {
- nsresult rv;
- rv = mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
- LOG((" timer init rv=0x%08x", rv));
- }
- }
- NS_IMETHODIMP
- CacheStorageService::Notify(nsITimer* aTimer)
- {
- LOG(("CacheStorageService::Notify"));
- mozilla::MutexAutoLock lock(mLock);
- if (aTimer == mPurgeTimer) {
- mPurgeTimer = nullptr;
- nsCOMPtr<nsIRunnable> event =
- NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit);
- Dispatch(event);
- }
- return NS_OK;
- }
- void
- CacheStorageService::PurgeOverMemoryLimit()
- {
- MOZ_ASSERT(IsOnManagementThread());
- LOG(("CacheStorageService::PurgeOverMemoryLimit"));
- static TimeDuration const kFourSeconds = TimeDuration::FromSeconds(4);
- TimeStamp now = TimeStamp::NowLoRes();
- if (!mLastPurgeTime.IsNull() && now - mLastPurgeTime < kFourSeconds) {
- LOG((" bypassed, too soon"));
- return;
- }
- mLastPurgeTime = now;
- Pool(true).PurgeOverMemoryLimit();
- Pool(false).PurgeOverMemoryLimit();
- }
- void
- CacheStorageService::MemoryPool::PurgeOverMemoryLimit()
- {
- TimeStamp start(TimeStamp::Now());
- uint32_t const memoryLimit = Limit();
- if (mMemorySize > memoryLimit) {
- LOG((" memory data consumption over the limit, abandon expired entries"));
- PurgeExpired();
- }
- bool frecencyNeedsSort = true;
- // No longer makes sense since:
- // Memory entries are never purged partially, only as a whole when the memory
- // cache limit is overreached.
- // Disk entries throw the data away ASAP so that only metadata are kept.
- // TODO when this concept of two separate pools is found working, the code should
- // clean up.
- #if 0
- if (mMemorySize > memoryLimit) {
- LOG((" memory data consumption over the limit, abandon disk backed data"));
- PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED);
- }
- if (mMemorySize > memoryLimit) {
- LOG((" metadata consumtion over the limit, abandon disk backed entries"));
- PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED);
- }
- #endif
- if (mMemorySize > memoryLimit) {
- LOG((" memory data consumption over the limit, abandon any entry"));
- PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
- }
- LOG((" purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
- }
- void
- CacheStorageService::MemoryPool::PurgeExpired()
- {
- MOZ_ASSERT(IsOnManagementThread());
- mExpirationArray.Sort(ExpirationComparator());
- uint32_t now = NowInSeconds();
- uint32_t const memoryLimit = Limit();
- for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
- if (CacheIOThread::YieldAndRerun())
- return;
- RefPtr<CacheEntry> entry = mExpirationArray[i];
- uint32_t expirationTime = entry->GetExpirationTime();
- if (expirationTime > 0 && expirationTime <= now &&
- entry->Purge(CacheEntry::PURGE_WHOLE)) {
- LOG((" purged expired, entry=%p, exptime=%u (now=%u)",
- entry.get(), entry->GetExpirationTime(), now));
- continue;
- }
- // not purged, move to the next one
- ++i;
- }
- }
- void
- CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat)
- {
- MOZ_ASSERT(IsOnManagementThread());
- if (aFrecencyNeedsSort) {
- mFrecencyArray.Sort(FrecencyComparator());
- aFrecencyNeedsSort = false;
- }
- uint32_t const memoryLimit = Limit();
- for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
- if (CacheIOThread::YieldAndRerun())
- return;
- RefPtr<CacheEntry> entry = mFrecencyArray[i];
- if (entry->Purge(aWhat)) {
- LOG((" abandoned (%d), entry=%p, frecency=%1.10f",
- aWhat, entry.get(), entry->GetFrecency()));
- continue;
- }
- // not purged, move to the next one
- ++i;
- }
- }
- void
- CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat)
- {
- LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat));
- MOZ_ASSERT(IsOnManagementThread());
- for (uint32_t i = 0; i < mFrecencyArray.Length();) {
- if (CacheIOThread::YieldAndRerun())
- return;
- RefPtr<CacheEntry> entry = mFrecencyArray[i];
- if (entry->Purge(aWhat)) {
- LOG((" abandoned entry=%p", entry.get()));
- continue;
- }
- // not purged, move to the next one
- ++i;
- }
- }
- // Methods exposed to and used by CacheStorage.
- nsresult
- CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
- const nsACString & aURI,
- const nsACString & aIdExtension,
- bool aReplace,
- CacheEntryHandle** aResult)
- {
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_ARG(aStorage);
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
- return AddStorageEntry(contextKey, aURI, aIdExtension,
- aStorage->WriteToDisk(),
- aStorage->SkipSizeCheck(),
- aStorage->Pinning(),
- aReplace,
- aResult);
- }
- nsresult
- CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
- const nsACString & aURI,
- const nsACString & aIdExtension,
- bool aWriteToDisk,
- bool aSkipSizeCheck,
- bool aPin,
- bool aReplace,
- CacheEntryHandle** aResult)
- {
- nsresult rv;
- nsAutoCString entryKey;
- rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
- NS_ENSURE_SUCCESS(rv, rv);
- LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
- entryKey.get(), aContextKey.BeginReading()));
- RefPtr<CacheEntry> entry;
- RefPtr<CacheEntryHandle> handle;
- {
- mozilla::MutexAutoLock lock(mLock);
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- // Ensure storage table
- CacheEntryTable* entries;
- if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
- entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
- sGlobalEntryTables->Put(aContextKey, entries);
- LOG((" new storage entries table for context '%s'", aContextKey.BeginReading()));
- }
- bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
- if (entryExists && !aReplace) {
- // check whether we want to turn this entry to a memory-only.
- if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
- LOG((" entry is persistent but we want mem-only, replacing it"));
- aReplace = true;
- }
- }
- // If truncate is demanded, delete and doom the current entry
- if (entryExists && aReplace) {
- entries->Remove(entryKey);
- LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
- // On purpose called under the lock to prevent races of doom and open on I/O thread
- // No need to remove from both memory-only and all-entries tables. The new entry
- // will overwrite the shadow entry in its ctor.
- entry->DoomAlreadyRemoved();
- entry = nullptr;
- entryExists = false;
- // Would only lead to deleting force-valid timestamp again. We don't need the
- // replace information anymore after this point anyway.
- aReplace = false;
- }
- // Ensure entry for the particular URL
- if (!entryExists) {
- // When replacing with a new entry, always remove the current force-valid timestamp,
- // this is the only place to do it.
- if (aReplace) {
- RemoveEntryForceValid(aContextKey, entryKey);
- }
- // Entry is not in the hashtable or has just been truncated...
- entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
- entries->Put(entryKey, entry);
- LOG((" new entry %p for %s", entry.get(), entryKey.get()));
- }
- if (entry) {
- // Here, if this entry was not for a long time referenced by any consumer,
- // gets again first 'handles count' reference.
- handle = entry->NewHandle();
- }
- }
- handle.forget(aResult);
- return NS_OK;
- }
- nsresult
- CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage,
- const nsACString & aURI,
- const nsACString & aIdExtension,
- bool* aResult)
- {
- nsresult rv;
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
- if (!aStorage->WriteToDisk()) {
- AppendMemoryStorageID(contextKey);
- }
- LOG(("CacheStorageService::CheckStorageEntry [uri=%s, eid=%s, contextKey=%s]",
- aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
- {
- mozilla::MutexAutoLock lock(mLock);
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- nsAutoCString entryKey;
- rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
- NS_ENSURE_SUCCESS(rv, rv);
- CacheEntryTable* entries;
- if ((*aResult = sGlobalEntryTables->Get(contextKey, &entries)) &&
- entries->GetWeak(entryKey, aResult)) {
- LOG((" found in hash tables"));
- return NS_OK;
- }
- }
- if (!aStorage->WriteToDisk()) {
- // Memory entry, nothing more to do.
- LOG((" not found in hash tables"));
- return NS_OK;
- }
- // Disk entry, not found in the hashtable, check the index.
- nsAutoCString fileKey;
- rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
- CacheIndex::EntryStatus status;
- rv = CacheIndex::HasEntry(fileKey, &status);
- if (NS_FAILED(rv) || status == CacheIndex::DO_NOT_KNOW) {
- LOG((" index doesn't know, rv=0x%08x", rv));
- return NS_ERROR_NOT_AVAILABLE;
- }
- *aResult = status == CacheIndex::EXISTS;
- LOG((" %sfound in index", *aResult ? "" : "not "));
- return NS_OK;
- }
- namespace {
- class CacheEntryDoomByKeyCallback : public CacheFileIOListener
- , public nsIRunnable
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIRUNNABLE
- explicit CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback)
- : mCallback(aCallback) { }
- private:
- virtual ~CacheEntryDoomByKeyCallback();
- NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
- NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) override { return NS_OK; }
- NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override { return NS_OK; }
- NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
- NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
- NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
- nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
- nsresult mResult;
- };
- CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback()
- {
- if (mCallback)
- ProxyReleaseMainThread(mCallback);
- }
- NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
- nsresult aResult)
- {
- if (!mCallback)
- return NS_OK;
- mResult = aResult;
- if (NS_IsMainThread()) {
- Run();
- } else {
- NS_DispatchToMainThread(this);
- }
- return NS_OK;
- }
- NS_IMETHODIMP CacheEntryDoomByKeyCallback::Run()
- {
- mCallback->OnCacheEntryDoomed(mResult);
- return NS_OK;
- }
- NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener, nsIRunnable);
- } // namespace
- nsresult
- CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage,
- const nsACString & aURI,
- const nsACString & aIdExtension,
- nsICacheEntryDoomCallback* aCallback)
- {
- LOG(("CacheStorageService::DoomStorageEntry"));
- NS_ENSURE_ARG(aStorage);
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
- nsAutoCString entryKey;
- nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
- NS_ENSURE_SUCCESS(rv, rv);
- RefPtr<CacheEntry> entry;
- {
- mozilla::MutexAutoLock lock(mLock);
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- CacheEntryTable* entries;
- if (sGlobalEntryTables->Get(contextKey, &entries)) {
- if (entries->Get(entryKey, getter_AddRefs(entry))) {
- if (aStorage->WriteToDisk() || !entry->IsUsingDisk()) {
- // When evicting from disk storage, purge
- // When evicting from memory storage and the entry is memory-only, purge
- LOG((" purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
- entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
- entries->Remove(entryKey);
- }
- else {
- // Otherwise, leave it
- LOG((" leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
- entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
- entry = nullptr;
- }
- }
- }
- if (!entry) {
- RemoveEntryForceValid(contextKey, entryKey);
- }
- }
- if (entry) {
- LOG((" dooming entry %p for %s", entry.get(), entryKey.get()));
- return entry->AsyncDoom(aCallback);
- }
- LOG((" no entry loaded for %s", entryKey.get()));
- if (aStorage->WriteToDisk()) {
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
- rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey);
- NS_ENSURE_SUCCESS(rv, rv);
- LOG((" dooming file only for %s", entryKey.get()));
- RefPtr<CacheEntryDoomByKeyCallback> callback(
- new CacheEntryDoomByKeyCallback(aCallback));
- rv = CacheFileIOManager::DoomFileByKey(entryKey, callback);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- class Callback : public Runnable
- {
- public:
- explicit Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
- NS_IMETHOD Run() override
- {
- mCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
- return NS_OK;
- }
- nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
- };
- if (aCallback) {
- RefPtr<Runnable> callback = new Callback(aCallback);
- return NS_DispatchToMainThread(callback);
- }
- return NS_OK;
- }
- nsresult
- CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
- nsICacheEntryDoomCallback* aCallback)
- {
- LOG(("CacheStorageService::DoomStorageEntries"));
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_ARG(aStorage);
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
- mozilla::MutexAutoLock lock(mLock);
- return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
- aStorage->WriteToDisk(), aStorage->Pinning(),
- aCallback);
- }
- nsresult
- CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
- nsILoadContextInfo* aContext,
- bool aDiskStorage,
- bool aPinned,
- nsICacheEntryDoomCallback* aCallback)
- {
- LOG(("CacheStorageService::DoomStorageEntries [context=%s]", aContextKey.BeginReading()));
- mLock.AssertCurrentThreadOwns();
- NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
- nsAutoCString memoryStorageID(aContextKey);
- AppendMemoryStorageID(memoryStorageID);
- if (aDiskStorage) {
- LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading()));
- // Walk one by one and remove entries according their pin status
- CacheEntryTable *diskEntries, *memoryEntries;
- if (sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
- sGlobalEntryTables->Get(memoryStorageID, &memoryEntries);
- for (auto iter = diskEntries->Iter(); !iter.Done(); iter.Next()) {
- auto entry = iter.Data();
- if (entry->DeferOrBypassRemovalOnPinStatus(aPinned)) {
- continue;
- }
- if (memoryEntries) {
- RemoveExactEntry(memoryEntries, iter.Key(), entry, false);
- }
- iter.Remove();
- }
- }
- if (aContext && !aContext->IsPrivate()) {
- LOG((" dooming disk entries"));
- CacheFileIOManager::EvictByContext(aContext, aPinned);
- }
- } else {
- LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));
- // Remove the memory entries table from the global tables.
- // Since we store memory entries also in the disk entries table
- // we need to remove the memory entries from the disk table one
- // by one manually.
- nsAutoPtr<CacheEntryTable> memoryEntries;
- sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);
- CacheEntryTable* diskEntries;
- if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
- for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
- auto entry = iter.Data();
- RemoveExactEntry(diskEntries, iter.Key(), entry, false);
- }
- }
- }
- {
- mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
- if (aContext) {
- for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
- bool matches;
- DebugOnly<nsresult> rv = CacheFileUtils::KeyMatchesLoadContextInfo(
- iter.Key(), aContext, &matches);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- if (matches) {
- iter.Remove();
- }
- }
- } else {
- mForcedValidEntries.Clear();
- }
- }
- // An artificial callback. This is a candidate for removal tho. In the new
- // cache any 'doom' or 'evict' function ensures that the entry or entries
- // being doomed is/are not accessible after the function returns. So there is
- // probably no need for a callback - has no meaning. But for compatibility
- // with the old cache that is still in the tree we keep the API similar to be
- // able to make tests as well as other consumers work for now.
- class Callback : public Runnable
- {
- public:
- explicit Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
- NS_IMETHOD Run() override
- {
- mCallback->OnCacheEntryDoomed(NS_OK);
- return NS_OK;
- }
- nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
- };
- if (aCallback) {
- RefPtr<Runnable> callback = new Callback(aCallback);
- return NS_DispatchToMainThread(callback);
- }
- return NS_OK;
- }
- nsresult
- CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
- bool aVisitEntries,
- nsICacheStorageVisitor* aVisitor)
- {
- LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries));
- NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_ARG(aStorage);
- if (aStorage->WriteToDisk()) {
- RefPtr<WalkDiskCacheRunnable> event =
- new WalkDiskCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
- return event->Walk();
- }
- RefPtr<WalkMemoryCacheRunnable> event =
- new WalkMemoryCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
- return event->Walk();
- }
- void
- CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
- const nsACString & aIdExtension,
- const nsACString & aURISpec)
- {
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
- nsAutoCString entryKey;
- CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown) {
- return;
- }
- CacheEntryTable* entries;
- RefPtr<CacheEntry> entry;
- if (sGlobalEntryTables->Get(contextKey, &entries) &&
- entries->Get(entryKey, getter_AddRefs(entry))) {
- if (entry->IsFileDoomed()) {
- // Need to remove under the lock to avoid possible race leading
- // to duplication of the entry per its key.
- RemoveExactEntry(entries, entryKey, entry, false);
- entry->DoomAlreadyRemoved();
- }
- // Entry found, but it's not the entry that has been found doomed
- // by the lower eviction layer. Just leave everything unchanged.
- return;
- }
- RemoveEntryForceValid(contextKey, entryKey);
- }
- bool
- CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo,
- const nsACString & aIdExtension,
- const nsACString & aURISpec,
- EntryInfoCallback *aCallback)
- {
- nsAutoCString contextKey;
- CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
- nsAutoCString entryKey;
- CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
- RefPtr<CacheEntry> entry;
- {
- mozilla::MutexAutoLock lock(mLock);
- if (mShutdown) {
- return false;
- }
- CacheEntryTable* entries;
- if (!sGlobalEntryTables->Get(contextKey, &entries)) {
- return false;
- }
- if (!entries->Get(entryKey, getter_AddRefs(entry))) {
- return false;
- }
- }
- GetCacheEntryInfo(entry, aCallback);
- return true;
- }
- // static
- void
- CacheStorageService::GetCacheEntryInfo(CacheEntry* aEntry,
- EntryInfoCallback *aCallback)
- {
- nsCString const uriSpec = aEntry->GetURI();
- nsCString const enhanceId = aEntry->GetEnhanceID();
- uint32_t dataSize;
- if (NS_FAILED(aEntry->GetStorageDataSize(&dataSize))) {
- dataSize = 0;
- }
- int32_t fetchCount;
- if (NS_FAILED(aEntry->GetFetchCount(&fetchCount))) {
- fetchCount = 0;
- }
- uint32_t lastModified;
- if (NS_FAILED(aEntry->GetLastModified(&lastModified))) {
- lastModified = 0;
- }
- uint32_t expirationTime;
- if (NS_FAILED(aEntry->GetExpirationTime(&expirationTime))) {
- expirationTime = 0;
- }
- aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize,
- fetchCount, lastModified, expirationTime,
- aEntry->IsPinned());
- }
- // static
- uint32_t CacheStorageService::CacheQueueSize(bool highPriority)
- {
- RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
- MOZ_ASSERT(thread);
- return thread->QueueSize(highPriority);
- }
- // nsIMemoryReporter
- size_t
- CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
- {
- CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
- size_t n = 0;
- // The elemets are referenced by sGlobalEntryTables and are reported from there
- n += Pool(true).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
- n += Pool(true).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
- n += Pool(false).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
- n += Pool(false).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
- // Entries reported manually in CacheStorageService::CollectReports callback
- if (sGlobalEntryTables) {
- n += sGlobalEntryTables->ShallowSizeOfIncludingThis(mallocSizeOf);
- }
- return n;
- }
- size_t
- CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
- {
- return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
- }
- NS_IMETHODIMP
- CacheStorageService::CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize)
- {
- MOZ_COLLECT_REPORT(
- "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
- CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
- "Memory used by the cache IO manager.");
- MOZ_COLLECT_REPORT(
- "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
- CacheIndex::SizeOfIncludingThis(MallocSizeOf),
- "Memory used by the cache index.");
- MutexAutoLock lock(mLock);
- // Report the service instance, this doesn't report entries, done lower
- MOZ_COLLECT_REPORT(
- "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
- SizeOfIncludingThis(MallocSizeOf),
- "Memory used by the cache storage service.");
- // Report all entries, each storage separately (by the context key)
- //
- // References are:
- // sGlobalEntryTables to N CacheEntryTable
- // CacheEntryTable to N CacheEntry
- // CacheEntry to 1 CacheFile
- // CacheFile to
- // N CacheFileChunk (keeping the actual data)
- // 1 CacheFileMetadata (keeping http headers etc.)
- // 1 CacheFileOutputStream
- // N CacheFileInputStream
- if (sGlobalEntryTables) {
- for (auto iter1 = sGlobalEntryTables->Iter(); !iter1.Done(); iter1.Next()) {
- CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
- CacheEntryTable* table = iter1.UserData();
- size_t size = 0;
- mozilla::MallocSizeOf mallocSizeOf = CacheStorageService::MallocSizeOf;
- size += table->ShallowSizeOfIncludingThis(mallocSizeOf);
- for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
- size += iter2.Key().SizeOfExcludingThisIfUnshared(mallocSizeOf);
- // Bypass memory-only entries, those will be reported when iterating the
- // memory only table. Memory-only entries are stored in both ALL_ENTRIES
- // and MEMORY_ONLY hashtables.
- RefPtr<mozilla::net::CacheEntry> const& entry = iter2.Data();
- if (table->Type() == CacheEntryTable::MEMORY_ONLY ||
- entry->IsUsingDisk()) {
- size += entry->SizeOfIncludingThis(mallocSizeOf);
- }
- }
- // These key names are not privacy-sensitive.
- aHandleReport->Callback(
- EmptyCString(),
- nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
- table->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
- iter1.Key().BeginReading()),
- nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, size,
- NS_LITERAL_CSTRING("Memory used by the cache storage."),
- aData);
- }
- }
- return NS_OK;
- }
- // nsICacheTesting
- NS_IMETHODIMP
- CacheStorageService::IOThreadSuspender::Run()
- {
- MonitorAutoLock mon(mMon);
- while (!mSignaled) {
- mon.Wait();
- }
- return NS_OK;
- }
- void
- CacheStorageService::IOThreadSuspender::Notify()
- {
- MonitorAutoLock mon(mMon);
- mSignaled = true;
- mon.Notify();
- }
- NS_IMETHODIMP
- CacheStorageService::SuspendCacheIOThread(uint32_t aLevel)
- {
- RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
- if (!thread) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- MOZ_ASSERT(!mActiveIOSuspender);
- mActiveIOSuspender = new IOThreadSuspender();
- return thread->Dispatch(mActiveIOSuspender, aLevel);
- }
- NS_IMETHODIMP
- CacheStorageService::ResumeCacheIOThread()
- {
- MOZ_ASSERT(mActiveIOSuspender);
- RefPtr<IOThreadSuspender> suspender;
- suspender.swap(mActiveIOSuspender);
- suspender->Notify();
- return NS_OK;
- }
- NS_IMETHODIMP
- CacheStorageService::Flush(nsIObserver* aObserver)
- {
- RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
- if (!thread) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (!observerService) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- // Adding as weak, the consumer is responsible to keep the reference
- // until notified.
- observerService->AddObserver(aObserver, "cacheservice:purge-memory-pools", false);
- // This runnable will do the purging and when done, notifies the above observer.
- // We dispatch it to the CLOSE level, so all data writes scheduled up to this time
- // will be done before this purging happens.
- RefPtr<CacheStorageService::PurgeFromMemoryRunnable> r =
- new CacheStorageService::PurgeFromMemoryRunnable(this, CacheEntry::PURGE_WHOLE);
- return thread->Dispatch(r, CacheIOThread::WRITE);
- }
- } // namespace net
- } // namespace mozilla
|