StartupCache.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  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 "prio.h"
  6. #include "PLDHashTable.h"
  7. #include "nsXPCOMStrings.h"
  8. #include "mozilla/IOInterposer.h"
  9. #include "mozilla/MemoryReporting.h"
  10. #include "mozilla/scache/StartupCache.h"
  11. #include "nsAutoPtr.h"
  12. #include "nsClassHashtable.h"
  13. #include "nsComponentManagerUtils.h"
  14. #include "nsDirectoryServiceUtils.h"
  15. #include "nsIClassInfo.h"
  16. #include "nsIFile.h"
  17. #include "nsIObserver.h"
  18. #include "nsIObserverService.h"
  19. #include "nsIOutputStream.h"
  20. #include "nsIStartupCache.h"
  21. #include "nsIStorageStream.h"
  22. #include "nsIStreamBufferAccess.h"
  23. #include "nsIStringStream.h"
  24. #include "nsISupports.h"
  25. #include "nsITimer.h"
  26. #include "nsIZipWriter.h"
  27. #include "nsIZipReader.h"
  28. #include "nsWeakReference.h"
  29. #include "nsZipArchive.h"
  30. #include "mozilla/Omnijar.h"
  31. #include "prenv.h"
  32. #include "mozilla/Telemetry.h"
  33. #include "nsThreadUtils.h"
  34. #include "nsXULAppAPI.h"
  35. #include "nsIProtocolHandler.h"
  36. #ifdef IS_BIG_ENDIAN
  37. #define SC_ENDIAN "big"
  38. #else
  39. #define SC_ENDIAN "little"
  40. #endif
  41. #if PR_BYTES_PER_WORD == 4
  42. #define SC_WORDSIZE "4"
  43. #else
  44. #define SC_WORDSIZE "8"
  45. #endif
  46. namespace mozilla {
  47. namespace scache {
  48. MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)
  49. NS_IMETHODIMP
  50. StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport,
  51. nsISupports* aData, bool aAnonymize)
  52. {
  53. MOZ_COLLECT_REPORT(
  54. "explicit/startup-cache/mapping", KIND_NONHEAP, UNITS_BYTES,
  55. SizeOfMapping(),
  56. "Memory used to hold the mapping of the startup cache from file. "
  57. "This memory is likely to be swapped out shortly after start-up.");
  58. MOZ_COLLECT_REPORT(
  59. "explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,
  60. HeapSizeOfIncludingThis(StartupCacheMallocSizeOf),
  61. "Memory used by the startup cache for things other than the file mapping.");
  62. return NS_OK;
  63. }
  64. #define STARTUP_CACHE_NAME "startupCache." SC_WORDSIZE "." SC_ENDIAN
  65. StartupCache*
  66. StartupCache::GetSingleton()
  67. {
  68. if (!gStartupCache) {
  69. if (!XRE_IsParentProcess()) {
  70. return nullptr;
  71. }
  72. #ifdef MOZ_DISABLE_STARTUPCACHE
  73. return nullptr;
  74. #else
  75. StartupCache::InitSingleton();
  76. #endif
  77. }
  78. return StartupCache::gStartupCache;
  79. }
  80. void
  81. StartupCache::DeleteSingleton()
  82. {
  83. StartupCache::gStartupCache = nullptr;
  84. }
  85. nsresult
  86. StartupCache::InitSingleton()
  87. {
  88. nsresult rv;
  89. StartupCache::gStartupCache = new StartupCache();
  90. rv = StartupCache::gStartupCache->Init();
  91. if (NS_FAILED(rv)) {
  92. StartupCache::gStartupCache = nullptr;
  93. }
  94. return rv;
  95. }
  96. StaticRefPtr<StartupCache> StartupCache::gStartupCache;
  97. bool StartupCache::gShutdownInitiated;
  98. bool StartupCache::gIgnoreDiskCache;
  99. enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE;
  100. NS_IMPL_ISUPPORTS(StartupCache, nsIMemoryReporter)
  101. StartupCache::StartupCache()
  102. : mArchive(nullptr), mStartupWriteInitiated(false), mWriteThread(nullptr)
  103. { }
  104. StartupCache::~StartupCache()
  105. {
  106. if (mTimer) {
  107. mTimer->Cancel();
  108. }
  109. // Generally, the in-memory table should be empty here,
  110. // but an early shutdown means either mTimer didn't run
  111. // or the write thread is still running.
  112. WaitOnWriteThread();
  113. // If we shutdown quickly timer wont have fired. Instead of writing
  114. // it on the main thread and block the shutdown we simply wont update
  115. // the startup cache. Always do this if the file doesn't exist since
  116. // we use it part of the package step.
  117. if (!mArchive) {
  118. WriteToDisk();
  119. }
  120. UnregisterWeakMemoryReporter(this);
  121. }
  122. nsresult
  123. StartupCache::Init()
  124. {
  125. // workaround for bug 653936
  126. nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
  127. nsresult rv;
  128. // This allows to override the startup cache filename
  129. // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
  130. char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
  131. if (env) {
  132. rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
  133. } else {
  134. nsCOMPtr<nsIFile> file;
  135. rv = NS_GetSpecialDirectory("ProfLDS",
  136. getter_AddRefs(file));
  137. if (NS_FAILED(rv)) {
  138. // return silently, this will fail in mochitests's xpcshell process.
  139. return rv;
  140. }
  141. nsCOMPtr<nsIFile> profDir;
  142. NS_GetSpecialDirectory("ProfDS", getter_AddRefs(profDir));
  143. if (profDir) {
  144. bool same;
  145. if (NS_SUCCEEDED(profDir->Equals(file, &same)) && !same) {
  146. // We no longer store the startup cache in the main profile
  147. // directory, so we should cleanup the old one.
  148. if (NS_SUCCEEDED(
  149. profDir->AppendNative(NS_LITERAL_CSTRING("startupCache")))) {
  150. profDir->Remove(true);
  151. }
  152. }
  153. }
  154. rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
  155. NS_ENSURE_SUCCESS(rv, rv);
  156. // Try to create the directory if it's not there yet
  157. rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
  158. if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
  159. return rv;
  160. rv = file->AppendNative(NS_LITERAL_CSTRING(STARTUP_CACHE_NAME));
  161. NS_ENSURE_SUCCESS(rv, rv);
  162. mFile = do_QueryInterface(file);
  163. }
  164. NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
  165. mObserverService = do_GetService("@mozilla.org/observer-service;1");
  166. if (!mObserverService) {
  167. NS_WARNING("Could not get observerService.");
  168. return NS_ERROR_UNEXPECTED;
  169. }
  170. mListener = new StartupCacheListener();
  171. rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
  172. false);
  173. NS_ENSURE_SUCCESS(rv, rv);
  174. rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
  175. false);
  176. NS_ENSURE_SUCCESS(rv, rv);
  177. rv = LoadArchive(RECORD_AGE);
  178. // Sometimes we don't have a cache yet, that's ok.
  179. // If it's corrupted, just remove it and start over.
  180. if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) {
  181. NS_WARNING("Failed to load startupcache file correctly, removing!");
  182. InvalidateCache();
  183. }
  184. RegisterWeakMemoryReporter(this);
  185. return NS_OK;
  186. }
  187. /**
  188. * LoadArchive can be called from the main thread or while reloading cache on write thread.
  189. */
  190. nsresult
  191. StartupCache::LoadArchive(enum TelemetrifyAge flag)
  192. {
  193. if (gIgnoreDiskCache)
  194. return NS_ERROR_FAILURE;
  195. bool exists;
  196. mArchive = nullptr;
  197. nsresult rv = mFile->Exists(&exists);
  198. if (NS_FAILED(rv) || !exists)
  199. return NS_ERROR_FILE_NOT_FOUND;
  200. mArchive = new nsZipArchive();
  201. rv = mArchive->OpenArchive(mFile);
  202. return rv;
  203. }
  204. namespace {
  205. nsresult
  206. GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
  207. UniquePtr<char[]>* outbuf, uint32_t* length)
  208. {
  209. if (!zip)
  210. return NS_ERROR_NOT_AVAILABLE;
  211. nsZipItemPtr<char> zipItem(zip, id, doCRC);
  212. if (!zipItem)
  213. return NS_ERROR_NOT_AVAILABLE;
  214. *outbuf = zipItem.Forget();
  215. *length = zipItem.Length();
  216. return NS_OK;
  217. }
  218. } /* anonymous namespace */
  219. // NOTE: this will not find a new entry until it has been written to disk!
  220. // Consumer should take ownership of the resulting buffer.
  221. nsresult
  222. StartupCache::GetBuffer(const char* id, UniquePtr<char[]>* outbuf, uint32_t* length)
  223. {
  224. PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
  225. NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
  226. WaitOnWriteThread();
  227. if (!mStartupWriteInitiated) {
  228. CacheEntry* entry;
  229. nsDependentCString idStr(id);
  230. mTable.Get(idStr, &entry);
  231. if (entry) {
  232. *outbuf = MakeUnique<char[]>(entry->size);
  233. memcpy(outbuf->get(), entry->data.get(), entry->size);
  234. *length = entry->size;
  235. return NS_OK;
  236. }
  237. }
  238. nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
  239. if (NS_SUCCEEDED(rv))
  240. return rv;
  241. RefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
  242. // no need to checksum omnijarred entries
  243. rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
  244. if (NS_SUCCEEDED(rv))
  245. return rv;
  246. omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
  247. // no need to checksum omnijarred entries
  248. return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
  249. }
  250. // Makes a copy of the buffer, client retains ownership of inbuf.
  251. nsresult
  252. StartupCache::PutBuffer(const char* id, const char* inbuf, uint32_t len)
  253. {
  254. NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
  255. WaitOnWriteThread();
  256. if (StartupCache::gShutdownInitiated) {
  257. return NS_ERROR_NOT_AVAILABLE;
  258. }
  259. auto data = MakeUnique<char[]>(len);
  260. memcpy(data.get(), inbuf, len);
  261. nsCString idStr(id);
  262. // Cache it for now, we'll write all together later.
  263. CacheEntry* entry;
  264. if (mTable.Get(idStr)) {
  265. NS_WARNING("Existing entry in StartupCache.");
  266. // Double-caching is undesirable but not an error.
  267. return NS_OK;
  268. }
  269. #ifdef DEBUG
  270. if (mArchive) {
  271. nsZipItem* zipItem = mArchive->GetItem(id);
  272. NS_ASSERTION(zipItem == nullptr, "Existing entry in disk StartupCache.");
  273. }
  274. #endif
  275. entry = new CacheEntry(Move(data), len);
  276. mTable.Put(idStr, entry);
  277. mPendingWrites.AppendElement(idStr);
  278. return ResetStartupWriteTimer();
  279. }
  280. size_t
  281. StartupCache::SizeOfMapping()
  282. {
  283. return mArchive ? mArchive->SizeOfMapping() : 0;
  284. }
  285. size_t
  286. StartupCache::HeapSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  287. {
  288. // This function could measure more members, but they haven't been found by
  289. // DMD to be significant. They can be added later if necessary.
  290. size_t n = aMallocSizeOf(this);
  291. n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
  292. for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
  293. n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
  294. }
  295. n += mPendingWrites.ShallowSizeOfExcludingThis(aMallocSizeOf);
  296. return n;
  297. }
  298. struct CacheWriteHolder
  299. {
  300. nsCOMPtr<nsIZipWriter> writer;
  301. nsCOMPtr<nsIStringInputStream> stream;
  302. PRTime time;
  303. };
  304. static void
  305. CacheCloseHelper(const nsACString& key, const CacheEntry* data,
  306. const CacheWriteHolder* holder)
  307. {
  308. MOZ_ASSERT(data); // assert key was found in mTable.
  309. nsresult rv;
  310. nsIStringInputStream* stream = holder->stream;
  311. nsIZipWriter* writer = holder->writer;
  312. stream->ShareData(data->data.get(), data->size);
  313. #ifdef DEBUG
  314. bool hasEntry;
  315. rv = writer->HasEntry(key, &hasEntry);
  316. NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false,
  317. "Existing entry in disk StartupCache.");
  318. #endif
  319. rv = writer->AddEntryStream(key, holder->time, true, stream, false);
  320. if (NS_FAILED(rv)) {
  321. NS_WARNING("cache entry deleted but not written to disk.");
  322. }
  323. }
  324. /**
  325. * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
  326. * to make sure there isn't a write happening on another thread
  327. */
  328. void
  329. StartupCache::WriteToDisk()
  330. {
  331. nsresult rv;
  332. mStartupWriteInitiated = true;
  333. if (mTable.Count() == 0)
  334. return;
  335. nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
  336. if (!zipW)
  337. return;
  338. rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
  339. if (NS_FAILED(rv)) {
  340. NS_WARNING("could not open zipfile for write");
  341. return;
  342. }
  343. // If we didn't have an mArchive member, that means that we failed to
  344. // open the startup cache for reading. Therefore, we need to record
  345. // the time of creation in a zipfile comment; this will be useful for
  346. // Telemetry statistics.
  347. PRTime now = PR_Now();
  348. if (!mArchive) {
  349. nsCString comment;
  350. comment.Assign((char *)&now, sizeof(now));
  351. zipW->SetComment(comment);
  352. }
  353. nsCOMPtr<nsIStringInputStream> stream
  354. = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
  355. if (NS_FAILED(rv)) {
  356. NS_WARNING("Couldn't create string input stream.");
  357. return;
  358. }
  359. CacheWriteHolder holder;
  360. holder.stream = stream;
  361. holder.writer = zipW;
  362. holder.time = now;
  363. for (auto key = mPendingWrites.begin(); key != mPendingWrites.end(); key++) {
  364. CacheCloseHelper(*key, mTable.Get(*key), &holder);
  365. }
  366. mPendingWrites.Clear();
  367. mTable.Clear();
  368. // Close the archive so Windows doesn't choke.
  369. mArchive = nullptr;
  370. zipW->Close();
  371. // We succesfully wrote the archive to disk; mark the disk file as trusted
  372. gIgnoreDiskCache = false;
  373. // Our reader's view of the archive is outdated now, reload it.
  374. LoadArchive(gPostFlushAgeAction);
  375. return;
  376. }
  377. void
  378. StartupCache::InvalidateCache()
  379. {
  380. WaitOnWriteThread();
  381. mPendingWrites.Clear();
  382. mTable.Clear();
  383. mArchive = nullptr;
  384. nsresult rv = mFile->Remove(false);
  385. if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
  386. rv != NS_ERROR_FILE_NOT_FOUND) {
  387. gIgnoreDiskCache = true;
  388. return;
  389. }
  390. gIgnoreDiskCache = false;
  391. LoadArchive(gPostFlushAgeAction);
  392. }
  393. void
  394. StartupCache::IgnoreDiskCache()
  395. {
  396. gIgnoreDiskCache = true;
  397. if (gStartupCache)
  398. gStartupCache->InvalidateCache();
  399. }
  400. /*
  401. * WaitOnWriteThread() is called from a main thread to wait for the worker
  402. * thread to finish. However since the same code is used in the worker thread and
  403. * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
  404. */
  405. void
  406. StartupCache::WaitOnWriteThread()
  407. {
  408. NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
  409. if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
  410. return;
  411. PR_JoinThread(mWriteThread);
  412. mWriteThread = nullptr;
  413. }
  414. void
  415. StartupCache::ThreadedWrite(void *aClosure)
  416. {
  417. PR_SetCurrentThreadName("StartupCache");
  418. mozilla::IOInterposer::RegisterCurrentThread();
  419. /*
  420. * It is safe to use the pointer passed in aClosure to reference the
  421. * StartupCache object because the thread's lifetime is tightly coupled to
  422. * the lifetime of the StartupCache object; this thread is joined in the
  423. * StartupCache destructor, guaranteeing that this function runs if and only
  424. * if the StartupCache object is valid.
  425. */
  426. StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
  427. startupCacheObj->WriteToDisk();
  428. mozilla::IOInterposer::UnregisterCurrentThread();
  429. }
  430. /*
  431. * The write-thread is spawned on a timeout(which is reset with every write). This
  432. * can avoid a slow shutdown. After writing out the cache, the zipreader is
  433. * reloaded on the worker thread.
  434. */
  435. void
  436. StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
  437. {
  438. /*
  439. * It is safe to use the pointer passed in aClosure to reference the
  440. * StartupCache object because the timer's lifetime is tightly coupled to
  441. * the lifetime of the StartupCache object; this timer is canceled in the
  442. * StartupCache destructor, guaranteeing that this function runs if and only
  443. * if the StartupCache object is valid.
  444. */
  445. StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
  446. startupCacheObj->mWriteThread = PR_CreateThread(PR_USER_THREAD,
  447. StartupCache::ThreadedWrite,
  448. startupCacheObj,
  449. PR_PRIORITY_NORMAL,
  450. PR_GLOBAL_THREAD,
  451. PR_JOINABLE_THREAD,
  452. 0);
  453. }
  454. // We don't want to refcount StartupCache, so we'll just
  455. // hold a ref to this and pass it to observerService instead.
  456. NS_IMPL_ISUPPORTS(StartupCacheListener, nsIObserver)
  457. nsresult
  458. StartupCacheListener::Observe(nsISupports *subject, const char* topic, const char16_t* data)
  459. {
  460. StartupCache* sc = StartupCache::GetSingleton();
  461. if (!sc)
  462. return NS_OK;
  463. if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
  464. // Do not leave the thread running past xpcom shutdown
  465. sc->WaitOnWriteThread();
  466. StartupCache::gShutdownInitiated = true;
  467. } else if (strcmp(topic, "startupcache-invalidate") == 0) {
  468. sc->InvalidateCache();
  469. }
  470. return NS_OK;
  471. }
  472. nsresult
  473. StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
  474. nsIObjectOutputStream** aOutStream)
  475. {
  476. NS_ENSURE_ARG_POINTER(aStream);
  477. #ifdef DEBUG
  478. StartupCacheDebugOutputStream* stream
  479. = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
  480. NS_ADDREF(*aOutStream = stream);
  481. #else
  482. NS_ADDREF(*aOutStream = aStream);
  483. #endif
  484. return NS_OK;
  485. }
  486. nsresult
  487. StartupCache::ResetStartupWriteTimer()
  488. {
  489. mStartupWriteInitiated = false;
  490. nsresult rv;
  491. if (!mTimer)
  492. mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  493. else
  494. rv = mTimer->Cancel();
  495. NS_ENSURE_SUCCESS(rv, rv);
  496. // Wait for 10 seconds, then write out the cache.
  497. mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
  498. nsITimer::TYPE_ONE_SHOT);
  499. return NS_OK;
  500. }
  501. nsresult
  502. StartupCache::RecordAgesAlways()
  503. {
  504. gPostFlushAgeAction = RECORD_AGE;
  505. return NS_OK;
  506. }
  507. // StartupCacheDebugOutputStream implementation
  508. #ifdef DEBUG
  509. NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream,
  510. nsIBinaryOutputStream, nsIOutputStream)
  511. bool
  512. StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
  513. {
  514. nsresult rv;
  515. nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
  516. if (!classInfo) {
  517. NS_ERROR("aObject must implement nsIClassInfo");
  518. return false;
  519. }
  520. uint32_t flags;
  521. rv = classInfo->GetFlags(&flags);
  522. NS_ENSURE_SUCCESS(rv, false);
  523. if (flags & nsIClassInfo::SINGLETON)
  524. return true;
  525. nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
  526. if (key) {
  527. NS_ERROR("non-singleton aObject is referenced multiple times in this"
  528. "serialization, we don't support that.");
  529. return false;
  530. }
  531. mObjectMap->PutEntry(aObject);
  532. return true;
  533. }
  534. // nsIObjectOutputStream implementation
  535. nsresult
  536. StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
  537. {
  538. nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
  539. NS_ASSERTION(rootObject.get() == aObject,
  540. "bad call to WriteObject -- call WriteCompoundObject!");
  541. bool check = CheckReferences(aObject);
  542. NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
  543. return mBinaryStream->WriteObject(aObject, aIsStrongRef);
  544. }
  545. nsresult
  546. StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
  547. {
  548. nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
  549. NS_ASSERTION(rootObject.get() == aObject,
  550. "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
  551. bool check = CheckReferences(aObject);
  552. NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
  553. return mBinaryStream->WriteSingleRefObject(aObject);
  554. }
  555. nsresult
  556. StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
  557. const nsIID& aIID,
  558. bool aIsStrongRef)
  559. {
  560. nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
  561. nsCOMPtr<nsISupports> roundtrip;
  562. rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
  563. NS_ASSERTION(roundtrip.get() == aObject,
  564. "bad aggregation or multiple inheritance detected by call to "
  565. "WriteCompoundObject!");
  566. bool check = CheckReferences(aObject);
  567. NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
  568. return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
  569. }
  570. nsresult
  571. StartupCacheDebugOutputStream::WriteID(nsID const& aID)
  572. {
  573. return mBinaryStream->WriteID(aID);
  574. }
  575. char*
  576. StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
  577. {
  578. return mBinaryStream->GetBuffer(aLength, aAlignMask);
  579. }
  580. void
  581. StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
  582. {
  583. mBinaryStream->PutBuffer(aBuffer, aLength);
  584. }
  585. #endif //DEBUG
  586. StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
  587. NS_IMPL_ISUPPORTS(StartupCacheWrapper, nsIStartupCache)
  588. StartupCacheWrapper::~StartupCacheWrapper()
  589. {
  590. MOZ_ASSERT(gStartupCacheWrapper == this);
  591. gStartupCacheWrapper = nullptr;
  592. }
  593. StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
  594. {
  595. if (!gStartupCacheWrapper)
  596. gStartupCacheWrapper = new StartupCacheWrapper();
  597. NS_ADDREF(gStartupCacheWrapper);
  598. return gStartupCacheWrapper;
  599. }
  600. nsresult
  601. StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length)
  602. {
  603. StartupCache* sc = StartupCache::GetSingleton();
  604. if (!sc) {
  605. return NS_ERROR_NOT_INITIALIZED;
  606. }
  607. UniquePtr<char[]> buf;
  608. nsresult rv = sc->GetBuffer(id, &buf, length);
  609. *outbuf = buf.release();
  610. return rv;
  611. }
  612. nsresult
  613. StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length)
  614. {
  615. StartupCache* sc = StartupCache::GetSingleton();
  616. if (!sc) {
  617. return NS_ERROR_NOT_INITIALIZED;
  618. }
  619. return sc->PutBuffer(id, inbuf, length);
  620. }
  621. nsresult
  622. StartupCacheWrapper::InvalidateCache()
  623. {
  624. StartupCache* sc = StartupCache::GetSingleton();
  625. if (!sc) {
  626. return NS_ERROR_NOT_INITIALIZED;
  627. }
  628. sc->InvalidateCache();
  629. return NS_OK;
  630. }
  631. nsresult
  632. StartupCacheWrapper::IgnoreDiskCache()
  633. {
  634. StartupCache::IgnoreDiskCache();
  635. return NS_OK;
  636. }
  637. nsresult
  638. StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
  639. nsIObjectOutputStream** outStream)
  640. {
  641. StartupCache* sc = StartupCache::GetSingleton();
  642. if (!sc) {
  643. return NS_ERROR_NOT_INITIALIZED;
  644. }
  645. return sc->GetDebugObjectOutputStream(stream, outStream);
  646. }
  647. nsresult
  648. StartupCacheWrapper::StartupWriteComplete(bool *complete)
  649. {
  650. StartupCache* sc = StartupCache::GetSingleton();
  651. if (!sc) {
  652. return NS_ERROR_NOT_INITIALIZED;
  653. }
  654. sc->WaitOnWriteThread();
  655. *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
  656. return NS_OK;
  657. }
  658. nsresult
  659. StartupCacheWrapper::ResetStartupWriteTimer()
  660. {
  661. StartupCache* sc = StartupCache::GetSingleton();
  662. return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
  663. }
  664. nsresult
  665. StartupCacheWrapper::GetObserver(nsIObserver** obv) {
  666. StartupCache* sc = StartupCache::GetSingleton();
  667. if (!sc) {
  668. return NS_ERROR_NOT_INITIALIZED;
  669. }
  670. NS_ADDREF(*obv = sc->mListener);
  671. return NS_OK;
  672. }
  673. nsresult
  674. StartupCacheWrapper::RecordAgesAlways() {
  675. StartupCache *sc = StartupCache::GetSingleton();
  676. return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
  677. }
  678. } // namespace scache
  679. } // namespace mozilla