LCOV - code coverage report
Current view: top level - startupcache - StartupCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 111 329 33.7 %
Date: 2017-07-14 16:53:18 Functions: 14 53 26.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13