CacheStorageService.cpp 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "CacheLog.h"
  6. #include "CacheStorageService.h"
  7. #include "CacheFileIOManager.h"
  8. #include "CacheObserver.h"
  9. #include "CacheIndex.h"
  10. #include "CacheIndexIterator.h"
  11. #include "CacheStorage.h"
  12. #include "AppCacheStorage.h"
  13. #include "CacheEntry.h"
  14. #include "CacheFileUtils.h"
  15. #include "OldWrappers.h"
  16. #include "nsCacheService.h"
  17. #include "nsDeleteDir.h"
  18. #include "nsICacheStorageVisitor.h"
  19. #include "nsIObserverService.h"
  20. #include "nsIFile.h"
  21. #include "nsIURI.h"
  22. #include "nsCOMPtr.h"
  23. #include "nsAutoPtr.h"
  24. #include "nsNetCID.h"
  25. #include "nsNetUtil.h"
  26. #include "nsServiceManagerUtils.h"
  27. #include "nsWeakReference.h"
  28. #include "mozilla/TimeStamp.h"
  29. #include "mozilla/DebugOnly.h"
  30. #include "mozilla/Services.h"
  31. namespace mozilla {
  32. namespace net {
  33. namespace {
  34. void AppendMemoryStorageID(nsAutoCString &key)
  35. {
  36. key.Append('/');
  37. key.Append('M');
  38. }
  39. } // namespace
  40. // Not defining as static or class member of CacheStorageService since
  41. // it would otherwise need to include CacheEntry.h and that then would
  42. // need to be exported to make nsNetModule.cpp compilable.
  43. typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable>
  44. GlobalEntryTables;
  45. /**
  46. * Keeps tables of entries. There is one entries table for each distinct load
  47. * context type. The distinction is based on following load context info states:
  48. * <isPrivate|isAnon|appId|inIsolatedMozBrowser> which builds a mapping key.
  49. *
  50. * Thread-safe to access, protected by the service mutex.
  51. */
  52. static GlobalEntryTables* sGlobalEntryTables;
  53. CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags)
  54. : mReportedMemoryConsumption(0)
  55. , mFlags(aFlags)
  56. {
  57. }
  58. void
  59. CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
  60. {
  61. if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) {
  62. CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
  63. }
  64. }
  65. CacheStorageService::MemoryPool::MemoryPool(EType aType)
  66. : mType(aType)
  67. , mMemorySize(0)
  68. {
  69. }
  70. CacheStorageService::MemoryPool::~MemoryPool()
  71. {
  72. if (mMemorySize != 0) {
  73. NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?");
  74. }
  75. }
  76. uint32_t
  77. CacheStorageService::MemoryPool::Limit() const
  78. {
  79. switch (mType) {
  80. case DISK:
  81. return CacheObserver::MetadataMemoryLimit();
  82. case MEMORY:
  83. return CacheObserver::MemoryCacheCapacity();
  84. }
  85. MOZ_CRASH("Bad pool type");
  86. return 0;
  87. }
  88. NS_IMPL_ISUPPORTS(CacheStorageService,
  89. nsICacheStorageService,
  90. nsIMemoryReporter,
  91. nsITimerCallback,
  92. nsICacheTesting)
  93. CacheStorageService* CacheStorageService::sSelf = nullptr;
  94. CacheStorageService::CacheStorageService()
  95. : mLock("CacheStorageService.mLock")
  96. , mForcedValidEntriesLock("CacheStorageService.mForcedValidEntriesLock")
  97. , mShutdown(false)
  98. , mDiskPool(MemoryPool::DISK)
  99. , mMemoryPool(MemoryPool::MEMORY)
  100. {
  101. CacheFileIOManager::Init();
  102. MOZ_ASSERT(!sSelf);
  103. sSelf = this;
  104. sGlobalEntryTables = new GlobalEntryTables();
  105. RegisterStrongMemoryReporter(this);
  106. }
  107. CacheStorageService::~CacheStorageService()
  108. {
  109. LOG(("CacheStorageService::~CacheStorageService"));
  110. sSelf = nullptr;
  111. }
  112. void CacheStorageService::Shutdown()
  113. {
  114. mozilla::MutexAutoLock lock(mLock);
  115. if (mShutdown)
  116. return;
  117. LOG(("CacheStorageService::Shutdown - start"));
  118. mShutdown = true;
  119. nsCOMPtr<nsIRunnable> event =
  120. NewRunnableMethod(this, &CacheStorageService::ShutdownBackground);
  121. Dispatch(event);
  122. #ifdef NS_FREE_PERMANENT_DATA
  123. sGlobalEntryTables->Clear();
  124. delete sGlobalEntryTables;
  125. #endif
  126. sGlobalEntryTables = nullptr;
  127. LOG(("CacheStorageService::Shutdown - done"));
  128. }
  129. void CacheStorageService::ShutdownBackground()
  130. {
  131. LOG(("CacheStorageService::ShutdownBackground - start"));
  132. MOZ_ASSERT(IsOnManagementThread());
  133. {
  134. mozilla::MutexAutoLock lock(mLock);
  135. // Cancel purge timer to avoid leaking.
  136. if (mPurgeTimer) {
  137. LOG((" freeing the timer"));
  138. mPurgeTimer->Cancel();
  139. }
  140. }
  141. #ifdef NS_FREE_PERMANENT_DATA
  142. Pool(false).mFrecencyArray.Clear();
  143. Pool(false).mExpirationArray.Clear();
  144. Pool(true).mFrecencyArray.Clear();
  145. Pool(true).mExpirationArray.Clear();
  146. #endif
  147. LOG(("CacheStorageService::ShutdownBackground - done"));
  148. }
  149. // Internal management methods
  150. namespace {
  151. // WalkCacheRunnable
  152. // Base class for particular storage entries visiting
  153. class WalkCacheRunnable : public Runnable
  154. , public CacheStorageService::EntryInfoCallback
  155. {
  156. protected:
  157. WalkCacheRunnable(nsICacheStorageVisitor* aVisitor,
  158. bool aVisitEntries)
  159. : mService(CacheStorageService::Self())
  160. , mCallback(aVisitor)
  161. , mSize(0)
  162. , mNotifyStorage(true)
  163. , mVisitEntries(aVisitEntries)
  164. , mCancel(false)
  165. {
  166. MOZ_ASSERT(NS_IsMainThread());
  167. }
  168. virtual ~WalkCacheRunnable()
  169. {
  170. if (mCallback) {
  171. ProxyReleaseMainThread(mCallback);
  172. }
  173. }
  174. RefPtr<CacheStorageService> mService;
  175. nsCOMPtr<nsICacheStorageVisitor> mCallback;
  176. uint64_t mSize;
  177. bool mNotifyStorage : 1;
  178. bool mVisitEntries : 1;
  179. Atomic<bool> mCancel;
  180. };
  181. // WalkMemoryCacheRunnable
  182. // Responsible to visit memory storage and walk
  183. // all entries on it asynchronously.
  184. class WalkMemoryCacheRunnable : public WalkCacheRunnable
  185. {
  186. public:
  187. WalkMemoryCacheRunnable(nsILoadContextInfo *aLoadInfo,
  188. bool aVisitEntries,
  189. nsICacheStorageVisitor* aVisitor)
  190. : WalkCacheRunnable(aVisitor, aVisitEntries)
  191. {
  192. CacheFileUtils::AppendKeyPrefix(aLoadInfo, mContextKey);
  193. MOZ_ASSERT(NS_IsMainThread());
  194. }
  195. nsresult Walk()
  196. {
  197. return mService->Dispatch(this);
  198. }
  199. private:
  200. NS_IMETHOD Run() override
  201. {
  202. if (CacheStorageService::IsOnManagementThread()) {
  203. LOG(("WalkMemoryCacheRunnable::Run - collecting [this=%p]", this));
  204. // First, walk, count and grab all entries from the storage
  205. mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock());
  206. if (!CacheStorageService::IsRunning())
  207. return NS_ERROR_NOT_INITIALIZED;
  208. CacheEntryTable* entries;
  209. if (sGlobalEntryTables->Get(mContextKey, &entries)) {
  210. for (auto iter = entries->Iter(); !iter.Done(); iter.Next()) {
  211. CacheEntry* entry = iter.UserData();
  212. // Ignore disk entries
  213. if (entry->IsUsingDisk()) {
  214. continue;
  215. }
  216. mSize += entry->GetMetadataMemoryConsumption();
  217. int64_t size;
  218. if (NS_SUCCEEDED(entry->GetDataSize(&size))) {
  219. mSize += size;
  220. }
  221. mEntryArray.AppendElement(entry);
  222. }
  223. }
  224. // Next, we dispatch to the main thread
  225. } else if (NS_IsMainThread()) {
  226. LOG(("WalkMemoryCacheRunnable::Run - notifying [this=%p]", this));
  227. if (mNotifyStorage) {
  228. LOG((" storage"));
  229. // Second, notify overall storage info
  230. mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize,
  231. CacheObserver::MemoryCacheCapacity(), nullptr);
  232. if (!mVisitEntries)
  233. return NS_OK; // done
  234. mNotifyStorage = false;
  235. } else {
  236. LOG((" entry [left=%d, canceled=%d]", mEntryArray.Length(), (bool)mCancel));
  237. // Third, notify each entry until depleted or canceled
  238. if (!mEntryArray.Length() || mCancel) {
  239. mCallback->OnCacheEntryVisitCompleted();
  240. return NS_OK; // done
  241. }
  242. // Grab the next entry
  243. RefPtr<CacheEntry> entry = mEntryArray[0];
  244. mEntryArray.RemoveElementAt(0);
  245. // Invokes this->OnEntryInfo, that calls the callback with all
  246. // information of the entry.
  247. CacheStorageService::GetCacheEntryInfo(entry, this);
  248. }
  249. } else {
  250. MOZ_CRASH("Bad thread");
  251. return NS_ERROR_FAILURE;
  252. }
  253. NS_DispatchToMainThread(this);
  254. return NS_OK;
  255. }
  256. virtual ~WalkMemoryCacheRunnable()
  257. {
  258. if (mCallback)
  259. ProxyReleaseMainThread(mCallback);
  260. }
  261. virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
  262. int64_t aDataSize, int32_t aFetchCount,
  263. uint32_t aLastModifiedTime, uint32_t aExpirationTime,
  264. bool aPinned) override
  265. {
  266. nsresult rv;
  267. nsCOMPtr<nsIURI> uri;
  268. rv = NS_NewURI(getter_AddRefs(uri), aURISpec);
  269. if (NS_FAILED(rv)) {
  270. return;
  271. }
  272. rv = mCallback->OnCacheEntryInfo(uri, aIdEnhance, aDataSize, aFetchCount,
  273. aLastModifiedTime, aExpirationTime, aPinned);
  274. if (NS_FAILED(rv)) {
  275. LOG((" callback failed, canceling the walk"));
  276. mCancel = true;
  277. }
  278. }
  279. private:
  280. nsCString mContextKey;
  281. nsTArray<RefPtr<CacheEntry> > mEntryArray;
  282. };
  283. // WalkDiskCacheRunnable
  284. // Using the cache index information to get the list of files per context.
  285. class WalkDiskCacheRunnable : public WalkCacheRunnable
  286. {
  287. public:
  288. WalkDiskCacheRunnable(nsILoadContextInfo *aLoadInfo,
  289. bool aVisitEntries,
  290. nsICacheStorageVisitor* aVisitor)
  291. : WalkCacheRunnable(aVisitor, aVisitEntries)
  292. , mLoadInfo(aLoadInfo)
  293. , mPass(COLLECT_STATS)
  294. {
  295. }
  296. nsresult Walk()
  297. {
  298. // TODO, bug 998693
  299. // Initial index build should be forced here so that about:cache soon
  300. // after startup gives some meaningfull results.
  301. // Dispatch to the INDEX level in hope that very recent cache entries
  302. // information gets to the index list before we grab the index iterator
  303. // for the first time. This tries to avoid miss of entries that has
  304. // been created right before the visit is required.
  305. RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
  306. NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
  307. return thread->Dispatch(this, CacheIOThread::INDEX);
  308. }
  309. private:
  310. // Invokes OnCacheEntryInfo callback for each single found entry.
  311. // There is one instance of this class per one entry.
  312. class OnCacheEntryInfoRunnable : public Runnable
  313. {
  314. public:
  315. explicit OnCacheEntryInfoRunnable(WalkDiskCacheRunnable* aWalker)
  316. : mWalker(aWalker)
  317. {
  318. }
  319. NS_IMETHOD Run() override
  320. {
  321. MOZ_ASSERT(NS_IsMainThread());
  322. nsresult rv;
  323. nsCOMPtr<nsIURI> uri;
  324. rv = NS_NewURI(getter_AddRefs(uri), mURISpec);
  325. if (NS_FAILED(rv)) {
  326. return NS_OK;
  327. }
  328. rv = mWalker->mCallback->OnCacheEntryInfo(
  329. uri, mIdEnhance, mDataSize, mFetchCount,
  330. mLastModifiedTime, mExpirationTime, mPinned);
  331. if (NS_FAILED(rv)) {
  332. mWalker->mCancel = true;
  333. }
  334. return NS_OK;
  335. }
  336. RefPtr<WalkDiskCacheRunnable> mWalker;
  337. nsCString mURISpec;
  338. nsCString mIdEnhance;
  339. int64_t mDataSize;
  340. int32_t mFetchCount;
  341. uint32_t mLastModifiedTime;
  342. uint32_t mExpirationTime;
  343. bool mPinned;
  344. };
  345. NS_IMETHOD Run() override
  346. {
  347. // The main loop
  348. nsresult rv;
  349. if (CacheStorageService::IsOnManagementThread()) {
  350. switch (mPass) {
  351. case COLLECT_STATS:
  352. // Get quickly the cache stats.
  353. uint32_t size;
  354. rv = CacheIndex::GetCacheStats(mLoadInfo, &size, &mCount);
  355. if (NS_FAILED(rv)) {
  356. if (mVisitEntries) {
  357. // both onStorageInfo and onCompleted are expected
  358. NS_DispatchToMainThread(this);
  359. }
  360. return NS_DispatchToMainThread(this);
  361. }
  362. mSize = size << 10;
  363. // Invoke onCacheStorageInfo with valid information.
  364. NS_DispatchToMainThread(this);
  365. if (!mVisitEntries) {
  366. return NS_OK; // done
  367. }
  368. mPass = ITERATE_METADATA;
  369. MOZ_FALLTHROUGH;
  370. case ITERATE_METADATA:
  371. // Now grab the context iterator.
  372. if (!mIter) {
  373. rv = CacheIndex::GetIterator(mLoadInfo, true, getter_AddRefs(mIter));
  374. if (NS_FAILED(rv)) {
  375. // Invoke onCacheEntryVisitCompleted now
  376. return NS_DispatchToMainThread(this);
  377. }
  378. }
  379. while (!mCancel && !CacheObserver::ShuttingDown()) {
  380. if (CacheIOThread::YieldAndRerun())
  381. return NS_OK;
  382. SHA1Sum::Hash hash;
  383. rv = mIter->GetNextHash(&hash);
  384. if (NS_FAILED(rv))
  385. break; // done (or error?)
  386. // This synchronously invokes OnEntryInfo on this class where we
  387. // redispatch to the main thread for the consumer callback.
  388. CacheFileIOManager::GetEntryInfo(&hash, this);
  389. }
  390. // Invoke onCacheEntryVisitCompleted on the main thread
  391. NS_DispatchToMainThread(this);
  392. }
  393. } else if (NS_IsMainThread()) {
  394. if (mNotifyStorage) {
  395. nsCOMPtr<nsIFile> dir;
  396. CacheFileIOManager::GetCacheDirectory(getter_AddRefs(dir));
  397. mCallback->OnCacheStorageInfo(mCount, mSize, CacheObserver::DiskCacheCapacity(), dir);
  398. mNotifyStorage = false;
  399. } else {
  400. mCallback->OnCacheEntryVisitCompleted();
  401. }
  402. } else {
  403. MOZ_CRASH("Bad thread");
  404. return NS_ERROR_FAILURE;
  405. }
  406. return NS_OK;
  407. }
  408. virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
  409. int64_t aDataSize, int32_t aFetchCount,
  410. uint32_t aLastModifiedTime, uint32_t aExpirationTime,
  411. bool aPinned) override
  412. {
  413. // Called directly from CacheFileIOManager::GetEntryInfo.
  414. // Invoke onCacheEntryInfo on the main thread for this entry.
  415. RefPtr<OnCacheEntryInfoRunnable> info = new OnCacheEntryInfoRunnable(this);
  416. info->mURISpec = aURISpec;
  417. info->mIdEnhance = aIdEnhance;
  418. info->mDataSize = aDataSize;
  419. info->mFetchCount = aFetchCount;
  420. info->mLastModifiedTime = aLastModifiedTime;
  421. info->mExpirationTime = aExpirationTime;
  422. info->mPinned = aPinned;
  423. NS_DispatchToMainThread(info);
  424. }
  425. RefPtr<nsILoadContextInfo> mLoadInfo;
  426. enum {
  427. // First, we collect stats for the load context.
  428. COLLECT_STATS,
  429. // Second, if demanded, we iterate over the entries gethered
  430. // from the iterator and call CacheFileIOManager::GetEntryInfo
  431. // for each found entry.
  432. ITERATE_METADATA,
  433. } mPass;
  434. RefPtr<CacheIndexIterator> mIter;
  435. uint32_t mCount;
  436. };
  437. } // namespace
  438. void CacheStorageService::DropPrivateBrowsingEntries()
  439. {
  440. mozilla::MutexAutoLock lock(mLock);
  441. if (mShutdown)
  442. return;
  443. nsTArray<nsCString> keys;
  444. for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
  445. const nsACString& key = iter.Key();
  446. nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(key);
  447. if (info && info->IsPrivate()) {
  448. keys.AppendElement(key);
  449. }
  450. }
  451. for (uint32_t i = 0; i < keys.Length(); ++i) {
  452. DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
  453. }
  454. }
  455. namespace {
  456. class CleaupCacheDirectoriesRunnable : public Runnable
  457. {
  458. public:
  459. NS_DECL_NSIRUNNABLE
  460. static bool Post(uint32_t aVersion, uint32_t aActive);
  461. private:
  462. CleaupCacheDirectoriesRunnable(uint32_t aVersion, uint32_t aActive)
  463. : mVersion(aVersion), mActive(aActive)
  464. {
  465. nsCacheService::GetDiskCacheDirectory(getter_AddRefs(mCache1Dir));
  466. CacheFileIOManager::GetCacheDirectory(getter_AddRefs(mCache2Dir));
  467. }
  468. virtual ~CleaupCacheDirectoriesRunnable() {}
  469. uint32_t mVersion, mActive;
  470. nsCOMPtr<nsIFile> mCache1Dir, mCache2Dir;
  471. };
  472. // static
  473. bool CleaupCacheDirectoriesRunnable::Post(uint32_t aVersion, uint32_t aActive)
  474. {
  475. // CleaupCacheDirectories is called regardless what cache version is set up to use.
  476. // To obtain the cache1 directory we must unfortunatelly instantiate the old cache
  477. // service despite it may not be used at all... This also initialize nsDeleteDir.
  478. nsCOMPtr<nsICacheService> service = do_GetService(NS_CACHESERVICE_CONTRACTID);
  479. if (!service)
  480. return false;
  481. nsCOMPtr<nsIEventTarget> thread;
  482. service->GetCacheIOTarget(getter_AddRefs(thread));
  483. if (!thread)
  484. return false;
  485. RefPtr<CleaupCacheDirectoriesRunnable> r =
  486. new CleaupCacheDirectoriesRunnable(aVersion, aActive);
  487. thread->Dispatch(r, NS_DISPATCH_NORMAL);
  488. return true;
  489. }
  490. NS_IMETHODIMP CleaupCacheDirectoriesRunnable::Run()
  491. {
  492. MOZ_ASSERT(!NS_IsMainThread());
  493. if (mCache1Dir) {
  494. nsDeleteDir::RemoveOldTrashes(mCache1Dir);
  495. }
  496. if (mCache2Dir) {
  497. nsDeleteDir::RemoveOldTrashes(mCache2Dir);
  498. }
  499. // Delete the non-active version cache data right now
  500. if (mVersion == mActive) {
  501. return NS_OK;
  502. }
  503. switch (mVersion) {
  504. case 0:
  505. if (mCache1Dir) {
  506. nsDeleteDir::DeleteDir(mCache1Dir, true, 30000);
  507. }
  508. break;
  509. case 1:
  510. if (mCache2Dir) {
  511. nsDeleteDir::DeleteDir(mCache2Dir, true, 30000);
  512. }
  513. break;
  514. }
  515. return NS_OK;
  516. }
  517. } // namespace
  518. // static
  519. void CacheStorageService::CleaupCacheDirectories(uint32_t aVersion, uint32_t aActive)
  520. {
  521. // Make sure we schedule just once in case CleaupCacheDirectories gets called
  522. // multiple times from some reason.
  523. static bool runOnce = CleaupCacheDirectoriesRunnable::Post(aVersion, aActive);
  524. if (!runOnce) {
  525. NS_WARNING("Could not start cache trashes cleanup");
  526. }
  527. }
  528. // Helper methods
  529. // static
  530. bool CacheStorageService::IsOnManagementThread()
  531. {
  532. RefPtr<CacheStorageService> service = Self();
  533. if (!service)
  534. return false;
  535. nsCOMPtr<nsIEventTarget> target = service->Thread();
  536. if (!target)
  537. return false;
  538. bool currentThread;
  539. nsresult rv = target->IsOnCurrentThread(&currentThread);
  540. return NS_SUCCEEDED(rv) && currentThread;
  541. }
  542. already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const
  543. {
  544. return CacheFileIOManager::IOTarget();
  545. }
  546. nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent)
  547. {
  548. RefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread();
  549. if (!cacheIOThread)
  550. return NS_ERROR_NOT_AVAILABLE;
  551. return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT);
  552. }
  553. // nsICacheStorageService
  554. NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo,
  555. nsICacheStorage * *_retval)
  556. {
  557. NS_ENSURE_ARG(aLoadContextInfo);
  558. NS_ENSURE_ARG(_retval);
  559. nsCOMPtr<nsICacheStorage> storage;
  560. if (CacheObserver::UseNewCache()) {
  561. storage = new CacheStorage(aLoadContextInfo, false, false, false, false);
  562. }
  563. else {
  564. storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
  565. }
  566. storage.forget(_retval);
  567. return NS_OK;
  568. }
  569. NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo,
  570. bool aLookupAppCache,
  571. nsICacheStorage * *_retval)
  572. {
  573. NS_ENSURE_ARG(aLoadContextInfo);
  574. NS_ENSURE_ARG(_retval);
  575. // TODO save some heap granularity - cache commonly used storages.
  576. // When disk cache is disabled, still provide a storage, but just keep stuff
  577. // in memory.
  578. bool useDisk = CacheObserver::UseDiskCache();
  579. nsCOMPtr<nsICacheStorage> storage;
  580. if (CacheObserver::UseNewCache()) {
  581. storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false /* size limit */, false /* don't pin */);
  582. }
  583. else {
  584. storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr);
  585. }
  586. storage.forget(_retval);
  587. return NS_OK;
  588. }
  589. NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo,
  590. nsICacheStorage * *_retval)
  591. {
  592. NS_ENSURE_ARG(aLoadContextInfo);
  593. NS_ENSURE_ARG(_retval);
  594. if (!CacheObserver::UseNewCache()) {
  595. return NS_ERROR_NOT_IMPLEMENTED;
  596. }
  597. // When disk cache is disabled don't pretend we cache.
  598. if (!CacheObserver::UseDiskCache()) {
  599. return NS_ERROR_NOT_AVAILABLE;
  600. }
  601. nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
  602. aLoadContextInfo, true /* use disk */, false /* no appcache */, true /* ignore size checks */, true /* pin */);
  603. storage.forget(_retval);
  604. return NS_OK;
  605. }
  606. NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
  607. nsIApplicationCache *aApplicationCache,
  608. nsICacheStorage * *_retval)
  609. {
  610. NS_ENSURE_ARG(aLoadContextInfo);
  611. NS_ENSURE_ARG(_retval);
  612. nsCOMPtr<nsICacheStorage> storage;
  613. if (CacheObserver::UseNewCache()) {
  614. // Using classification since cl believes we want to instantiate this method
  615. // having the same name as the desired class...
  616. storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache);
  617. }
  618. else {
  619. storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
  620. }
  621. storage.forget(_retval);
  622. return NS_OK;
  623. }
  624. NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo,
  625. nsICacheStorage * *_retval)
  626. {
  627. NS_ENSURE_ARG(aLoadContextInfo);
  628. NS_ENSURE_ARG(_retval);
  629. nsCOMPtr<nsICacheStorage> storage;
  630. if (CacheObserver::UseNewCache()) {
  631. storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */, false /* no pinning */);
  632. }
  633. else {
  634. storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
  635. }
  636. storage.forget(_retval);
  637. return NS_OK;
  638. }
  639. NS_IMETHODIMP CacheStorageService::Clear()
  640. {
  641. nsresult rv;
  642. if (CacheObserver::UseNewCache()) {
  643. // Tell the index to block notification to AsyncGetDiskConsumption.
  644. // Will be allowed again from CacheFileContextEvictor::EvictEntries()
  645. // when all the context have been removed from disk.
  646. CacheIndex::OnAsyncEviction(true);
  647. {
  648. mozilla::MutexAutoLock lock(mLock);
  649. {
  650. mozilla::MutexAutoLock forcedValidEntriesLock(mForcedValidEntriesLock);
  651. mForcedValidEntries.Clear();
  652. }
  653. NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
  654. nsTArray<nsCString> keys;
  655. for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
  656. keys.AppendElement(iter.Key());
  657. }
  658. for (uint32_t i = 0; i < keys.Length(); ++i) {
  659. DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
  660. }
  661. // Passing null as a load info means to evict all contexts.
  662. // EvictByContext() respects the entry pinning. EvictAll() does not.
  663. rv = CacheFileIOManager::EvictByContext(nullptr, false);
  664. NS_ENSURE_SUCCESS(rv, rv);
  665. }
  666. } else {
  667. nsCOMPtr<nsICacheService> serv =
  668. do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
  669. NS_ENSURE_SUCCESS(rv, rv);
  670. rv = serv->EvictEntries(nsICache::STORE_ANYWHERE);
  671. NS_ENSURE_SUCCESS(rv, rv);
  672. }
  673. return NS_OK;
  674. }
  675. NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
  676. {
  677. uint32_t what;
  678. switch (aWhat) {
  679. case PURGE_DISK_DATA_ONLY:
  680. what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED;
  681. break;
  682. case PURGE_DISK_ALL:
  683. what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED;
  684. break;
  685. case PURGE_EVERYTHING:
  686. what = CacheEntry::PURGE_WHOLE;
  687. break;
  688. default:
  689. return NS_ERROR_INVALID_ARG;
  690. }
  691. nsCOMPtr<nsIRunnable> event =
  692. new PurgeFromMemoryRunnable(this, what);
  693. return Dispatch(event);
  694. }
  695. NS_IMETHODIMP CacheStorageService::PurgeFromMemoryRunnable::Run()
  696. {
  697. if (NS_IsMainThread()) {
  698. nsCOMPtr<nsIObserverService> observerService =
  699. mozilla::services::GetObserverService();
  700. if (observerService) {
  701. observerService->NotifyObservers(nullptr, "cacheservice:purge-memory-pools", nullptr);
  702. }
  703. return NS_OK;
  704. }
  705. if (mService) {
  706. // TODO not all flags apply to both pools
  707. mService->Pool(true).PurgeAll(mWhat);
  708. mService->Pool(false).PurgeAll(mWhat);
  709. mService = nullptr;
  710. }
  711. NS_DispatchToMainThread(this);
  712. return NS_OK;
  713. }
  714. NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
  715. nsICacheStorageConsumptionObserver* aObserver)
  716. {
  717. NS_ENSURE_ARG(aObserver);
  718. nsresult rv;
  719. if (CacheObserver::UseNewCache()) {
  720. rv = CacheIndex::AsyncGetDiskConsumption(aObserver);
  721. NS_ENSURE_SUCCESS(rv, rv);
  722. } else {
  723. rv = _OldGetDiskConsumption::Get(aObserver);
  724. NS_ENSURE_SUCCESS(rv, rv);
  725. }
  726. return NS_OK;
  727. }
  728. NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget)
  729. {
  730. NS_ENSURE_ARG(aEventTarget);
  731. if (CacheObserver::UseNewCache()) {
  732. nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
  733. ioTarget.forget(aEventTarget);
  734. }
  735. else {
  736. nsresult rv;
  737. nsCOMPtr<nsICacheService> serv =
  738. do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
  739. NS_ENSURE_SUCCESS(rv, rv);
  740. rv = serv->GetCacheIOTarget(aEventTarget);
  741. NS_ENSURE_SUCCESS(rv, rv);
  742. }
  743. return NS_OK;
  744. }
  745. // Methods used by CacheEntry for management of in-memory structures.
  746. namespace {
  747. class FrecencyComparator
  748. {
  749. public:
  750. bool Equals(CacheEntry* a, CacheEntry* b) const {
  751. return a->GetFrecency() == b->GetFrecency();
  752. }
  753. bool LessThan(CacheEntry* a, CacheEntry* b) const {
  754. return a->GetFrecency() < b->GetFrecency();
  755. }
  756. };
  757. class ExpirationComparator
  758. {
  759. public:
  760. bool Equals(CacheEntry* a, CacheEntry* b) const {
  761. return a->GetExpirationTime() == b->GetExpirationTime();
  762. }
  763. bool LessThan(CacheEntry* a, CacheEntry* b) const {
  764. return a->GetExpirationTime() < b->GetExpirationTime();
  765. }
  766. };
  767. } // namespace
  768. void
  769. CacheStorageService::RegisterEntry(CacheEntry* aEntry)
  770. {
  771. MOZ_ASSERT(IsOnManagementThread());
  772. if (mShutdown || !aEntry->CanRegister())
  773. return;
  774. LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry));
  775. MemoryPool& pool = Pool(aEntry->IsUsingDisk());
  776. pool.mFrecencyArray.AppendElement(aEntry);
  777. pool.mExpirationArray.AppendElement(aEntry);
  778. aEntry->SetRegistered(true);
  779. }
  780. void
  781. CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
  782. {
  783. MOZ_ASSERT(IsOnManagementThread());
  784. if (!aEntry->IsRegistered())
  785. return;
  786. LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry));
  787. MemoryPool& pool = Pool(aEntry->IsUsingDisk());
  788. mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry);
  789. mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry);
  790. MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration));
  791. // Note: aEntry->CanRegister() since now returns false
  792. aEntry->SetRegistered(false);
  793. }
  794. static bool
  795. AddExactEntry(CacheEntryTable* aEntries,
  796. nsACString const& aKey,
  797. CacheEntry* aEntry,
  798. bool aOverwrite)
  799. {
  800. RefPtr<CacheEntry> existingEntry;
  801. if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
  802. bool equals = existingEntry == aEntry;
  803. LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals));
  804. return equals; // Already there...
  805. }
  806. LOG(("AddExactEntry [entry=%p put]", aEntry));
  807. aEntries->Put(aKey, aEntry);
  808. return true;
  809. }
  810. static bool
  811. RemoveExactEntry(CacheEntryTable* aEntries,
  812. nsACString const& aKey,
  813. CacheEntry* aEntry,
  814. bool aOverwrite)
  815. {
  816. RefPtr<CacheEntry> existingEntry;
  817. if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
  818. LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
  819. return false; // Already removed...
  820. }
  821. if (!aOverwrite && existingEntry != aEntry) {
  822. LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
  823. return false; // Already replaced...
  824. }
  825. LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
  826. aEntries->Remove(aKey);
  827. return true;
  828. }
  829. bool
  830. CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
  831. {
  832. LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry));
  833. nsAutoCString entryKey;
  834. nsresult rv = aEntry->HashingKey(entryKey);
  835. if (NS_FAILED(rv)) {
  836. NS_ERROR("aEntry->HashingKey() failed?");
  837. return false;
  838. }
  839. mozilla::MutexAutoLock lock(mLock);
  840. if (mShutdown) {
  841. LOG((" after shutdown"));
  842. return false;
  843. }
  844. if (aOnlyUnreferenced) {
  845. if (aEntry->IsReferenced()) {
  846. LOG((" still referenced, not removing"));
  847. return false;
  848. }
  849. if (!aEntry->IsUsingDisk() && IsForcedValidEntry(aEntry->GetStorageID(), entryKey)) {
  850. LOG((" forced valid, not removing"));
  851. return false;
  852. }
  853. }
  854. CacheEntryTable* entries;
  855. if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
  856. RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
  857. nsAutoCString memoryStorageID(aEntry->GetStorageID());
  858. AppendMemoryStorageID(memoryStorageID);
  859. if (sGlobalEntryTables->Get(memoryStorageID, &entries))
  860. RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
  861. return true;
  862. }
  863. void
  864. CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry,
  865. bool aOnlyInMemory,
  866. bool aOverwrite)
  867. {
  868. LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]",
  869. aEntry, aOnlyInMemory, aOverwrite));
  870. // This method is responsible to put this entry to a special record hashtable
  871. // that contains only entries that are stored in memory.
  872. // Keep in mind that every entry, regardless of whether is in-memory-only or not
  873. // is always recorded in the storage master hash table, the one identified by
  874. // CacheEntry.StorageID().
  875. mLock.AssertCurrentThreadOwns();
  876. if (mShutdown) {
  877. LOG((" after shutdown"));
  878. return;
  879. }
  880. nsresult rv;
  881. nsAutoCString entryKey;
  882. rv = aEntry->HashingKey(entryKey);
  883. if (NS_FAILED(rv)) {
  884. NS_ERROR("aEntry->HashingKey() failed?");
  885. return;
  886. }
  887. CacheEntryTable* entries = nullptr;
  888. nsAutoCString memoryStorageID(aEntry->GetStorageID());
  889. AppendMemoryStorageID(memoryStorageID);
  890. if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
  891. if (!aOnlyInMemory) {
  892. LOG((" not recorded as memory only"));
  893. return;
  894. }
  895. entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
  896. sGlobalEntryTables->Put(memoryStorageID, entries);
  897. LOG((" new memory-only storage table for %s", memoryStorageID.get()));
  898. }
  899. if (aOnlyInMemory) {
  900. AddExactEntry(entries, entryKey, aEntry, aOverwrite);
  901. }
  902. else {
  903. RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
  904. }
  905. }
  906. // Checks if a cache entry is forced valid (will be loaded directly from cache
  907. // without further validation) - see nsICacheEntry.idl for further details
  908. bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextKey,
  909. nsACString const &aEntryKey)
  910. {
  911. return IsForcedValidEntry(aContextKey + aEntryKey);
  912. }
  913. bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextEntryKey)
  914. {
  915. mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
  916. TimeStamp validUntil;
  917. if (!mForcedValidEntries.Get(aContextEntryKey, &validUntil)) {
  918. return false;
  919. }
  920. if (validUntil.IsNull()) {
  921. return false;
  922. }
  923. // Entry timeout not reached yet
  924. if (TimeStamp::NowLoRes() <= validUntil) {
  925. return true;
  926. }
  927. // Entry timeout has been reached
  928. mForcedValidEntries.Remove(aContextEntryKey);
  929. return false;
  930. }
  931. // Allows a cache entry to be loaded directly from cache without further
  932. // validation - see nsICacheEntry.idl for further details
  933. void CacheStorageService::ForceEntryValidFor(nsACString const &aContextKey,
  934. nsACString const &aEntryKey,
  935. uint32_t aSecondsToTheFuture)
  936. {
  937. mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
  938. TimeStamp now = TimeStamp::NowLoRes();
  939. ForcedValidEntriesPrune(now);
  940. // This will be the timeout
  941. TimeStamp validUntil = now + TimeDuration::FromSeconds(aSecondsToTheFuture);
  942. mForcedValidEntries.Put(aContextKey + aEntryKey, validUntil);
  943. }
  944. void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey,
  945. nsACString const &aEntryKey)
  946. {
  947. mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
  948. LOG(("CacheStorageService::RemoveEntryForceValid context='%s' entryKey=%s",
  949. aContextKey.BeginReading(), aEntryKey.BeginReading()));
  950. mForcedValidEntries.Remove(aContextKey + aEntryKey);
  951. }
  952. // Cleans out the old entries in mForcedValidEntries
  953. void CacheStorageService::ForcedValidEntriesPrune(TimeStamp &now)
  954. {
  955. static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
  956. static TimeStamp dontPruneUntil = now + oneMinute;
  957. if (now < dontPruneUntil)
  958. return;
  959. for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
  960. if (iter.Data() < now) {
  961. iter.Remove();
  962. }
  963. }
  964. dontPruneUntil = now + oneMinute;
  965. }
  966. void
  967. CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer,
  968. uint32_t aCurrentMemoryConsumption)
  969. {
  970. LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]",
  971. aConsumer, aCurrentMemoryConsumption));
  972. uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption;
  973. if (savedMemorySize == aCurrentMemoryConsumption)
  974. return;
  975. // Exchange saved size with current one.
  976. aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption;
  977. bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY);
  978. bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange(
  979. savedMemorySize, aCurrentMemoryConsumption);
  980. if (!overLimit)
  981. return;
  982. // It's likely the timer has already been set when we get here,
  983. // check outside the lock to save resources.
  984. if (mPurgeTimer)
  985. return;
  986. // We don't know if this is called under the service lock or not,
  987. // hence rather dispatch.
  988. RefPtr<nsIEventTarget> cacheIOTarget = Thread();
  989. if (!cacheIOTarget)
  990. return;
  991. // Dispatch as a priority task, we want to set the purge timer
  992. // ASAP to prevent vain redispatch of this event.
  993. nsCOMPtr<nsIRunnable> event =
  994. NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit);
  995. cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
  996. }
  997. bool
  998. CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize,
  999. uint32_t aCurrentMemoryConsumption)
  1000. {
  1001. mMemorySize -= aSavedMemorySize;
  1002. mMemorySize += aCurrentMemoryConsumption;
  1003. LOG((" mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize));
  1004. // Bypass purging when memory has not grew up significantly
  1005. if (aCurrentMemoryConsumption <= aSavedMemorySize)
  1006. return false;
  1007. return mMemorySize > Limit();
  1008. }
  1009. void
  1010. CacheStorageService::SchedulePurgeOverMemoryLimit()
  1011. {
  1012. LOG(("CacheStorageService::SchedulePurgeOverMemoryLimit"));
  1013. mozilla::MutexAutoLock lock(mLock);
  1014. if (mShutdown) {
  1015. LOG((" past shutdown"));
  1016. return;
  1017. }
  1018. if (mPurgeTimer) {
  1019. LOG((" timer already up"));
  1020. return;
  1021. }
  1022. mPurgeTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1023. if (mPurgeTimer) {
  1024. nsresult rv;
  1025. rv = mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
  1026. LOG((" timer init rv=0x%08x", rv));
  1027. }
  1028. }
  1029. NS_IMETHODIMP
  1030. CacheStorageService::Notify(nsITimer* aTimer)
  1031. {
  1032. LOG(("CacheStorageService::Notify"));
  1033. mozilla::MutexAutoLock lock(mLock);
  1034. if (aTimer == mPurgeTimer) {
  1035. mPurgeTimer = nullptr;
  1036. nsCOMPtr<nsIRunnable> event =
  1037. NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit);
  1038. Dispatch(event);
  1039. }
  1040. return NS_OK;
  1041. }
  1042. void
  1043. CacheStorageService::PurgeOverMemoryLimit()
  1044. {
  1045. MOZ_ASSERT(IsOnManagementThread());
  1046. LOG(("CacheStorageService::PurgeOverMemoryLimit"));
  1047. static TimeDuration const kFourSeconds = TimeDuration::FromSeconds(4);
  1048. TimeStamp now = TimeStamp::NowLoRes();
  1049. if (!mLastPurgeTime.IsNull() && now - mLastPurgeTime < kFourSeconds) {
  1050. LOG((" bypassed, too soon"));
  1051. return;
  1052. }
  1053. mLastPurgeTime = now;
  1054. Pool(true).PurgeOverMemoryLimit();
  1055. Pool(false).PurgeOverMemoryLimit();
  1056. }
  1057. void
  1058. CacheStorageService::MemoryPool::PurgeOverMemoryLimit()
  1059. {
  1060. TimeStamp start(TimeStamp::Now());
  1061. uint32_t const memoryLimit = Limit();
  1062. if (mMemorySize > memoryLimit) {
  1063. LOG((" memory data consumption over the limit, abandon expired entries"));
  1064. PurgeExpired();
  1065. }
  1066. bool frecencyNeedsSort = true;
  1067. // No longer makes sense since:
  1068. // Memory entries are never purged partially, only as a whole when the memory
  1069. // cache limit is overreached.
  1070. // Disk entries throw the data away ASAP so that only metadata are kept.
  1071. // TODO when this concept of two separate pools is found working, the code should
  1072. // clean up.
  1073. #if 0
  1074. if (mMemorySize > memoryLimit) {
  1075. LOG((" memory data consumption over the limit, abandon disk backed data"));
  1076. PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED);
  1077. }
  1078. if (mMemorySize > memoryLimit) {
  1079. LOG((" metadata consumtion over the limit, abandon disk backed entries"));
  1080. PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED);
  1081. }
  1082. #endif
  1083. if (mMemorySize > memoryLimit) {
  1084. LOG((" memory data consumption over the limit, abandon any entry"));
  1085. PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
  1086. }
  1087. LOG((" purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
  1088. }
  1089. void
  1090. CacheStorageService::MemoryPool::PurgeExpired()
  1091. {
  1092. MOZ_ASSERT(IsOnManagementThread());
  1093. mExpirationArray.Sort(ExpirationComparator());
  1094. uint32_t now = NowInSeconds();
  1095. uint32_t const memoryLimit = Limit();
  1096. for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
  1097. if (CacheIOThread::YieldAndRerun())
  1098. return;
  1099. RefPtr<CacheEntry> entry = mExpirationArray[i];
  1100. uint32_t expirationTime = entry->GetExpirationTime();
  1101. if (expirationTime > 0 && expirationTime <= now &&
  1102. entry->Purge(CacheEntry::PURGE_WHOLE)) {
  1103. LOG((" purged expired, entry=%p, exptime=%u (now=%u)",
  1104. entry.get(), entry->GetExpirationTime(), now));
  1105. continue;
  1106. }
  1107. // not purged, move to the next one
  1108. ++i;
  1109. }
  1110. }
  1111. void
  1112. CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat)
  1113. {
  1114. MOZ_ASSERT(IsOnManagementThread());
  1115. if (aFrecencyNeedsSort) {
  1116. mFrecencyArray.Sort(FrecencyComparator());
  1117. aFrecencyNeedsSort = false;
  1118. }
  1119. uint32_t const memoryLimit = Limit();
  1120. for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
  1121. if (CacheIOThread::YieldAndRerun())
  1122. return;
  1123. RefPtr<CacheEntry> entry = mFrecencyArray[i];
  1124. if (entry->Purge(aWhat)) {
  1125. LOG((" abandoned (%d), entry=%p, frecency=%1.10f",
  1126. aWhat, entry.get(), entry->GetFrecency()));
  1127. continue;
  1128. }
  1129. // not purged, move to the next one
  1130. ++i;
  1131. }
  1132. }
  1133. void
  1134. CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat)
  1135. {
  1136. LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat));
  1137. MOZ_ASSERT(IsOnManagementThread());
  1138. for (uint32_t i = 0; i < mFrecencyArray.Length();) {
  1139. if (CacheIOThread::YieldAndRerun())
  1140. return;
  1141. RefPtr<CacheEntry> entry = mFrecencyArray[i];
  1142. if (entry->Purge(aWhat)) {
  1143. LOG((" abandoned entry=%p", entry.get()));
  1144. continue;
  1145. }
  1146. // not purged, move to the next one
  1147. ++i;
  1148. }
  1149. }
  1150. // Methods exposed to and used by CacheStorage.
  1151. nsresult
  1152. CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
  1153. const nsACString & aURI,
  1154. const nsACString & aIdExtension,
  1155. bool aReplace,
  1156. CacheEntryHandle** aResult)
  1157. {
  1158. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1159. NS_ENSURE_ARG(aStorage);
  1160. nsAutoCString contextKey;
  1161. CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1162. return AddStorageEntry(contextKey, aURI, aIdExtension,
  1163. aStorage->WriteToDisk(),
  1164. aStorage->SkipSizeCheck(),
  1165. aStorage->Pinning(),
  1166. aReplace,
  1167. aResult);
  1168. }
  1169. nsresult
  1170. CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
  1171. const nsACString & aURI,
  1172. const nsACString & aIdExtension,
  1173. bool aWriteToDisk,
  1174. bool aSkipSizeCheck,
  1175. bool aPin,
  1176. bool aReplace,
  1177. CacheEntryHandle** aResult)
  1178. {
  1179. nsresult rv;
  1180. nsAutoCString entryKey;
  1181. rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  1182. NS_ENSURE_SUCCESS(rv, rv);
  1183. LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
  1184. entryKey.get(), aContextKey.BeginReading()));
  1185. RefPtr<CacheEntry> entry;
  1186. RefPtr<CacheEntryHandle> handle;
  1187. {
  1188. mozilla::MutexAutoLock lock(mLock);
  1189. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1190. // Ensure storage table
  1191. CacheEntryTable* entries;
  1192. if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
  1193. entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
  1194. sGlobalEntryTables->Put(aContextKey, entries);
  1195. LOG((" new storage entries table for context '%s'", aContextKey.BeginReading()));
  1196. }
  1197. bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
  1198. if (entryExists && !aReplace) {
  1199. // check whether we want to turn this entry to a memory-only.
  1200. if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
  1201. LOG((" entry is persistent but we want mem-only, replacing it"));
  1202. aReplace = true;
  1203. }
  1204. }
  1205. // If truncate is demanded, delete and doom the current entry
  1206. if (entryExists && aReplace) {
  1207. entries->Remove(entryKey);
  1208. LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
  1209. // On purpose called under the lock to prevent races of doom and open on I/O thread
  1210. // No need to remove from both memory-only and all-entries tables. The new entry
  1211. // will overwrite the shadow entry in its ctor.
  1212. entry->DoomAlreadyRemoved();
  1213. entry = nullptr;
  1214. entryExists = false;
  1215. // Would only lead to deleting force-valid timestamp again. We don't need the
  1216. // replace information anymore after this point anyway.
  1217. aReplace = false;
  1218. }
  1219. // Ensure entry for the particular URL
  1220. if (!entryExists) {
  1221. // When replacing with a new entry, always remove the current force-valid timestamp,
  1222. // this is the only place to do it.
  1223. if (aReplace) {
  1224. RemoveEntryForceValid(aContextKey, entryKey);
  1225. }
  1226. // Entry is not in the hashtable or has just been truncated...
  1227. entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
  1228. entries->Put(entryKey, entry);
  1229. LOG((" new entry %p for %s", entry.get(), entryKey.get()));
  1230. }
  1231. if (entry) {
  1232. // Here, if this entry was not for a long time referenced by any consumer,
  1233. // gets again first 'handles count' reference.
  1234. handle = entry->NewHandle();
  1235. }
  1236. }
  1237. handle.forget(aResult);
  1238. return NS_OK;
  1239. }
  1240. nsresult
  1241. CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage,
  1242. const nsACString & aURI,
  1243. const nsACString & aIdExtension,
  1244. bool* aResult)
  1245. {
  1246. nsresult rv;
  1247. nsAutoCString contextKey;
  1248. CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1249. if (!aStorage->WriteToDisk()) {
  1250. AppendMemoryStorageID(contextKey);
  1251. }
  1252. LOG(("CacheStorageService::CheckStorageEntry [uri=%s, eid=%s, contextKey=%s]",
  1253. aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
  1254. {
  1255. mozilla::MutexAutoLock lock(mLock);
  1256. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1257. nsAutoCString entryKey;
  1258. rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  1259. NS_ENSURE_SUCCESS(rv, rv);
  1260. CacheEntryTable* entries;
  1261. if ((*aResult = sGlobalEntryTables->Get(contextKey, &entries)) &&
  1262. entries->GetWeak(entryKey, aResult)) {
  1263. LOG((" found in hash tables"));
  1264. return NS_OK;
  1265. }
  1266. }
  1267. if (!aStorage->WriteToDisk()) {
  1268. // Memory entry, nothing more to do.
  1269. LOG((" not found in hash tables"));
  1270. return NS_OK;
  1271. }
  1272. // Disk entry, not found in the hashtable, check the index.
  1273. nsAutoCString fileKey;
  1274. rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
  1275. CacheIndex::EntryStatus status;
  1276. rv = CacheIndex::HasEntry(fileKey, &status);
  1277. if (NS_FAILED(rv) || status == CacheIndex::DO_NOT_KNOW) {
  1278. LOG((" index doesn't know, rv=0x%08x", rv));
  1279. return NS_ERROR_NOT_AVAILABLE;
  1280. }
  1281. *aResult = status == CacheIndex::EXISTS;
  1282. LOG((" %sfound in index", *aResult ? "" : "not "));
  1283. return NS_OK;
  1284. }
  1285. namespace {
  1286. class CacheEntryDoomByKeyCallback : public CacheFileIOListener
  1287. , public nsIRunnable
  1288. {
  1289. public:
  1290. NS_DECL_THREADSAFE_ISUPPORTS
  1291. NS_DECL_NSIRUNNABLE
  1292. explicit CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback)
  1293. : mCallback(aCallback) { }
  1294. private:
  1295. virtual ~CacheEntryDoomByKeyCallback();
  1296. NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
  1297. NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) override { return NS_OK; }
  1298. NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override { return NS_OK; }
  1299. NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
  1300. NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
  1301. NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
  1302. nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  1303. nsresult mResult;
  1304. };
  1305. CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback()
  1306. {
  1307. if (mCallback)
  1308. ProxyReleaseMainThread(mCallback);
  1309. }
  1310. NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
  1311. nsresult aResult)
  1312. {
  1313. if (!mCallback)
  1314. return NS_OK;
  1315. mResult = aResult;
  1316. if (NS_IsMainThread()) {
  1317. Run();
  1318. } else {
  1319. NS_DispatchToMainThread(this);
  1320. }
  1321. return NS_OK;
  1322. }
  1323. NS_IMETHODIMP CacheEntryDoomByKeyCallback::Run()
  1324. {
  1325. mCallback->OnCacheEntryDoomed(mResult);
  1326. return NS_OK;
  1327. }
  1328. NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener, nsIRunnable);
  1329. } // namespace
  1330. nsresult
  1331. CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage,
  1332. const nsACString & aURI,
  1333. const nsACString & aIdExtension,
  1334. nsICacheEntryDoomCallback* aCallback)
  1335. {
  1336. LOG(("CacheStorageService::DoomStorageEntry"));
  1337. NS_ENSURE_ARG(aStorage);
  1338. nsAutoCString contextKey;
  1339. CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1340. nsAutoCString entryKey;
  1341. nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
  1342. NS_ENSURE_SUCCESS(rv, rv);
  1343. RefPtr<CacheEntry> entry;
  1344. {
  1345. mozilla::MutexAutoLock lock(mLock);
  1346. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1347. CacheEntryTable* entries;
  1348. if (sGlobalEntryTables->Get(contextKey, &entries)) {
  1349. if (entries->Get(entryKey, getter_AddRefs(entry))) {
  1350. if (aStorage->WriteToDisk() || !entry->IsUsingDisk()) {
  1351. // When evicting from disk storage, purge
  1352. // When evicting from memory storage and the entry is memory-only, purge
  1353. LOG((" purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
  1354. entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
  1355. entries->Remove(entryKey);
  1356. }
  1357. else {
  1358. // Otherwise, leave it
  1359. LOG((" leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
  1360. entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
  1361. entry = nullptr;
  1362. }
  1363. }
  1364. }
  1365. if (!entry) {
  1366. RemoveEntryForceValid(contextKey, entryKey);
  1367. }
  1368. }
  1369. if (entry) {
  1370. LOG((" dooming entry %p for %s", entry.get(), entryKey.get()));
  1371. return entry->AsyncDoom(aCallback);
  1372. }
  1373. LOG((" no entry loaded for %s", entryKey.get()));
  1374. if (aStorage->WriteToDisk()) {
  1375. nsAutoCString contextKey;
  1376. CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1377. rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey);
  1378. NS_ENSURE_SUCCESS(rv, rv);
  1379. LOG((" dooming file only for %s", entryKey.get()));
  1380. RefPtr<CacheEntryDoomByKeyCallback> callback(
  1381. new CacheEntryDoomByKeyCallback(aCallback));
  1382. rv = CacheFileIOManager::DoomFileByKey(entryKey, callback);
  1383. NS_ENSURE_SUCCESS(rv, rv);
  1384. return NS_OK;
  1385. }
  1386. class Callback : public Runnable
  1387. {
  1388. public:
  1389. explicit Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
  1390. NS_IMETHOD Run() override
  1391. {
  1392. mCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
  1393. return NS_OK;
  1394. }
  1395. nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  1396. };
  1397. if (aCallback) {
  1398. RefPtr<Runnable> callback = new Callback(aCallback);
  1399. return NS_DispatchToMainThread(callback);
  1400. }
  1401. return NS_OK;
  1402. }
  1403. nsresult
  1404. CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
  1405. nsICacheEntryDoomCallback* aCallback)
  1406. {
  1407. LOG(("CacheStorageService::DoomStorageEntries"));
  1408. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1409. NS_ENSURE_ARG(aStorage);
  1410. nsAutoCString contextKey;
  1411. CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
  1412. mozilla::MutexAutoLock lock(mLock);
  1413. return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
  1414. aStorage->WriteToDisk(), aStorage->Pinning(),
  1415. aCallback);
  1416. }
  1417. nsresult
  1418. CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
  1419. nsILoadContextInfo* aContext,
  1420. bool aDiskStorage,
  1421. bool aPinned,
  1422. nsICacheEntryDoomCallback* aCallback)
  1423. {
  1424. LOG(("CacheStorageService::DoomStorageEntries [context=%s]", aContextKey.BeginReading()));
  1425. mLock.AssertCurrentThreadOwns();
  1426. NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
  1427. nsAutoCString memoryStorageID(aContextKey);
  1428. AppendMemoryStorageID(memoryStorageID);
  1429. if (aDiskStorage) {
  1430. LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading()));
  1431. // Walk one by one and remove entries according their pin status
  1432. CacheEntryTable *diskEntries, *memoryEntries;
  1433. if (sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
  1434. sGlobalEntryTables->Get(memoryStorageID, &memoryEntries);
  1435. for (auto iter = diskEntries->Iter(); !iter.Done(); iter.Next()) {
  1436. auto entry = iter.Data();
  1437. if (entry->DeferOrBypassRemovalOnPinStatus(aPinned)) {
  1438. continue;
  1439. }
  1440. if (memoryEntries) {
  1441. RemoveExactEntry(memoryEntries, iter.Key(), entry, false);
  1442. }
  1443. iter.Remove();
  1444. }
  1445. }
  1446. if (aContext && !aContext->IsPrivate()) {
  1447. LOG((" dooming disk entries"));
  1448. CacheFileIOManager::EvictByContext(aContext, aPinned);
  1449. }
  1450. } else {
  1451. LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));
  1452. // Remove the memory entries table from the global tables.
  1453. // Since we store memory entries also in the disk entries table
  1454. // we need to remove the memory entries from the disk table one
  1455. // by one manually.
  1456. nsAutoPtr<CacheEntryTable> memoryEntries;
  1457. sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);
  1458. CacheEntryTable* diskEntries;
  1459. if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
  1460. for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
  1461. auto entry = iter.Data();
  1462. RemoveExactEntry(diskEntries, iter.Key(), entry, false);
  1463. }
  1464. }
  1465. }
  1466. {
  1467. mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
  1468. if (aContext) {
  1469. for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
  1470. bool matches;
  1471. DebugOnly<nsresult> rv = CacheFileUtils::KeyMatchesLoadContextInfo(
  1472. iter.Key(), aContext, &matches);
  1473. MOZ_ASSERT(NS_SUCCEEDED(rv));
  1474. if (matches) {
  1475. iter.Remove();
  1476. }
  1477. }
  1478. } else {
  1479. mForcedValidEntries.Clear();
  1480. }
  1481. }
  1482. // An artificial callback. This is a candidate for removal tho. In the new
  1483. // cache any 'doom' or 'evict' function ensures that the entry or entries
  1484. // being doomed is/are not accessible after the function returns. So there is
  1485. // probably no need for a callback - has no meaning. But for compatibility
  1486. // with the old cache that is still in the tree we keep the API similar to be
  1487. // able to make tests as well as other consumers work for now.
  1488. class Callback : public Runnable
  1489. {
  1490. public:
  1491. explicit Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
  1492. NS_IMETHOD Run() override
  1493. {
  1494. mCallback->OnCacheEntryDoomed(NS_OK);
  1495. return NS_OK;
  1496. }
  1497. nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
  1498. };
  1499. if (aCallback) {
  1500. RefPtr<Runnable> callback = new Callback(aCallback);
  1501. return NS_DispatchToMainThread(callback);
  1502. }
  1503. return NS_OK;
  1504. }
  1505. nsresult
  1506. CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
  1507. bool aVisitEntries,
  1508. nsICacheStorageVisitor* aVisitor)
  1509. {
  1510. LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries));
  1511. NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
  1512. NS_ENSURE_ARG(aStorage);
  1513. if (aStorage->WriteToDisk()) {
  1514. RefPtr<WalkDiskCacheRunnable> event =
  1515. new WalkDiskCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
  1516. return event->Walk();
  1517. }
  1518. RefPtr<WalkMemoryCacheRunnable> event =
  1519. new WalkMemoryCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
  1520. return event->Walk();
  1521. }
  1522. void
  1523. CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
  1524. const nsACString & aIdExtension,
  1525. const nsACString & aURISpec)
  1526. {
  1527. nsAutoCString contextKey;
  1528. CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
  1529. nsAutoCString entryKey;
  1530. CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
  1531. mozilla::MutexAutoLock lock(mLock);
  1532. if (mShutdown) {
  1533. return;
  1534. }
  1535. CacheEntryTable* entries;
  1536. RefPtr<CacheEntry> entry;
  1537. if (sGlobalEntryTables->Get(contextKey, &entries) &&
  1538. entries->Get(entryKey, getter_AddRefs(entry))) {
  1539. if (entry->IsFileDoomed()) {
  1540. // Need to remove under the lock to avoid possible race leading
  1541. // to duplication of the entry per its key.
  1542. RemoveExactEntry(entries, entryKey, entry, false);
  1543. entry->DoomAlreadyRemoved();
  1544. }
  1545. // Entry found, but it's not the entry that has been found doomed
  1546. // by the lower eviction layer. Just leave everything unchanged.
  1547. return;
  1548. }
  1549. RemoveEntryForceValid(contextKey, entryKey);
  1550. }
  1551. bool
  1552. CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo,
  1553. const nsACString & aIdExtension,
  1554. const nsACString & aURISpec,
  1555. EntryInfoCallback *aCallback)
  1556. {
  1557. nsAutoCString contextKey;
  1558. CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
  1559. nsAutoCString entryKey;
  1560. CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
  1561. RefPtr<CacheEntry> entry;
  1562. {
  1563. mozilla::MutexAutoLock lock(mLock);
  1564. if (mShutdown) {
  1565. return false;
  1566. }
  1567. CacheEntryTable* entries;
  1568. if (!sGlobalEntryTables->Get(contextKey, &entries)) {
  1569. return false;
  1570. }
  1571. if (!entries->Get(entryKey, getter_AddRefs(entry))) {
  1572. return false;
  1573. }
  1574. }
  1575. GetCacheEntryInfo(entry, aCallback);
  1576. return true;
  1577. }
  1578. // static
  1579. void
  1580. CacheStorageService::GetCacheEntryInfo(CacheEntry* aEntry,
  1581. EntryInfoCallback *aCallback)
  1582. {
  1583. nsCString const uriSpec = aEntry->GetURI();
  1584. nsCString const enhanceId = aEntry->GetEnhanceID();
  1585. uint32_t dataSize;
  1586. if (NS_FAILED(aEntry->GetStorageDataSize(&dataSize))) {
  1587. dataSize = 0;
  1588. }
  1589. int32_t fetchCount;
  1590. if (NS_FAILED(aEntry->GetFetchCount(&fetchCount))) {
  1591. fetchCount = 0;
  1592. }
  1593. uint32_t lastModified;
  1594. if (NS_FAILED(aEntry->GetLastModified(&lastModified))) {
  1595. lastModified = 0;
  1596. }
  1597. uint32_t expirationTime;
  1598. if (NS_FAILED(aEntry->GetExpirationTime(&expirationTime))) {
  1599. expirationTime = 0;
  1600. }
  1601. aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize,
  1602. fetchCount, lastModified, expirationTime,
  1603. aEntry->IsPinned());
  1604. }
  1605. // static
  1606. uint32_t CacheStorageService::CacheQueueSize(bool highPriority)
  1607. {
  1608. RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
  1609. MOZ_ASSERT(thread);
  1610. return thread->QueueSize(highPriority);
  1611. }
  1612. // nsIMemoryReporter
  1613. size_t
  1614. CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1615. {
  1616. CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
  1617. size_t n = 0;
  1618. // The elemets are referenced by sGlobalEntryTables and are reported from there
  1619. n += Pool(true).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
  1620. n += Pool(true).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
  1621. n += Pool(false).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
  1622. n += Pool(false).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
  1623. // Entries reported manually in CacheStorageService::CollectReports callback
  1624. if (sGlobalEntryTables) {
  1625. n += sGlobalEntryTables->ShallowSizeOfIncludingThis(mallocSizeOf);
  1626. }
  1627. return n;
  1628. }
  1629. size_t
  1630. CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1631. {
  1632. return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
  1633. }
  1634. NS_IMETHODIMP
  1635. CacheStorageService::CollectReports(nsIHandleReportCallback* aHandleReport,
  1636. nsISupports* aData, bool aAnonymize)
  1637. {
  1638. MOZ_COLLECT_REPORT(
  1639. "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
  1640. CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
  1641. "Memory used by the cache IO manager.");
  1642. MOZ_COLLECT_REPORT(
  1643. "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
  1644. CacheIndex::SizeOfIncludingThis(MallocSizeOf),
  1645. "Memory used by the cache index.");
  1646. MutexAutoLock lock(mLock);
  1647. // Report the service instance, this doesn't report entries, done lower
  1648. MOZ_COLLECT_REPORT(
  1649. "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
  1650. SizeOfIncludingThis(MallocSizeOf),
  1651. "Memory used by the cache storage service.");
  1652. // Report all entries, each storage separately (by the context key)
  1653. //
  1654. // References are:
  1655. // sGlobalEntryTables to N CacheEntryTable
  1656. // CacheEntryTable to N CacheEntry
  1657. // CacheEntry to 1 CacheFile
  1658. // CacheFile to
  1659. // N CacheFileChunk (keeping the actual data)
  1660. // 1 CacheFileMetadata (keeping http headers etc.)
  1661. // 1 CacheFileOutputStream
  1662. // N CacheFileInputStream
  1663. if (sGlobalEntryTables) {
  1664. for (auto iter1 = sGlobalEntryTables->Iter(); !iter1.Done(); iter1.Next()) {
  1665. CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
  1666. CacheEntryTable* table = iter1.UserData();
  1667. size_t size = 0;
  1668. mozilla::MallocSizeOf mallocSizeOf = CacheStorageService::MallocSizeOf;
  1669. size += table->ShallowSizeOfIncludingThis(mallocSizeOf);
  1670. for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
  1671. size += iter2.Key().SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1672. // Bypass memory-only entries, those will be reported when iterating the
  1673. // memory only table. Memory-only entries are stored in both ALL_ENTRIES
  1674. // and MEMORY_ONLY hashtables.
  1675. RefPtr<mozilla::net::CacheEntry> const& entry = iter2.Data();
  1676. if (table->Type() == CacheEntryTable::MEMORY_ONLY ||
  1677. entry->IsUsingDisk()) {
  1678. size += entry->SizeOfIncludingThis(mallocSizeOf);
  1679. }
  1680. }
  1681. // These key names are not privacy-sensitive.
  1682. aHandleReport->Callback(
  1683. EmptyCString(),
  1684. nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
  1685. table->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
  1686. iter1.Key().BeginReading()),
  1687. nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, size,
  1688. NS_LITERAL_CSTRING("Memory used by the cache storage."),
  1689. aData);
  1690. }
  1691. }
  1692. return NS_OK;
  1693. }
  1694. // nsICacheTesting
  1695. NS_IMETHODIMP
  1696. CacheStorageService::IOThreadSuspender::Run()
  1697. {
  1698. MonitorAutoLock mon(mMon);
  1699. while (!mSignaled) {
  1700. mon.Wait();
  1701. }
  1702. return NS_OK;
  1703. }
  1704. void
  1705. CacheStorageService::IOThreadSuspender::Notify()
  1706. {
  1707. MonitorAutoLock mon(mMon);
  1708. mSignaled = true;
  1709. mon.Notify();
  1710. }
  1711. NS_IMETHODIMP
  1712. CacheStorageService::SuspendCacheIOThread(uint32_t aLevel)
  1713. {
  1714. RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
  1715. if (!thread) {
  1716. return NS_ERROR_NOT_AVAILABLE;
  1717. }
  1718. MOZ_ASSERT(!mActiveIOSuspender);
  1719. mActiveIOSuspender = new IOThreadSuspender();
  1720. return thread->Dispatch(mActiveIOSuspender, aLevel);
  1721. }
  1722. NS_IMETHODIMP
  1723. CacheStorageService::ResumeCacheIOThread()
  1724. {
  1725. MOZ_ASSERT(mActiveIOSuspender);
  1726. RefPtr<IOThreadSuspender> suspender;
  1727. suspender.swap(mActiveIOSuspender);
  1728. suspender->Notify();
  1729. return NS_OK;
  1730. }
  1731. NS_IMETHODIMP
  1732. CacheStorageService::Flush(nsIObserver* aObserver)
  1733. {
  1734. RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
  1735. if (!thread) {
  1736. return NS_ERROR_NOT_AVAILABLE;
  1737. }
  1738. nsCOMPtr<nsIObserverService> observerService =
  1739. mozilla::services::GetObserverService();
  1740. if (!observerService) {
  1741. return NS_ERROR_NOT_AVAILABLE;
  1742. }
  1743. // Adding as weak, the consumer is responsible to keep the reference
  1744. // until notified.
  1745. observerService->AddObserver(aObserver, "cacheservice:purge-memory-pools", false);
  1746. // This runnable will do the purging and when done, notifies the above observer.
  1747. // We dispatch it to the CLOSE level, so all data writes scheduled up to this time
  1748. // will be done before this purging happens.
  1749. RefPtr<CacheStorageService::PurgeFromMemoryRunnable> r =
  1750. new CacheStorageService::PurgeFromMemoryRunnable(this, CacheEntry::PURGE_WHOLE);
  1751. return thread->Dispatch(r, CacheIOThread::WRITE);
  1752. }
  1753. } // namespace net
  1754. } // namespace mozilla