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