123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "prio.h"
- #include "PLDHashTable.h"
- #include "nsXPCOMStrings.h"
- #include "mozilla/IOInterposer.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/scache/StartupCache.h"
- #include "nsAutoPtr.h"
- #include "nsClassHashtable.h"
- #include "nsComponentManagerUtils.h"
- #include "nsDirectoryServiceUtils.h"
- #include "nsIClassInfo.h"
- #include "nsIFile.h"
- #include "nsIObserver.h"
- #include "nsIObserverService.h"
- #include "nsIOutputStream.h"
- #include "nsIStartupCache.h"
- #include "nsIStorageStream.h"
- #include "nsIStreamBufferAccess.h"
- #include "nsIStringStream.h"
- #include "nsISupports.h"
- #include "nsITimer.h"
- #include "nsIZipWriter.h"
- #include "nsIZipReader.h"
- #include "nsWeakReference.h"
- #include "nsZipArchive.h"
- #include "mozilla/Omnijar.h"
- #include "prenv.h"
- #include "mozilla/Telemetry.h"
- #include "nsThreadUtils.h"
- #include "nsXULAppAPI.h"
- #include "nsIProtocolHandler.h"
- #ifdef IS_BIG_ENDIAN
- #define SC_ENDIAN "big"
- #else
- #define SC_ENDIAN "little"
- #endif
- #if PR_BYTES_PER_WORD == 4
- #define SC_WORDSIZE "4"
- #else
- #define SC_WORDSIZE "8"
- #endif
- namespace mozilla {
- namespace scache {
- MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)
- NS_IMETHODIMP
- StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize)
- {
- MOZ_COLLECT_REPORT(
- "explicit/startup-cache/mapping", KIND_NONHEAP, UNITS_BYTES,
- SizeOfMapping(),
- "Memory used to hold the mapping of the startup cache from file. "
- "This memory is likely to be swapped out shortly after start-up.");
- MOZ_COLLECT_REPORT(
- "explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,
- HeapSizeOfIncludingThis(StartupCacheMallocSizeOf),
- "Memory used by the startup cache for things other than the file mapping.");
- return NS_OK;
- }
- #define STARTUP_CACHE_NAME "startupCache." SC_WORDSIZE "." SC_ENDIAN
- StartupCache*
- StartupCache::GetSingleton()
- {
- if (!gStartupCache) {
- if (!XRE_IsParentProcess()) {
- return nullptr;
- }
- #ifdef MOZ_DISABLE_STARTUPCACHE
- return nullptr;
- #else
- StartupCache::InitSingleton();
- #endif
- }
- return StartupCache::gStartupCache;
- }
- void
- StartupCache::DeleteSingleton()
- {
- StartupCache::gStartupCache = nullptr;
- }
- nsresult
- StartupCache::InitSingleton()
- {
- nsresult rv;
- StartupCache::gStartupCache = new StartupCache();
- rv = StartupCache::gStartupCache->Init();
- if (NS_FAILED(rv)) {
- StartupCache::gStartupCache = nullptr;
- }
- return rv;
- }
- StaticRefPtr<StartupCache> StartupCache::gStartupCache;
- bool StartupCache::gShutdownInitiated;
- bool StartupCache::gIgnoreDiskCache;
- enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE;
- NS_IMPL_ISUPPORTS(StartupCache, nsIMemoryReporter)
- StartupCache::StartupCache()
- : mArchive(nullptr), mStartupWriteInitiated(false), mWriteThread(nullptr)
- { }
- StartupCache::~StartupCache()
- {
- if (mTimer) {
- mTimer->Cancel();
- }
- // Generally, the in-memory table should be empty here,
- // but an early shutdown means either mTimer didn't run
- // or the write thread is still running.
- WaitOnWriteThread();
- // If we shutdown quickly timer wont have fired. Instead of writing
- // it on the main thread and block the shutdown we simply wont update
- // the startup cache. Always do this if the file doesn't exist since
- // we use it part of the package step.
- if (!mArchive) {
- WriteToDisk();
- }
- UnregisterWeakMemoryReporter(this);
- }
- nsresult
- StartupCache::Init()
- {
- // workaround for bug 653936
- nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
- nsresult rv;
- // This allows to override the startup cache filename
- // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
- char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
- if (env) {
- rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
- } else {
- nsCOMPtr<nsIFile> file;
- rv = NS_GetSpecialDirectory("ProfLDS",
- getter_AddRefs(file));
- if (NS_FAILED(rv)) {
- // return silently, this will fail in mochitests's xpcshell process.
- return rv;
- }
- nsCOMPtr<nsIFile> profDir;
- NS_GetSpecialDirectory("ProfDS", getter_AddRefs(profDir));
- if (profDir) {
- bool same;
- if (NS_SUCCEEDED(profDir->Equals(file, &same)) && !same) {
- // We no longer store the startup cache in the main profile
- // directory, so we should cleanup the old one.
- if (NS_SUCCEEDED(
- profDir->AppendNative(NS_LITERAL_CSTRING("startupCache")))) {
- profDir->Remove(true);
- }
- }
- }
- rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
- NS_ENSURE_SUCCESS(rv, rv);
- // Try to create the directory if it's not there yet
- rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
- if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
- return rv;
- rv = file->AppendNative(NS_LITERAL_CSTRING(STARTUP_CACHE_NAME));
- NS_ENSURE_SUCCESS(rv, rv);
- mFile = do_QueryInterface(file);
- }
- NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
- mObserverService = do_GetService("@mozilla.org/observer-service;1");
- if (!mObserverService) {
- NS_WARNING("Could not get observerService.");
- return NS_ERROR_UNEXPECTED;
- }
- mListener = new StartupCacheListener();
- rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
- false);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
- false);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = LoadArchive(RECORD_AGE);
- // Sometimes we don't have a cache yet, that's ok.
- // If it's corrupted, just remove it and start over.
- if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) {
- NS_WARNING("Failed to load startupcache file correctly, removing!");
- InvalidateCache();
- }
- RegisterWeakMemoryReporter(this);
- return NS_OK;
- }
- /**
- * LoadArchive can be called from the main thread or while reloading cache on write thread.
- */
- nsresult
- StartupCache::LoadArchive(enum TelemetrifyAge flag)
- {
- if (gIgnoreDiskCache)
- return NS_ERROR_FAILURE;
- bool exists;
- mArchive = nullptr;
- nsresult rv = mFile->Exists(&exists);
- if (NS_FAILED(rv) || !exists)
- return NS_ERROR_FILE_NOT_FOUND;
-
- mArchive = new nsZipArchive();
- rv = mArchive->OpenArchive(mFile);
- return rv;
- }
- namespace {
- nsresult
- GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
- UniquePtr<char[]>* outbuf, uint32_t* length)
- {
- if (!zip)
- return NS_ERROR_NOT_AVAILABLE;
- nsZipItemPtr<char> zipItem(zip, id, doCRC);
- if (!zipItem)
- return NS_ERROR_NOT_AVAILABLE;
- *outbuf = zipItem.Forget();
- *length = zipItem.Length();
- return NS_OK;
- }
- } /* anonymous namespace */
- // NOTE: this will not find a new entry until it has been written to disk!
- // Consumer should take ownership of the resulting buffer.
- nsresult
- StartupCache::GetBuffer(const char* id, UniquePtr<char[]>* outbuf, uint32_t* length)
- {
- PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
- NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
- WaitOnWriteThread();
- if (!mStartupWriteInitiated) {
- CacheEntry* entry;
- nsDependentCString idStr(id);
- mTable.Get(idStr, &entry);
- if (entry) {
- *outbuf = MakeUnique<char[]>(entry->size);
- memcpy(outbuf->get(), entry->data.get(), entry->size);
- *length = entry->size;
- return NS_OK;
- }
- }
- nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
- if (NS_SUCCEEDED(rv))
- return rv;
- RefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
- // no need to checksum omnijarred entries
- rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
- if (NS_SUCCEEDED(rv))
- return rv;
- omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
- // no need to checksum omnijarred entries
- return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
- }
- // Makes a copy of the buffer, client retains ownership of inbuf.
- nsresult
- StartupCache::PutBuffer(const char* id, const char* inbuf, uint32_t len)
- {
- NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
- WaitOnWriteThread();
- if (StartupCache::gShutdownInitiated) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- auto data = MakeUnique<char[]>(len);
- memcpy(data.get(), inbuf, len);
- nsCString idStr(id);
- // Cache it for now, we'll write all together later.
- CacheEntry* entry;
-
- if (mTable.Get(idStr)) {
- NS_WARNING("Existing entry in StartupCache.");
- // Double-caching is undesirable but not an error.
- return NS_OK;
- }
- #ifdef DEBUG
- if (mArchive) {
- nsZipItem* zipItem = mArchive->GetItem(id);
- NS_ASSERTION(zipItem == nullptr, "Existing entry in disk StartupCache.");
- }
- #endif
- entry = new CacheEntry(Move(data), len);
- mTable.Put(idStr, entry);
- mPendingWrites.AppendElement(idStr);
- return ResetStartupWriteTimer();
- }
- size_t
- StartupCache::SizeOfMapping()
- {
- return mArchive ? mArchive->SizeOfMapping() : 0;
- }
- size_t
- StartupCache::HeapSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
- {
- // This function could measure more members, but they haven't been found by
- // DMD to be significant. They can be added later if necessary.
- size_t n = aMallocSizeOf(this);
- n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
- for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
- n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
- }
- n += mPendingWrites.ShallowSizeOfExcludingThis(aMallocSizeOf);
- return n;
- }
- struct CacheWriteHolder
- {
- nsCOMPtr<nsIZipWriter> writer;
- nsCOMPtr<nsIStringInputStream> stream;
- PRTime time;
- };
- static void
- CacheCloseHelper(const nsACString& key, const CacheEntry* data,
- const CacheWriteHolder* holder)
- {
- MOZ_ASSERT(data); // assert key was found in mTable.
- nsresult rv;
- nsIStringInputStream* stream = holder->stream;
- nsIZipWriter* writer = holder->writer;
- stream->ShareData(data->data.get(), data->size);
- #ifdef DEBUG
- bool hasEntry;
- rv = writer->HasEntry(key, &hasEntry);
- NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false,
- "Existing entry in disk StartupCache.");
- #endif
- rv = writer->AddEntryStream(key, holder->time, true, stream, false);
- if (NS_FAILED(rv)) {
- NS_WARNING("cache entry deleted but not written to disk.");
- }
- }
- /**
- * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
- * to make sure there isn't a write happening on another thread
- */
- void
- StartupCache::WriteToDisk()
- {
- nsresult rv;
- mStartupWriteInitiated = true;
- if (mTable.Count() == 0)
- return;
- nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
- if (!zipW)
- return;
- rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
- if (NS_FAILED(rv)) {
- NS_WARNING("could not open zipfile for write");
- return;
- }
- // If we didn't have an mArchive member, that means that we failed to
- // open the startup cache for reading. Therefore, we need to record
- // the time of creation in a zipfile comment; this will be useful for
- // Telemetry statistics.
- PRTime now = PR_Now();
- if (!mArchive) {
- nsCString comment;
- comment.Assign((char *)&now, sizeof(now));
- zipW->SetComment(comment);
- }
- nsCOMPtr<nsIStringInputStream> stream
- = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
- if (NS_FAILED(rv)) {
- NS_WARNING("Couldn't create string input stream.");
- return;
- }
- CacheWriteHolder holder;
- holder.stream = stream;
- holder.writer = zipW;
- holder.time = now;
- for (auto key = mPendingWrites.begin(); key != mPendingWrites.end(); key++) {
- CacheCloseHelper(*key, mTable.Get(*key), &holder);
- }
- mPendingWrites.Clear();
- mTable.Clear();
- // Close the archive so Windows doesn't choke.
- mArchive = nullptr;
- zipW->Close();
- // We succesfully wrote the archive to disk; mark the disk file as trusted
- gIgnoreDiskCache = false;
- // Our reader's view of the archive is outdated now, reload it.
- LoadArchive(gPostFlushAgeAction);
-
- return;
- }
- void
- StartupCache::InvalidateCache()
- {
- WaitOnWriteThread();
- mPendingWrites.Clear();
- mTable.Clear();
- mArchive = nullptr;
- nsresult rv = mFile->Remove(false);
- if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
- rv != NS_ERROR_FILE_NOT_FOUND) {
- gIgnoreDiskCache = true;
- return;
- }
- gIgnoreDiskCache = false;
- LoadArchive(gPostFlushAgeAction);
- }
- void
- StartupCache::IgnoreDiskCache()
- {
- gIgnoreDiskCache = true;
- if (gStartupCache)
- gStartupCache->InvalidateCache();
- }
- /*
- * WaitOnWriteThread() is called from a main thread to wait for the worker
- * thread to finish. However since the same code is used in the worker thread and
- * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
- */
- void
- StartupCache::WaitOnWriteThread()
- {
- NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
- if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
- return;
- PR_JoinThread(mWriteThread);
- mWriteThread = nullptr;
- }
- void
- StartupCache::ThreadedWrite(void *aClosure)
- {
- PR_SetCurrentThreadName("StartupCache");
- mozilla::IOInterposer::RegisterCurrentThread();
- /*
- * It is safe to use the pointer passed in aClosure to reference the
- * StartupCache object because the thread's lifetime is tightly coupled to
- * the lifetime of the StartupCache object; this thread is joined in the
- * StartupCache destructor, guaranteeing that this function runs if and only
- * if the StartupCache object is valid.
- */
- StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
- startupCacheObj->WriteToDisk();
- mozilla::IOInterposer::UnregisterCurrentThread();
- }
- /*
- * The write-thread is spawned on a timeout(which is reset with every write). This
- * can avoid a slow shutdown. After writing out the cache, the zipreader is
- * reloaded on the worker thread.
- */
- void
- StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
- {
- /*
- * It is safe to use the pointer passed in aClosure to reference the
- * StartupCache object because the timer's lifetime is tightly coupled to
- * the lifetime of the StartupCache object; this timer is canceled in the
- * StartupCache destructor, guaranteeing that this function runs if and only
- * if the StartupCache object is valid.
- */
- StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
- startupCacheObj->mWriteThread = PR_CreateThread(PR_USER_THREAD,
- StartupCache::ThreadedWrite,
- startupCacheObj,
- PR_PRIORITY_NORMAL,
- PR_GLOBAL_THREAD,
- PR_JOINABLE_THREAD,
- 0);
- }
- // We don't want to refcount StartupCache, so we'll just
- // hold a ref to this and pass it to observerService instead.
- NS_IMPL_ISUPPORTS(StartupCacheListener, nsIObserver)
- nsresult
- StartupCacheListener::Observe(nsISupports *subject, const char* topic, const char16_t* data)
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc)
- return NS_OK;
- if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
- // Do not leave the thread running past xpcom shutdown
- sc->WaitOnWriteThread();
- StartupCache::gShutdownInitiated = true;
- } else if (strcmp(topic, "startupcache-invalidate") == 0) {
- sc->InvalidateCache();
- }
- return NS_OK;
- }
- nsresult
- StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
- nsIObjectOutputStream** aOutStream)
- {
- NS_ENSURE_ARG_POINTER(aStream);
- #ifdef DEBUG
- StartupCacheDebugOutputStream* stream
- = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
- NS_ADDREF(*aOutStream = stream);
- #else
- NS_ADDREF(*aOutStream = aStream);
- #endif
-
- return NS_OK;
- }
- nsresult
- StartupCache::ResetStartupWriteTimer()
- {
- mStartupWriteInitiated = false;
- nsresult rv;
- if (!mTimer)
- mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
- else
- rv = mTimer->Cancel();
- NS_ENSURE_SUCCESS(rv, rv);
- // Wait for 10 seconds, then write out the cache.
- mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
- nsITimer::TYPE_ONE_SHOT);
- return NS_OK;
- }
- nsresult
- StartupCache::RecordAgesAlways()
- {
- gPostFlushAgeAction = RECORD_AGE;
- return NS_OK;
- }
- // StartupCacheDebugOutputStream implementation
- #ifdef DEBUG
- NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream,
- nsIBinaryOutputStream, nsIOutputStream)
- bool
- StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
- {
- nsresult rv;
-
- nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
- if (!classInfo) {
- NS_ERROR("aObject must implement nsIClassInfo");
- return false;
- }
-
- uint32_t flags;
- rv = classInfo->GetFlags(&flags);
- NS_ENSURE_SUCCESS(rv, false);
- if (flags & nsIClassInfo::SINGLETON)
- return true;
-
- nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
- if (key) {
- NS_ERROR("non-singleton aObject is referenced multiple times in this"
- "serialization, we don't support that.");
- return false;
- }
- mObjectMap->PutEntry(aObject);
- return true;
- }
- // nsIObjectOutputStream implementation
- nsresult
- StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
- {
- nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
-
- NS_ASSERTION(rootObject.get() == aObject,
- "bad call to WriteObject -- call WriteCompoundObject!");
- bool check = CheckReferences(aObject);
- NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
- return mBinaryStream->WriteObject(aObject, aIsStrongRef);
- }
- nsresult
- StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
- {
- nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
-
- NS_ASSERTION(rootObject.get() == aObject,
- "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
- bool check = CheckReferences(aObject);
- NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
- return mBinaryStream->WriteSingleRefObject(aObject);
- }
- nsresult
- StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
- const nsIID& aIID,
- bool aIsStrongRef)
- {
- nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
-
- nsCOMPtr<nsISupports> roundtrip;
- rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
- NS_ASSERTION(roundtrip.get() == aObject,
- "bad aggregation or multiple inheritance detected by call to "
- "WriteCompoundObject!");
- bool check = CheckReferences(aObject);
- NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
- return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
- }
- nsresult
- StartupCacheDebugOutputStream::WriteID(nsID const& aID)
- {
- return mBinaryStream->WriteID(aID);
- }
- char*
- StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
- {
- return mBinaryStream->GetBuffer(aLength, aAlignMask);
- }
- void
- StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
- {
- mBinaryStream->PutBuffer(aBuffer, aLength);
- }
- #endif //DEBUG
- StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
- NS_IMPL_ISUPPORTS(StartupCacheWrapper, nsIStartupCache)
- StartupCacheWrapper::~StartupCacheWrapper()
- {
- MOZ_ASSERT(gStartupCacheWrapper == this);
- gStartupCacheWrapper = nullptr;
- }
- StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
- {
- if (!gStartupCacheWrapper)
- gStartupCacheWrapper = new StartupCacheWrapper();
- NS_ADDREF(gStartupCacheWrapper);
- return gStartupCacheWrapper;
- }
- nsresult
- StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length)
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- UniquePtr<char[]> buf;
- nsresult rv = sc->GetBuffer(id, &buf, length);
- *outbuf = buf.release();
- return rv;
- }
- nsresult
- StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length)
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- return sc->PutBuffer(id, inbuf, length);
- }
- nsresult
- StartupCacheWrapper::InvalidateCache()
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- sc->InvalidateCache();
- return NS_OK;
- }
- nsresult
- StartupCacheWrapper::IgnoreDiskCache()
- {
- StartupCache::IgnoreDiskCache();
- return NS_OK;
- }
- nsresult
- StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
- nsIObjectOutputStream** outStream)
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- return sc->GetDebugObjectOutputStream(stream, outStream);
- }
- nsresult
- StartupCacheWrapper::StartupWriteComplete(bool *complete)
- {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- sc->WaitOnWriteThread();
- *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
- return NS_OK;
- }
- nsresult
- StartupCacheWrapper::ResetStartupWriteTimer()
- {
- StartupCache* sc = StartupCache::GetSingleton();
- return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
- }
- nsresult
- StartupCacheWrapper::GetObserver(nsIObserver** obv) {
- StartupCache* sc = StartupCache::GetSingleton();
- if (!sc) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- NS_ADDREF(*obv = sc->mListener);
- return NS_OK;
- }
- nsresult
- StartupCacheWrapper::RecordAgesAlways() {
- StartupCache *sc = StartupCache::GetSingleton();
- return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
- }
- } // namespace scache
- } // namespace mozilla
|