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/. */
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"
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"
29 #include "nsIZipWriter.h"
30 #include "nsIZipReader.h"
31 #include "nsWeakReference.h"
32 #include "nsZipArchive.h"
33 #include "mozilla/Omnijar.h"
35 #include "mozilla/Telemetry.h"
36 #include "nsThreadUtils.h"
37 #include "nsXULAppAPI.h"
38 #include "nsIProtocolHandler.h"
41 #define SC_ENDIAN "big"
43 #define SC_ENDIAN "little"
46 #if PR_BYTES_PER_WORD == 4
47 #define SC_WORDSIZE "4"
49 #define SC_WORDSIZE "8"
55 MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf
)
58 StartupCache::CollectReports(nsIHandleReportCallback
* aHandleReport
,
59 nsISupports
* aData
, bool aAnonymize
)
61 #define REPORT(_path, _kind, _amount, _desc) \
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); \
71 REPORT("explicit/startup-cache/mapping", KIND_NONHEAP
,
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 "
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
;
90 StartupCache::GetSingleton()
93 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
96 #ifdef MOZ_DISABLE_STARTUPCACHE
100 StartupCache::InitSingleton();
103 return StartupCache::gStartupCache
;
107 StartupCache::DeleteSingleton()
109 StartupCache::gStartupCache
= nullptr;
113 StartupCache::InitSingleton()
116 StartupCache::gStartupCache
= new StartupCache();
118 rv
= StartupCache::gStartupCache
->Init();
120 StartupCache::gStartupCache
= nullptr;
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()
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.
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.
155 UnregisterWeakMemoryReporter(this);
161 // workaround for bug 653936
162 nsCOMPtr
<nsIProtocolHandler
> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX
"jar"));
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");
170 rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(env
), false, getter_AddRefs(mFile
));
172 nsCOMPtr
<nsIFile
> file
;
173 rv
= NS_GetSpecialDirectory("ProfLDS",
174 getter_AddRefs(file
));
176 // return silently, this will fail in mochitests's xpcshell process.
180 nsCOMPtr
<nsIFile
> profDir
;
181 NS_GetSpecialDirectory("ProfDS", getter_AddRefs(profDir
));
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.
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
)
202 #if defined(XP_WIN) && defined(MOZ_METRO)
203 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro
) {
204 rv
= file
->AppendNative(NS_LITERAL_CSTRING(sMetroStartupCacheName
));
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
,
228 NS_ENSURE_SUCCESS(rv
, rv
);
229 rv
= mObserverService
->AddObserver(mListener
, "startupcache-invalidate",
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!");
242 RegisterWeakMemoryReporter(this);
248 * LoadArchive can be called from the main thread or while reloading cache on write thread.
251 StartupCache::LoadArchive(enum TelemetrifyAge flag
)
253 if (gIgnoreDiskCache
)
254 return NS_ERROR_FAILURE
;
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
)
268 if (!mArchive
->GetComment(comment
)) {
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
,
296 GetBufferFromZipArchive(nsZipArchive
*zip
, bool doCRC
, const char* id
,
297 char** outbuf
, uint32_t* length
)
300 return NS_ERROR_NOT_AVAILABLE
;
302 nsZipItemPtr
<char> zipItem(zip
, id
, doCRC
);
304 return NS_ERROR_NOT_AVAILABLE
;
306 *outbuf
= zipItem
.Forget();
307 *length
= zipItem
.Length();
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.
316 StartupCache::GetBuffer(const char* id
, char** outbuf
, uint32_t* length
)
318 NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
320 if (!mStartupWriteInitiated
) {
322 nsDependentCString
idStr(id
);
323 mTable
.Get(idStr
, &entry
);
325 *outbuf
= new char[entry
->size
];
326 memcpy(*outbuf
, entry
->data
, entry
->size
);
327 *length
= entry
->size
;
332 nsresult rv
= GetBufferFromZipArchive(mArchive
, true, id
, outbuf
, length
);
333 if (NS_SUCCEEDED(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
))
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.
349 StartupCache::PutBuffer(const char* id
, const char* inbuf
, uint32_t len
)
351 NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
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.
365 mTable
.Get(idStr
, &entry
);
366 NS_ASSERTION(entry
== nullptr, "Existing entry in StartupCache.");
369 nsZipItem
* zipItem
= mArchive
->GetItem(id
);
370 NS_ASSERTION(zipItem
== nullptr, "Existing entry in disk StartupCache.");
374 entry
= new CacheEntry(data
.forget(), len
);
375 mTable
.Put(idStr
, entry
);
376 return ResetStartupWriteTimer();
380 StartupCache::SizeOfMapping()
382 return mArchive
? mArchive
->SizeOfMapping() : 0;
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
);
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
;
409 CacheCloseHelper(const nsACString
& key
, nsAutoPtr
<CacheEntry
>& data
,
414 CacheWriteHolder
* holder
= (CacheWriteHolder
*) closure
;
415 nsIStringInputStream
* stream
= holder
->stream
;
416 nsIZipWriter
* writer
= holder
->writer
;
418 stream
->ShareData(data
->data
, data
->size
);
422 rv
= writer
->HasEntry(key
, &hasEntry
);
423 NS_ASSERTION(NS_SUCCEEDED(rv
) && hasEntry
== false,
424 "Existing entry in disk StartupCache.");
426 rv
= writer
->AddEntryStream(key
, holder
->time
, true, stream
, false);
429 NS_WARNING("cache entry deleted but not written to disk.");
431 return PL_DHASH_REMOVE
;
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
440 StartupCache::WriteToDisk()
443 mStartupWriteInitiated
= true;
445 if (mTable
.Count() == 0)
448 nsCOMPtr
<nsIZipWriter
> zipW
= do_CreateInstance("@mozilla.org/zipwriter;1");
452 rv
= zipW
->Open(mFile
, PR_RDWR
| PR_CREATE_FILE
);
454 NS_WARNING("could not open zipfile for write");
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();
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
);
472 NS_WARNING("Couldn't create string input stream.");
476 CacheWriteHolder holder
;
477 holder
.stream
= stream
;
478 holder
.writer
= zipW
;
481 mTable
.Enumerate(CacheCloseHelper
, &holder
);
483 // Close the archive so Windows doesn't choke.
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
);
497 StartupCache::InvalidateCache()
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);
509 gIgnoreDiskCache
= false;
510 LoadArchive(gPostFlushAgeAction
);
514 StartupCache::IgnoreDiskCache()
516 gIgnoreDiskCache
= true;
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.
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())
533 PR_JoinThread(mWriteThread
);
534 mWriteThread
= nullptr;
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.
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
,
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
)
584 StartupCacheListener::Observe(nsISupports
*subject
, const char* topic
, const char16_t
* data
)
586 StartupCache
* sc
= StartupCache::GetSingleton();
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();
601 StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream
* aStream
,
602 nsIObjectOutputStream
** aOutStream
)
604 NS_ENSURE_ARG_POINTER(aStream
);
606 StartupCacheDebugOutputStream
* stream
607 = new StartupCacheDebugOutputStream(aStream
, &mWriteObjectMap
);
608 NS_ADDREF(*aOutStream
= stream
);
610 NS_ADDREF(*aOutStream
= aStream
);
617 StartupCache::ResetStartupWriteTimer()
619 mStartupWriteInitiated
= false;
622 mTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
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
);
633 StartupCache::RecordAgesAlways()
635 gPostFlushAgeAction
= RECORD_AGE
;
639 // StartupCacheDebugOutputStream implementation
641 NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream
, nsIObjectOutputStream
,
642 nsIBinaryOutputStream
, nsIOutputStream
)
645 StartupCacheDebugOutputStream::CheckReferences(nsISupports
* aObject
)
649 nsCOMPtr
<nsIClassInfo
> classInfo
= do_QueryInterface(aObject
);
651 NS_ERROR("aObject must implement nsIClassInfo");
656 rv
= classInfo
->GetFlags(&flags
);
657 NS_ENSURE_SUCCESS(rv
, false);
658 if (flags
& nsIClassInfo::SINGLETON
)
661 nsISupportsHashKey
* key
= mObjectMap
->GetEntry(aObject
);
663 NS_ERROR("non-singleton aObject is referenced multiple times in this"
664 "serialization, we don't support that.");
668 mObjectMap
->PutEntry(aObject
);
672 // nsIObjectOutputStream implementation
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
);
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
);
698 StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports
* aObject
,
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
);
716 StartupCacheDebugOutputStream::WriteID(nsID
const& aID
)
718 return mBinaryStream
->WriteID(aID
);
722 StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength
, uint32_t aAlignMask
)
724 return mBinaryStream
->GetBuffer(aLength
, aAlignMask
);
728 StartupCacheDebugOutputStream::PutBuffer(char* aBuffer
, uint32_t aLength
)
730 mBinaryStream
->PutBuffer(aBuffer
, aLength
);
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
;
748 StartupCacheWrapper::GetBuffer(const char* id
, char** outbuf
, uint32_t* length
)
750 StartupCache
* sc
= StartupCache::GetSingleton();
752 return NS_ERROR_NOT_INITIALIZED
;
754 return sc
->GetBuffer(id
, outbuf
, length
);
758 StartupCacheWrapper::PutBuffer(const char* id
, const char* inbuf
, uint32_t length
)
760 StartupCache
* sc
= StartupCache::GetSingleton();
762 return NS_ERROR_NOT_INITIALIZED
;
764 return sc
->PutBuffer(id
, inbuf
, length
);
768 StartupCacheWrapper::InvalidateCache()
770 StartupCache
* sc
= StartupCache::GetSingleton();
772 return NS_ERROR_NOT_INITIALIZED
;
774 sc
->InvalidateCache();
779 StartupCacheWrapper::IgnoreDiskCache()
781 StartupCache::IgnoreDiskCache();
786 StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream
* stream
,
787 nsIObjectOutputStream
** outStream
)
789 StartupCache
* sc
= StartupCache::GetSingleton();
791 return NS_ERROR_NOT_INITIALIZED
;
793 return sc
->GetDebugObjectOutputStream(stream
, outStream
);
797 StartupCacheWrapper::StartupWriteComplete(bool *complete
)
799 StartupCache
* sc
= StartupCache::GetSingleton();
801 return NS_ERROR_NOT_INITIALIZED
;
803 sc
->WaitOnWriteThread();
804 *complete
= sc
->mStartupWriteInitiated
&& sc
->mTable
.Count() == 0;
809 StartupCacheWrapper::ResetStartupWriteTimer()
811 StartupCache
* sc
= StartupCache::GetSingleton();
812 return sc
? sc
->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED
;
816 StartupCacheWrapper::GetObserver(nsIObserver
** obv
) {
817 StartupCache
* sc
= StartupCache::GetSingleton();
819 return NS_ERROR_NOT_INITIALIZED
;
821 NS_ADDREF(*obv
= sc
->mListener
);
826 StartupCacheWrapper::RecordAgesAlways() {
827 StartupCache
*sc
= StartupCache::GetSingleton();
828 return sc
? sc
->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED
;
831 } // namespace scache
832 } // namespace mozilla