diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /startupcache/StartupCache.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'startupcache/StartupCache.cpp')
-rw-r--r-- | startupcache/StartupCache.cpp | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp new file mode 100644 index 0000000000..371f4795c0 --- /dev/null +++ b/startupcache/StartupCache.cpp @@ -0,0 +1,835 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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); + if (NS_FAILED(rv) || flag == IGNORE_AGE) + return rv; + + nsCString comment; + if (!mArchive->GetComment(comment)) { + return rv; + } + + const char *data; + size_t len = NS_CStringGetData(comment, &data); + PRTime creationStamp; + // We might not have a comment if the startup cache file was created + // before we started recording creation times in the comment. + if (len == sizeof(creationStamp)) { + memcpy(&creationStamp, data, len); + PRTime current = PR_Now(); + int64_t diff = current - creationStamp; + + // We can't use AccumulateTimeDelta here because we have no way of + // reifying a TimeStamp from creationStamp. + int64_t usec_per_hour = PR_USEC_PER_SEC * int64_t(3600); + int64_t hour_diff = (diff + usec_per_hour - 1) / usec_per_hour; + mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_AGE_HOURS, + hour_diff); + } + + 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; + mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_INVALID, 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 |