startupcache/StartupCache.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     7 #include "prio.h"
     8 #include "pldhash.h"
     9 #include "nsXPCOMStrings.h"
    10 #include "mozilla/IOInterposer.h"
    11 #include "mozilla/MemoryReporting.h"
    12 #include "mozilla/scache/StartupCache.h"
    14 #include "nsAutoPtr.h"
    15 #include "nsClassHashtable.h"
    16 #include "nsComponentManagerUtils.h"
    17 #include "nsDirectoryServiceUtils.h"
    18 #include "nsIClassInfo.h"
    19 #include "nsIFile.h"
    20 #include "nsIObserver.h"
    21 #include "nsIObserverService.h"
    22 #include "nsIOutputStream.h"
    23 #include "nsIStartupCache.h"
    24 #include "nsIStorageStream.h"
    25 #include "nsIStreamBufferAccess.h"
    26 #include "nsIStringStream.h"
    27 #include "nsISupports.h"
    28 #include "nsITimer.h"
    29 #include "nsIZipWriter.h"
    30 #include "nsIZipReader.h"
    31 #include "nsWeakReference.h"
    32 #include "nsZipArchive.h"
    33 #include "mozilla/Omnijar.h"
    34 #include "prenv.h"
    35 #include "mozilla/Telemetry.h"
    36 #include "nsThreadUtils.h"
    37 #include "nsXULAppAPI.h"
    38 #include "nsIProtocolHandler.h"
    40 #ifdef IS_BIG_ENDIAN
    41 #define SC_ENDIAN "big"
    42 #else
    43 #define SC_ENDIAN "little"
    44 #endif
    46 #if PR_BYTES_PER_WORD == 4
    47 #define SC_WORDSIZE "4"
    48 #else
    49 #define SC_WORDSIZE "8"
    50 #endif
    52 namespace mozilla {
    53 namespace scache {
    55 MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)
    57 NS_IMETHODIMP
    58 StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport,
    59                              nsISupports* aData)
    60 {
    61 #define REPORT(_path, _kind, _amount, _desc)                                \
    62   do {                                                                      \
    63     nsresult rv =                                                           \
    64       aHandleReport->Callback(EmptyCString(),                               \
    65                               NS_LITERAL_CSTRING(_path),                    \
    66                               _kind, UNITS_BYTES, _amount,                  \
    67                               NS_LITERAL_CSTRING(_desc), aData);            \
    68     NS_ENSURE_SUCCESS(rv, rv);                                              \
    69   } while (0)
    71   REPORT("explicit/startup-cache/mapping", KIND_NONHEAP,
    72          SizeOfMapping(),
    73          "Memory used to hold the mapping of the startup cache from file. "
    74          "This memory is likely to be swapped out shortly after start-up.");
    76   REPORT("explicit/startup-cache/data", KIND_HEAP,
    77          HeapSizeOfIncludingThis(StartupCacheMallocSizeOf),
    78          "Memory used by the startup cache for things other than the file "
    79          "mapping.");
    81   return NS_OK;
    82 }
    84 static const char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
    85 #if defined(XP_WIN) && defined(MOZ_METRO)
    86 static const char sMetroStartupCacheName[] = "metroStartupCache." SC_WORDSIZE "." SC_ENDIAN;
    87 #endif
    89 StartupCache*
    90 StartupCache::GetSingleton()
    91 {
    92   if (!gStartupCache) {
    93     if (XRE_GetProcessType() != GeckoProcessType_Default) {
    94       return nullptr;
    95     }
    96 #ifdef MOZ_B2G
    97     return nullptr;
    98 #endif
   100     StartupCache::InitSingleton();
   101   }
   103   return StartupCache::gStartupCache;
   104 }
   106 void
   107 StartupCache::DeleteSingleton()
   108 {
   109   StartupCache::gStartupCache = nullptr;
   110 }
   112 nsresult
   113 StartupCache::InitSingleton()
   114 {
   115   nsresult rv;
   116   StartupCache::gStartupCache = new StartupCache();
   118   rv = StartupCache::gStartupCache->Init();
   119   if (NS_FAILED(rv)) {
   120     StartupCache::gStartupCache = nullptr;
   121   }
   122   return rv;
   123 }
   125 StaticRefPtr<StartupCache> StartupCache::gStartupCache;
   126 bool StartupCache::gShutdownInitiated;
   127 bool StartupCache::gIgnoreDiskCache;
   128 enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE;
   130 NS_IMPL_ISUPPORTS(StartupCache, nsIMemoryReporter)
   132 StartupCache::StartupCache()
   133   : mArchive(nullptr), mStartupWriteInitiated(false), mWriteThread(nullptr)
   134 { }
   136 StartupCache::~StartupCache()
   137 {
   138   if (mTimer) {
   139     mTimer->Cancel();
   140   }
   142   // Generally, the in-memory table should be empty here,
   143   // but an early shutdown means either mTimer didn't run
   144   // or the write thread is still running.
   145   WaitOnWriteThread();
   147   // If we shutdown quickly timer wont have fired. Instead of writing
   148   // it on the main thread and block the shutdown we simply wont update
   149   // the startup cache. Always do this if the file doesn't exist since
   150   // we use it part of the package step.
   151   if (!mArchive) {
   152     WriteToDisk();
   153   }
   155   UnregisterWeakMemoryReporter(this);
   156 }
   158 nsresult
   159 StartupCache::Init()
   160 {
   161   // workaround for bug 653936
   162   nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
   164   nsresult rv;
   166   // This allows to override the startup cache filename
   167   // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
   168   char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
   169   if (env) {
   170     rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
   171   } else {
   172     nsCOMPtr<nsIFile> file;
   173     rv = NS_GetSpecialDirectory("ProfLDS",
   174                                 getter_AddRefs(file));
   175     if (NS_FAILED(rv)) {
   176       // return silently, this will fail in mochitests's xpcshell process.
   177       return rv;
   178     }
   180     nsCOMPtr<nsIFile> profDir;
   181     NS_GetSpecialDirectory("ProfDS", getter_AddRefs(profDir));
   182     if (profDir) {
   183       bool same;
   184       if (NS_SUCCEEDED(profDir->Equals(file, &same)) && !same) {
   185         // We no longer store the startup cache in the main profile
   186         // directory, so we should cleanup the old one.
   187         if (NS_SUCCEEDED(
   188               profDir->AppendNative(NS_LITERAL_CSTRING("startupCache")))) {
   189           profDir->Remove(true);
   190         }
   191       }
   192     }
   194     rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
   195     NS_ENSURE_SUCCESS(rv, rv);
   197     // Try to create the directory if it's not there yet
   198     rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
   199     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
   200       return rv;
   202 #if defined(XP_WIN) && defined(MOZ_METRO)
   203     if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
   204       rv = file->AppendNative(NS_LITERAL_CSTRING(sMetroStartupCacheName));
   205     } else
   206 #endif
   207     {
   208       rv = file->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
   209     }
   211     NS_ENSURE_SUCCESS(rv, rv);
   213     mFile = do_QueryInterface(file);
   214   }
   216   NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
   218   mObserverService = do_GetService("@mozilla.org/observer-service;1");
   220   if (!mObserverService) {
   221     NS_WARNING("Could not get observerService.");
   222     return NS_ERROR_UNEXPECTED;
   223   }
   225   mListener = new StartupCacheListener();
   226   rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
   227                                      false);
   228   NS_ENSURE_SUCCESS(rv, rv);
   229   rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
   230                                      false);
   231   NS_ENSURE_SUCCESS(rv, rv);
   233   rv = LoadArchive(RECORD_AGE);
   235   // Sometimes we don't have a cache yet, that's ok.
   236   // If it's corrupted, just remove it and start over.
   237   if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) {
   238     NS_WARNING("Failed to load startupcache file correctly, removing!");
   239     InvalidateCache();
   240   }
   242   RegisterWeakMemoryReporter(this);
   244   return NS_OK;
   245 }
   247 /**
   248  * LoadArchive can be called from the main thread or while reloading cache on write thread.
   249  */
   250 nsresult
   251 StartupCache::LoadArchive(enum TelemetrifyAge flag)
   252 {
   253   if (gIgnoreDiskCache)
   254     return NS_ERROR_FAILURE;
   256   bool exists;
   257   mArchive = nullptr;
   258   nsresult rv = mFile->Exists(&exists);
   259   if (NS_FAILED(rv) || !exists)
   260     return NS_ERROR_FILE_NOT_FOUND;
   262   mArchive = new nsZipArchive();
   263   rv = mArchive->OpenArchive(mFile);
   264   if (NS_FAILED(rv) || flag == IGNORE_AGE)
   265     return rv;
   267   nsCString comment;
   268   if (!mArchive->GetComment(comment)) {
   269     return rv;
   270   }
   272   const char *data;
   273   size_t len = NS_CStringGetData(comment, &data);
   274   PRTime creationStamp;
   275   // We might not have a comment if the startup cache file was created
   276   // before we started recording creation times in the comment.
   277   if (len == sizeof(creationStamp)) {
   278     memcpy(&creationStamp, data, len);
   279     PRTime current = PR_Now();
   280     int64_t diff = current - creationStamp;
   282     // We can't use AccumulateTimeDelta here because we have no way of
   283     // reifying a TimeStamp from creationStamp.
   284     int64_t usec_per_hour = PR_USEC_PER_SEC * int64_t(3600);
   285     int64_t hour_diff = (diff + usec_per_hour - 1) / usec_per_hour;
   286     mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_AGE_HOURS,
   287                                    hour_diff);
   288   }
   290   return rv;
   291 }
   293 namespace {
   295 nsresult
   296 GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
   297                         char** outbuf, uint32_t* length)
   298 {
   299   if (!zip)
   300     return NS_ERROR_NOT_AVAILABLE;
   302   nsZipItemPtr<char> zipItem(zip, id, doCRC);
   303   if (!zipItem)
   304     return NS_ERROR_NOT_AVAILABLE;
   306   *outbuf = zipItem.Forget();
   307   *length = zipItem.Length();
   308   return NS_OK;
   309 }
   311 } /* anonymous namespace */
   313 // NOTE: this will not find a new entry until it has been written to disk!
   314 // Consumer should take ownership of the resulting buffer.
   315 nsresult
   316 StartupCache::GetBuffer(const char* id, char** outbuf, uint32_t* length) 
   317 {
   318   NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
   319   WaitOnWriteThread();
   320   if (!mStartupWriteInitiated) {
   321     CacheEntry* entry; 
   322     nsDependentCString idStr(id);
   323     mTable.Get(idStr, &entry);
   324     if (entry) {
   325       *outbuf = new char[entry->size];
   326       memcpy(*outbuf, entry->data, entry->size);
   327       *length = entry->size;
   328       return NS_OK;
   329     }
   330   }
   332   nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
   333   if (NS_SUCCEEDED(rv))
   334     return rv;
   336   nsRefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
   337   // no need to checksum omnijarred entries
   338   rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
   339   if (NS_SUCCEEDED(rv))
   340     return rv;
   342   omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
   343   // no need to checksum omnijarred entries
   344   return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
   345 }
   347 // Makes a copy of the buffer, client retains ownership of inbuf.
   348 nsresult
   349 StartupCache::PutBuffer(const char* id, const char* inbuf, uint32_t len) 
   350 {
   351   NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
   352   WaitOnWriteThread();
   353   if (StartupCache::gShutdownInitiated) {
   354     return NS_ERROR_NOT_AVAILABLE;
   355   }
   357   nsAutoArrayPtr<char> data(new char[len]);
   358   memcpy(data, inbuf, len);
   360   nsDependentCString idStr(id);
   361   // Cache it for now, we'll write all together later.
   362   CacheEntry* entry; 
   364 #ifdef DEBUG
   365   mTable.Get(idStr, &entry);
   366   NS_ASSERTION(entry == nullptr, "Existing entry in StartupCache.");
   368   if (mArchive) {
   369     nsZipItem* zipItem = mArchive->GetItem(id);
   370     NS_ASSERTION(zipItem == nullptr, "Existing entry in disk StartupCache.");
   371   }
   372 #endif
   374   entry = new CacheEntry(data.forget(), len);
   375   mTable.Put(idStr, entry);
   376   return ResetStartupWriteTimer();
   377 }
   379 size_t
   380 StartupCache::SizeOfMapping()
   381 {
   382     return mArchive ? mArchive->SizeOfMapping() : 0;
   383 }
   385 size_t
   386 StartupCache::HeapSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   387 {
   388     // This function could measure more members, but they haven't been found by
   389     // DMD to be significant.  They can be added later if necessary.
   390     return aMallocSizeOf(this) +
   391            mTable.SizeOfExcludingThis(SizeOfEntryExcludingThis, aMallocSizeOf);
   392 }
   394 /* static */ size_t
   395 StartupCache::SizeOfEntryExcludingThis(const nsACString& key, const nsAutoPtr<CacheEntry>& data,
   396                                        mozilla::MallocSizeOf mallocSizeOf, void *)
   397 {
   398     return data->SizeOfExcludingThis(mallocSizeOf);
   399 }
   401 struct CacheWriteHolder
   402 {
   403   nsCOMPtr<nsIZipWriter> writer;
   404   nsCOMPtr<nsIStringInputStream> stream;
   405   PRTime time;
   406 };
   408 PLDHashOperator
   409 CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data, 
   410                  void* closure) 
   411 {
   412   nsresult rv;
   414   CacheWriteHolder* holder = (CacheWriteHolder*) closure;  
   415   nsIStringInputStream* stream = holder->stream;
   416   nsIZipWriter* writer = holder->writer;
   418   stream->ShareData(data->data, data->size);
   420 #ifdef DEBUG
   421   bool hasEntry;
   422   rv = writer->HasEntry(key, &hasEntry);
   423   NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false, 
   424                "Existing entry in disk StartupCache.");
   425 #endif
   426   rv = writer->AddEntryStream(key, holder->time, true, stream, false);
   428   if (NS_FAILED(rv)) {
   429     NS_WARNING("cache entry deleted but not written to disk.");
   430   }
   431   return PL_DHASH_REMOVE;
   432 }
   435 /** 
   436  * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
   437  * to make sure there isn't a write happening on another thread
   438  */
   439 void
   440 StartupCache::WriteToDisk() 
   441 {
   442   nsresult rv;
   443   mStartupWriteInitiated = true;
   445   if (mTable.Count() == 0)
   446     return;
   448   nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
   449   if (!zipW)
   450     return;
   452   rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
   453   if (NS_FAILED(rv)) {
   454     NS_WARNING("could not open zipfile for write");
   455     return;
   456   } 
   458   // If we didn't have an mArchive member, that means that we failed to
   459   // open the startup cache for reading.  Therefore, we need to record
   460   // the time of creation in a zipfile comment; this will be useful for
   461   // Telemetry statistics.
   462   PRTime now = PR_Now();
   463   if (!mArchive) {
   464     nsCString comment;
   465     comment.Assign((char *)&now, sizeof(now));
   466     zipW->SetComment(comment);
   467   }
   469   nsCOMPtr<nsIStringInputStream> stream 
   470     = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   471   if (NS_FAILED(rv)) {
   472     NS_WARNING("Couldn't create string input stream.");
   473     return;
   474   }
   476   CacheWriteHolder holder;
   477   holder.stream = stream;
   478   holder.writer = zipW;
   479   holder.time = now;
   481   mTable.Enumerate(CacheCloseHelper, &holder);
   483   // Close the archive so Windows doesn't choke.
   484   mArchive = nullptr;
   485   zipW->Close();
   487   // We succesfully wrote the archive to disk; mark the disk file as trusted
   488   gIgnoreDiskCache = false;
   490   // Our reader's view of the archive is outdated now, reload it.
   491   LoadArchive(gPostFlushAgeAction);
   493   return;
   494 }
   496 void
   497 StartupCache::InvalidateCache() 
   498 {
   499   WaitOnWriteThread();
   500   mTable.Clear();
   501   mArchive = nullptr;
   502   nsresult rv = mFile->Remove(false);
   503   if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
   504       rv != NS_ERROR_FILE_NOT_FOUND) {
   505     gIgnoreDiskCache = true;
   506     mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_INVALID, true);
   507     return;
   508   }
   509   gIgnoreDiskCache = false;
   510   LoadArchive(gPostFlushAgeAction);
   511 }
   513 void
   514 StartupCache::IgnoreDiskCache()
   515 {
   516   gIgnoreDiskCache = true;
   517   if (gStartupCache)
   518     gStartupCache->InvalidateCache();
   519 }
   521 /*
   522  * WaitOnWriteThread() is called from a main thread to wait for the worker
   523  * thread to finish. However since the same code is used in the worker thread and
   524  * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
   525  */
   526 void
   527 StartupCache::WaitOnWriteThread()
   528 {
   529   NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
   530   if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
   531     return;
   533   PR_JoinThread(mWriteThread);
   534   mWriteThread = nullptr;
   535 }
   537 void
   538 StartupCache::ThreadedWrite(void *aClosure)
   539 {
   540   PR_SetCurrentThreadName("StartupCache");
   541   mozilla::IOInterposer::RegisterCurrentThread();
   542   /*
   543    * It is safe to use the pointer passed in aClosure to reference the
   544    * StartupCache object because the thread's lifetime is tightly coupled to
   545    * the lifetime of the StartupCache object; this thread is joined in the
   546    * StartupCache destructor, guaranteeing that this function runs if and only
   547    * if the StartupCache object is valid.
   548    */
   549   StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
   550   startupCacheObj->WriteToDisk();
   551   mozilla::IOInterposer::UnregisterCurrentThread();
   552 }
   554 /*
   555  * The write-thread is spawned on a timeout(which is reset with every write). This
   556  * can avoid a slow shutdown. After writing out the cache, the zipreader is
   557  * reloaded on the worker thread.
   558  */
   559 void
   560 StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
   561 {
   562   /*
   563    * It is safe to use the pointer passed in aClosure to reference the
   564    * StartupCache object because the timer's lifetime is tightly coupled to
   565    * the lifetime of the StartupCache object; this timer is canceled in the
   566    * StartupCache destructor, guaranteeing that this function runs if and only
   567    * if the StartupCache object is valid.
   568    */
   569   StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
   570   startupCacheObj->mWriteThread = PR_CreateThread(PR_USER_THREAD,
   571                                                   StartupCache::ThreadedWrite,
   572                                                   startupCacheObj,
   573                                                   PR_PRIORITY_NORMAL,
   574                                                   PR_GLOBAL_THREAD,
   575                                                   PR_JOINABLE_THREAD,
   576                                                   0);
   577 }
   579 // We don't want to refcount StartupCache, so we'll just
   580 // hold a ref to this and pass it to observerService instead.
   581 NS_IMPL_ISUPPORTS(StartupCacheListener, nsIObserver)
   583 nsresult
   584 StartupCacheListener::Observe(nsISupports *subject, const char* topic, const char16_t* data)
   585 {
   586   StartupCache* sc = StartupCache::GetSingleton();
   587   if (!sc)
   588     return NS_OK;
   590   if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
   591     // Do not leave the thread running past xpcom shutdown
   592     sc->WaitOnWriteThread();
   593     StartupCache::gShutdownInitiated = true;
   594   } else if (strcmp(topic, "startupcache-invalidate") == 0) {
   595     sc->InvalidateCache();
   596   }
   597   return NS_OK;
   598 } 
   600 nsresult
   601 StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
   602                                          nsIObjectOutputStream** aOutStream) 
   603 {
   604   NS_ENSURE_ARG_POINTER(aStream);
   605 #ifdef DEBUG
   606   StartupCacheDebugOutputStream* stream
   607     = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
   608   NS_ADDREF(*aOutStream = stream);
   609 #else
   610   NS_ADDREF(*aOutStream = aStream);
   611 #endif
   613   return NS_OK;
   614 }
   616 nsresult
   617 StartupCache::ResetStartupWriteTimer()
   618 {
   619   mStartupWriteInitiated = false;
   620   nsresult rv;
   621   if (!mTimer)
   622     mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   623   else
   624     rv = mTimer->Cancel();
   625   NS_ENSURE_SUCCESS(rv, rv);
   626   // Wait for 10 seconds, then write out the cache.
   627   mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
   628                                nsITimer::TYPE_ONE_SHOT);
   629   return NS_OK;
   630 }
   632 nsresult
   633 StartupCache::RecordAgesAlways()
   634 {
   635   gPostFlushAgeAction = RECORD_AGE;
   636   return NS_OK;
   637 }
   639 // StartupCacheDebugOutputStream implementation
   640 #ifdef DEBUG
   641 NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream, 
   642                   nsIBinaryOutputStream, nsIOutputStream)
   644 bool
   645 StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
   646 {
   647   nsresult rv;
   649   nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
   650   if (!classInfo) {
   651     NS_ERROR("aObject must implement nsIClassInfo");
   652     return false;
   653   }
   655   uint32_t flags;
   656   rv = classInfo->GetFlags(&flags);
   657   NS_ENSURE_SUCCESS(rv, false);
   658   if (flags & nsIClassInfo::SINGLETON)
   659     return true;
   661   nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
   662   if (key) {
   663     NS_ERROR("non-singleton aObject is referenced multiple times in this" 
   664                   "serialization, we don't support that.");
   665     return false;
   666   }
   668   mObjectMap->PutEntry(aObject);
   669   return true;
   670 }
   672 // nsIObjectOutputStream implementation
   673 nsresult
   674 StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
   675 {
   676   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
   678   NS_ASSERTION(rootObject.get() == aObject,
   679                "bad call to WriteObject -- call WriteCompoundObject!");
   680   bool check = CheckReferences(aObject);
   681   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
   682   return mBinaryStream->WriteObject(aObject, aIsStrongRef);
   683 }
   685 nsresult
   686 StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
   687 {
   688   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
   690   NS_ASSERTION(rootObject.get() == aObject,
   691                "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
   692   bool check = CheckReferences(aObject);
   693   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
   694   return mBinaryStream->WriteSingleRefObject(aObject);
   695 }
   697 nsresult
   698 StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
   699                                                 const nsIID& aIID,
   700                                                 bool aIsStrongRef)
   701 {
   702   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
   704   nsCOMPtr<nsISupports> roundtrip;
   705   rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
   706   NS_ASSERTION(roundtrip.get() == aObject,
   707                "bad aggregation or multiple inheritance detected by call to "
   708                "WriteCompoundObject!");
   710   bool check = CheckReferences(aObject);
   711   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
   712   return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
   713 }
   715 nsresult
   716 StartupCacheDebugOutputStream::WriteID(nsID const& aID) 
   717 {
   718   return mBinaryStream->WriteID(aID);
   719 }
   721 char*
   722 StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
   723 {
   724   return mBinaryStream->GetBuffer(aLength, aAlignMask);
   725 }
   727 void
   728 StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
   729 {
   730   mBinaryStream->PutBuffer(aBuffer, aLength);
   731 }
   732 #endif //DEBUG
   734 StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
   736 NS_IMPL_ISUPPORTS(StartupCacheWrapper, nsIStartupCache)
   738 StartupCacheWrapper* StartupCacheWrapper::GetSingleton() 
   739 {
   740   if (!gStartupCacheWrapper)
   741     gStartupCacheWrapper = new StartupCacheWrapper();
   743   NS_ADDREF(gStartupCacheWrapper);
   744   return gStartupCacheWrapper;
   745 }
   747 nsresult 
   748 StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length) 
   749 {
   750   StartupCache* sc = StartupCache::GetSingleton();
   751   if (!sc) {
   752     return NS_ERROR_NOT_INITIALIZED;
   753   }
   754   return sc->GetBuffer(id, outbuf, length);
   755 }
   757 nsresult
   758 StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length) 
   759 {
   760   StartupCache* sc = StartupCache::GetSingleton();
   761   if (!sc) {
   762     return NS_ERROR_NOT_INITIALIZED;
   763   }
   764   return sc->PutBuffer(id, inbuf, length);
   765 }
   767 nsresult
   768 StartupCacheWrapper::InvalidateCache() 
   769 {
   770   StartupCache* sc = StartupCache::GetSingleton();
   771   if (!sc) {
   772     return NS_ERROR_NOT_INITIALIZED;
   773   }
   774   sc->InvalidateCache();
   775   return NS_OK;
   776 }
   778 nsresult
   779 StartupCacheWrapper::IgnoreDiskCache()
   780 {
   781   StartupCache::IgnoreDiskCache();
   782   return NS_OK;
   783 }
   785 nsresult 
   786 StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
   787                                                 nsIObjectOutputStream** outStream) 
   788 {
   789   StartupCache* sc = StartupCache::GetSingleton();
   790   if (!sc) {
   791     return NS_ERROR_NOT_INITIALIZED;
   792   }
   793   return sc->GetDebugObjectOutputStream(stream, outStream);
   794 }
   796 nsresult
   797 StartupCacheWrapper::StartupWriteComplete(bool *complete)
   798 {
   799   StartupCache* sc = StartupCache::GetSingleton();
   800   if (!sc) {
   801     return NS_ERROR_NOT_INITIALIZED;
   802   }
   803   sc->WaitOnWriteThread();
   804   *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
   805   return NS_OK;
   806 }
   808 nsresult
   809 StartupCacheWrapper::ResetStartupWriteTimer()
   810 {
   811   StartupCache* sc = StartupCache::GetSingleton();
   812   return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
   813 }
   815 nsresult
   816 StartupCacheWrapper::GetObserver(nsIObserver** obv) {
   817   StartupCache* sc = StartupCache::GetSingleton();
   818   if (!sc) {
   819     return NS_ERROR_NOT_INITIALIZED;
   820   }
   821   NS_ADDREF(*obv = sc->mListener);
   822   return NS_OK;
   823 }
   825 nsresult
   826 StartupCacheWrapper::RecordAgesAlways() {
   827   StartupCache *sc = StartupCache::GetSingleton();
   828   return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
   829 }
   831 } // namespace scache
   832 } // namespace mozilla

mercurial