Bumping manifests a=b2g-bump
[gecko.git] / startupcache / StartupCache.cpp
blob2ea4cebbcde9620e238590f6a8045d0a3f61f544
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, bool aAnonymize)
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;
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()
92 if (!gStartupCache) {
93 if (XRE_GetProcessType() != GeckoProcessType_Default) {
94 return nullptr;
96 #ifdef MOZ_DISABLE_STARTUP_CACHE
97 return nullptr;
98 #endif
100 StartupCache::InitSingleton();
103 return StartupCache::gStartupCache;
106 void
107 StartupCache::DeleteSingleton()
109 StartupCache::gStartupCache = nullptr;
112 nsresult
113 StartupCache::InitSingleton()
115 nsresult rv;
116 StartupCache::gStartupCache = new StartupCache();
118 rv = StartupCache::gStartupCache->Init();
119 if (NS_FAILED(rv)) {
120 StartupCache::gStartupCache = nullptr;
122 return rv;
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)
136 StartupCache::~StartupCache()
138 if (mTimer) {
139 mTimer->Cancel();
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();
155 UnregisterWeakMemoryReporter(this);
158 nsresult
159 StartupCache::Init()
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;
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);
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
208 rv = file->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
211 NS_ENSURE_SUCCESS(rv, rv);
213 mFile = do_QueryInterface(file);
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;
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();
242 RegisterWeakMemoryReporter(this);
244 return NS_OK;
248 * LoadArchive can be called from the main thread or while reloading cache on write thread.
250 nsresult
251 StartupCache::LoadArchive(enum TelemetrifyAge flag)
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;
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);
290 return rv;
293 namespace {
295 nsresult
296 GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
297 char** outbuf, uint32_t* length)
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;
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)
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;
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);
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)
351 NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
352 WaitOnWriteThread();
353 if (StartupCache::gShutdownInitiated) {
354 return NS_ERROR_NOT_AVAILABLE;
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.");
372 #endif
374 entry = new CacheEntry(data.forget(), len);
375 mTable.Put(idStr, entry);
376 return ResetStartupWriteTimer();
379 size_t
380 StartupCache::SizeOfMapping()
382 return mArchive ? mArchive->SizeOfMapping() : 0;
385 size_t
386 StartupCache::HeapSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
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);
394 /* static */ size_t
395 StartupCache::SizeOfEntryExcludingThis(const nsACString& key, const nsAutoPtr<CacheEntry>& data,
396 mozilla::MallocSizeOf mallocSizeOf, void *)
398 return data->SizeOfExcludingThis(mallocSizeOf);
401 struct CacheWriteHolder
403 nsCOMPtr<nsIZipWriter> writer;
404 nsCOMPtr<nsIStringInputStream> stream;
405 PRTime time;
408 PLDHashOperator
409 CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data,
410 void* closure)
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.");
431 return PL_DHASH_REMOVE;
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
439 void
440 StartupCache::WriteToDisk()
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;
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);
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;
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;
496 void
497 StartupCache::InvalidateCache()
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;
509 gIgnoreDiskCache = false;
510 LoadArchive(gPostFlushAgeAction);
513 void
514 StartupCache::IgnoreDiskCache()
516 gIgnoreDiskCache = true;
517 if (gStartupCache)
518 gStartupCache->InvalidateCache();
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.
526 void
527 StartupCache::WaitOnWriteThread()
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;
537 void
538 StartupCache::ThreadedWrite(void *aClosure)
540 PR_SetCurrentThreadName("StartupCache");
541 mozilla::IOInterposer::RegisterCurrentThread();
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.
549 StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
550 startupCacheObj->WriteToDisk();
551 mozilla::IOInterposer::UnregisterCurrentThread();
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.
559 void
560 StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
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.
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,
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)
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();
597 return NS_OK;
600 nsresult
601 StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
602 nsIObjectOutputStream** aOutStream)
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;
616 nsresult
617 StartupCache::ResetStartupWriteTimer()
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;
632 nsresult
633 StartupCache::RecordAgesAlways()
635 gPostFlushAgeAction = RECORD_AGE;
636 return NS_OK;
639 // StartupCacheDebugOutputStream implementation
640 #ifdef DEBUG
641 NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream,
642 nsIBinaryOutputStream, nsIOutputStream)
644 bool
645 StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
647 nsresult rv;
649 nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
650 if (!classInfo) {
651 NS_ERROR("aObject must implement nsIClassInfo");
652 return false;
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;
668 mObjectMap->PutEntry(aObject);
669 return true;
672 // nsIObjectOutputStream implementation
673 nsresult
674 StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
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);
685 nsresult
686 StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
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);
697 nsresult
698 StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
699 const nsIID& aIID,
700 bool aIsStrongRef)
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);
715 nsresult
716 StartupCacheDebugOutputStream::WriteID(nsID const& aID)
718 return mBinaryStream->WriteID(aID);
721 char*
722 StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
724 return mBinaryStream->GetBuffer(aLength, aAlignMask);
727 void
728 StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
730 mBinaryStream->PutBuffer(aBuffer, aLength);
732 #endif //DEBUG
734 StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
736 NS_IMPL_ISUPPORTS(StartupCacheWrapper, nsIStartupCache)
738 StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
740 if (!gStartupCacheWrapper)
741 gStartupCacheWrapper = new StartupCacheWrapper();
743 NS_ADDREF(gStartupCacheWrapper);
744 return gStartupCacheWrapper;
747 nsresult
748 StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length)
750 StartupCache* sc = StartupCache::GetSingleton();
751 if (!sc) {
752 return NS_ERROR_NOT_INITIALIZED;
754 return sc->GetBuffer(id, outbuf, length);
757 nsresult
758 StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length)
760 StartupCache* sc = StartupCache::GetSingleton();
761 if (!sc) {
762 return NS_ERROR_NOT_INITIALIZED;
764 return sc->PutBuffer(id, inbuf, length);
767 nsresult
768 StartupCacheWrapper::InvalidateCache()
770 StartupCache* sc = StartupCache::GetSingleton();
771 if (!sc) {
772 return NS_ERROR_NOT_INITIALIZED;
774 sc->InvalidateCache();
775 return NS_OK;
778 nsresult
779 StartupCacheWrapper::IgnoreDiskCache()
781 StartupCache::IgnoreDiskCache();
782 return NS_OK;
785 nsresult
786 StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
787 nsIObjectOutputStream** outStream)
789 StartupCache* sc = StartupCache::GetSingleton();
790 if (!sc) {
791 return NS_ERROR_NOT_INITIALIZED;
793 return sc->GetDebugObjectOutputStream(stream, outStream);
796 nsresult
797 StartupCacheWrapper::StartupWriteComplete(bool *complete)
799 StartupCache* sc = StartupCache::GetSingleton();
800 if (!sc) {
801 return NS_ERROR_NOT_INITIALIZED;
803 sc->WaitOnWriteThread();
804 *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
805 return NS_OK;
808 nsresult
809 StartupCacheWrapper::ResetStartupWriteTimer()
811 StartupCache* sc = StartupCache::GetSingleton();
812 return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
815 nsresult
816 StartupCacheWrapper::GetObserver(nsIObserver** obv) {
817 StartupCache* sc = StartupCache::GetSingleton();
818 if (!sc) {
819 return NS_ERROR_NOT_INITIALIZED;
821 NS_ADDREF(*obv = sc->mListener);
822 return NS_OK;
825 nsresult
826 StartupCacheWrapper::RecordAgesAlways() {
827 StartupCache *sc = StartupCache::GetSingleton();
828 return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
831 } // namespace scache
832 } // namespace mozilla