|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef StartupCache_h_ |
|
7 #define StartupCache_h_ |
|
8 |
|
9 #include "nsClassHashtable.h" |
|
10 #include "nsComponentManagerUtils.h" |
|
11 #include "nsZipArchive.h" |
|
12 #include "nsIStartupCache.h" |
|
13 #include "nsITimer.h" |
|
14 #include "nsIMemoryReporter.h" |
|
15 #include "nsIObserverService.h" |
|
16 #include "nsIObserver.h" |
|
17 #include "nsIOutputStream.h" |
|
18 #include "nsIFile.h" |
|
19 #include "mozilla/Attributes.h" |
|
20 #include "mozilla/MemoryReporting.h" |
|
21 #include "mozilla/StaticPtr.h" |
|
22 |
|
23 /** |
|
24 * The StartupCache is a persistent cache of simple key-value pairs, |
|
25 * where the keys are null-terminated c-strings and the values are |
|
26 * arbitrary data, passed as a (char*, size) tuple. |
|
27 * |
|
28 * Clients should use the GetSingleton() static method to access the cache. It |
|
29 * will be available from the end of XPCOM init (NS_InitXPCOM3 in nsXPComInit.cpp), |
|
30 * until XPCOM shutdown begins. The GetSingleton() method will return null if the cache |
|
31 * is unavailable. The cache is only provided for libxul builds -- |
|
32 * it will fail to link in non-libxul builds. The XPCOM interface is provided |
|
33 * only to allow compiled-code tests; clients should avoid using it. |
|
34 * |
|
35 * The API provided is very simple: GetBuffer() returns a buffer that was previously |
|
36 * stored in the cache (if any), and PutBuffer() inserts a buffer into the cache. |
|
37 * GetBuffer returns a new buffer, and the caller must take ownership of it. |
|
38 * PutBuffer will assert if the client attempts to insert a buffer with the same name as |
|
39 * an existing entry. The cache makes a copy of the passed-in buffer, so client |
|
40 * retains ownership. |
|
41 * |
|
42 * InvalidateCache() may be called if a client suspects data corruption |
|
43 * or wishes to invalidate for any other reason. This will remove all existing cache data. |
|
44 * Additionally, the static method IgnoreDiskCache() can be called if it is |
|
45 * believed that the on-disk cache file is itself corrupt. This call implicitly |
|
46 * calls InvalidateCache (if the singleton has been initialized) to ensure any |
|
47 * data already read from disk is discarded. The cache will not load data from |
|
48 * the disk file until a successful write occurs. |
|
49 * |
|
50 * Finally, getDebugObjectOutputStream() allows debug code to wrap an objectstream |
|
51 * with a debug objectstream, to check for multiply-referenced objects. These will |
|
52 * generally fail to deserialize correctly, unless they are stateless singletons or the |
|
53 * client maintains their own object data map for deserialization. |
|
54 * |
|
55 * Writes before the final-ui-startup notification are placed in an intermediate |
|
56 * cache in memory, then written out to disk at a later time, to get writes off the |
|
57 * startup path. In any case, clients should not rely on being able to GetBuffer() |
|
58 * data that is written to the cache, since it may not have been written to disk or |
|
59 * another client may have invalidated the cache. In other words, it should be used as |
|
60 * a cache only, and not a reliable persistent store. |
|
61 * |
|
62 * Some utility functions are provided in StartupCacheUtils. These functions wrap the |
|
63 * buffers into object streams, which may be useful for serializing objects. Note |
|
64 * the above caution about multiply-referenced objects, though -- the streams are just |
|
65 * as 'dumb' as the underlying buffers about multiply-referenced objects. They just |
|
66 * provide some convenience in writing out data. |
|
67 */ |
|
68 |
|
69 namespace mozilla { |
|
70 |
|
71 namespace scache { |
|
72 |
|
73 struct CacheEntry |
|
74 { |
|
75 nsAutoArrayPtr<char> data; |
|
76 uint32_t size; |
|
77 |
|
78 CacheEntry() : data(nullptr), size(0) { } |
|
79 |
|
80 // Takes possession of buf |
|
81 CacheEntry(char* buf, uint32_t len) : data(buf), size(len) { } |
|
82 |
|
83 ~CacheEntry() |
|
84 { |
|
85 } |
|
86 |
|
87 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { |
|
88 return mallocSizeOf(data); |
|
89 } |
|
90 }; |
|
91 |
|
92 // We don't want to refcount StartupCache, and ObserverService wants to |
|
93 // refcount its listeners, so we'll let it refcount this instead. |
|
94 class StartupCacheListener MOZ_FINAL : public nsIObserver |
|
95 { |
|
96 NS_DECL_THREADSAFE_ISUPPORTS |
|
97 NS_DECL_NSIOBSERVER |
|
98 }; |
|
99 |
|
100 class StartupCache : public nsIMemoryReporter |
|
101 { |
|
102 |
|
103 friend class StartupCacheListener; |
|
104 friend class StartupCacheWrapper; |
|
105 |
|
106 public: |
|
107 NS_DECL_THREADSAFE_ISUPPORTS |
|
108 NS_DECL_NSIMEMORYREPORTER |
|
109 |
|
110 // StartupCache methods. See above comments for a more detailed description. |
|
111 |
|
112 // Returns a buffer that was previously stored, caller takes ownership. |
|
113 nsresult GetBuffer(const char* id, char** outbuf, uint32_t* length); |
|
114 |
|
115 // Stores a buffer. Caller keeps ownership, we make a copy. |
|
116 nsresult PutBuffer(const char* id, const char* inbuf, uint32_t length); |
|
117 |
|
118 // Removes the cache file. |
|
119 void InvalidateCache(); |
|
120 |
|
121 // Signal that data should not be loaded from the cache file |
|
122 static void IgnoreDiskCache(); |
|
123 |
|
124 // In DEBUG builds, returns a stream that will attempt to check for |
|
125 // and disallow multiple writes of the same object. |
|
126 nsresult GetDebugObjectOutputStream(nsIObjectOutputStream* aStream, |
|
127 nsIObjectOutputStream** outStream); |
|
128 |
|
129 nsresult RecordAgesAlways(); |
|
130 |
|
131 static StartupCache* GetSingleton(); |
|
132 static void DeleteSingleton(); |
|
133 |
|
134 // This measures all the heap memory used by the StartupCache, i.e. it |
|
135 // excludes the mapping. |
|
136 size_t HeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); |
|
137 |
|
138 size_t SizeOfMapping(); |
|
139 |
|
140 private: |
|
141 StartupCache(); |
|
142 virtual ~StartupCache(); |
|
143 |
|
144 enum TelemetrifyAge { |
|
145 IGNORE_AGE = 0, |
|
146 RECORD_AGE = 1 |
|
147 }; |
|
148 static enum TelemetrifyAge gPostFlushAgeAction; |
|
149 |
|
150 nsresult LoadArchive(enum TelemetrifyAge flag); |
|
151 nsresult Init(); |
|
152 void WriteToDisk(); |
|
153 nsresult ResetStartupWriteTimer(); |
|
154 void WaitOnWriteThread(); |
|
155 |
|
156 static nsresult InitSingleton(); |
|
157 static void WriteTimeout(nsITimer *aTimer, void *aClosure); |
|
158 static void ThreadedWrite(void *aClosure); |
|
159 |
|
160 static size_t SizeOfEntryExcludingThis(const nsACString& key, |
|
161 const nsAutoPtr<CacheEntry>& data, |
|
162 mozilla::MallocSizeOf mallocSizeOf, |
|
163 void *); |
|
164 |
|
165 nsClassHashtable<nsCStringHashKey, CacheEntry> mTable; |
|
166 nsRefPtr<nsZipArchive> mArchive; |
|
167 nsCOMPtr<nsIFile> mFile; |
|
168 |
|
169 nsCOMPtr<nsIObserverService> mObserverService; |
|
170 nsRefPtr<StartupCacheListener> mListener; |
|
171 nsCOMPtr<nsITimer> mTimer; |
|
172 |
|
173 bool mStartupWriteInitiated; |
|
174 |
|
175 static StaticRefPtr<StartupCache> gStartupCache; |
|
176 static bool gShutdownInitiated; |
|
177 static bool gIgnoreDiskCache; |
|
178 PRThread *mWriteThread; |
|
179 #ifdef DEBUG |
|
180 nsTHashtable<nsISupportsHashKey> mWriteObjectMap; |
|
181 #endif |
|
182 }; |
|
183 |
|
184 // This debug outputstream attempts to detect if clients are writing multiple |
|
185 // references to the same object. We only support that if that object |
|
186 // is a singleton. |
|
187 #ifdef DEBUG |
|
188 class StartupCacheDebugOutputStream MOZ_FINAL |
|
189 : public nsIObjectOutputStream |
|
190 { |
|
191 NS_DECL_ISUPPORTS |
|
192 NS_DECL_NSIOBJECTOUTPUTSTREAM |
|
193 |
|
194 StartupCacheDebugOutputStream (nsIObjectOutputStream* binaryStream, |
|
195 nsTHashtable<nsISupportsHashKey>* objectMap) |
|
196 : mBinaryStream(binaryStream), mObjectMap(objectMap) { } |
|
197 |
|
198 NS_FORWARD_SAFE_NSIBINARYOUTPUTSTREAM(mBinaryStream) |
|
199 NS_FORWARD_SAFE_NSIOUTPUTSTREAM(mBinaryStream) |
|
200 |
|
201 bool CheckReferences(nsISupports* aObject); |
|
202 |
|
203 nsCOMPtr<nsIObjectOutputStream> mBinaryStream; |
|
204 nsTHashtable<nsISupportsHashKey> *mObjectMap; |
|
205 }; |
|
206 #endif // DEBUG |
|
207 |
|
208 // XPCOM wrapper interface provided for tests only. |
|
209 #define NS_STARTUPCACHE_CID \ |
|
210 {0xae4505a9, 0x87ab, 0x477c, \ |
|
211 {0xb5, 0x77, 0xf9, 0x23, 0x57, 0xed, 0xa8, 0x84}} |
|
212 // contract id: "@mozilla.org/startupcache/cache;1" |
|
213 |
|
214 class StartupCacheWrapper MOZ_FINAL |
|
215 : public nsIStartupCache |
|
216 { |
|
217 NS_DECL_THREADSAFE_ISUPPORTS |
|
218 NS_DECL_NSISTARTUPCACHE |
|
219 |
|
220 static StartupCacheWrapper* GetSingleton(); |
|
221 static StartupCacheWrapper *gStartupCacheWrapper; |
|
222 }; |
|
223 |
|
224 } // namespace scache |
|
225 } // namespace mozilla |
|
226 #endif //StartupCache_h_ |