|
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 DOMStorageDBThread_h___ |
|
7 #define DOMStorageDBThread_h___ |
|
8 |
|
9 #include "prthread.h" |
|
10 #include "prinrval.h" |
|
11 #include "nsTArray.h" |
|
12 #include "mozilla/Monitor.h" |
|
13 #include "mozilla/storage/StatementCache.h" |
|
14 #include "nsString.h" |
|
15 #include "nsCOMPtr.h" |
|
16 #include "nsClassHashtable.h" |
|
17 #include "nsIFile.h" |
|
18 |
|
19 class mozIStorageConnection; |
|
20 |
|
21 namespace mozilla { |
|
22 namespace dom { |
|
23 |
|
24 class DOMStorageCacheBridge; |
|
25 class DOMStorageUsageBridge; |
|
26 class DOMStorageUsage; |
|
27 |
|
28 typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache; |
|
29 |
|
30 // Interface used by the cache to post operations to the asynchronous |
|
31 // database thread or process. |
|
32 class DOMStorageDBBridge |
|
33 { |
|
34 public: |
|
35 DOMStorageDBBridge(); |
|
36 virtual ~DOMStorageDBBridge() {} |
|
37 |
|
38 // Ensures the database engine is started |
|
39 virtual nsresult Init() = 0; |
|
40 |
|
41 // Releases the database and disallows its usage |
|
42 virtual nsresult Shutdown() = 0; |
|
43 |
|
44 // Asynchronously fills the cache with data from the database for first use. |
|
45 // When |aPriority| is true, the preload operation is scheduled as the first one. |
|
46 // This method is responsible to keep hard reference to the cache for the time of |
|
47 // the preload or, when preload cannot be performed, call LoadDone() immediately. |
|
48 virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0; |
|
49 |
|
50 // Asynchronously fill the |usage| object with actual usage of data by its scope. |
|
51 // The scope is eTLD+1 tops, never deeper subdomains. |
|
52 virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0; |
|
53 |
|
54 // Synchronously fills the cache, when |aForceSync| is false and cache already got some |
|
55 // data before, the method waits for the running preload to finish |
|
56 virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0; |
|
57 |
|
58 // Called when an existing key is modified in the storage, schedules update to the database |
|
59 virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; |
|
60 |
|
61 // Called when an existing key is modified in the storage, schedules update to the database |
|
62 virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; |
|
63 |
|
64 // Called when an item is removed from the storage, schedules delete of the key |
|
65 virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0; |
|
66 |
|
67 // Called when the whole storage is cleared by the DOM API, schedules delete of the scope |
|
68 virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0; |
|
69 |
|
70 // Called when chrome deletes e.g. cookies, schedules delete of the whole database |
|
71 virtual void AsyncClearAll() = 0; |
|
72 |
|
73 // Called when only a domain and its subdomains or an app data is about to clear |
|
74 virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0; |
|
75 |
|
76 // Forces scheduled DB operations to be early flushed to the disk |
|
77 virtual void AsyncFlush() = 0; |
|
78 |
|
79 // Check whether the scope has any data stored on disk and is thus allowed to preload |
|
80 virtual bool ShouldPreloadScope(const nsACString& aScope) = 0; |
|
81 |
|
82 // Get the complete list of scopes having data |
|
83 virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0; |
|
84 }; |
|
85 |
|
86 // The implementation of the the database engine, this directly works |
|
87 // with the sqlite or any other db API we are based on |
|
88 // This class is resposible for collecting and processing asynchronous |
|
89 // DB operations over caches (DOMStorageCache) communicating though |
|
90 // DOMStorageCacheBridge interface class |
|
91 class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge |
|
92 { |
|
93 public: |
|
94 class PendingOperations; |
|
95 |
|
96 // Representation of a singe database task, like adding and removing keys, |
|
97 // (pre)loading the whole origin data, cleaning. |
|
98 class DBOperation |
|
99 { |
|
100 public: |
|
101 typedef enum { |
|
102 // Only operation that reads data from the database |
|
103 opPreload, |
|
104 // The same as opPreload, just executed with highest priority |
|
105 opPreloadUrgent, |
|
106 |
|
107 // Load usage of a scope |
|
108 opGetUsage, |
|
109 |
|
110 // Operations invoked by the DOM content API |
|
111 opAddItem, |
|
112 opUpdateItem, |
|
113 opRemoveItem, |
|
114 opClear, |
|
115 |
|
116 // Operations invoked by chrome |
|
117 opClearAll, |
|
118 opClearMatchingScope, |
|
119 } OperationType; |
|
120 |
|
121 DBOperation(const OperationType aType, |
|
122 DOMStorageCacheBridge* aCache = nullptr, |
|
123 const nsAString& aKey = EmptyString(), |
|
124 const nsAString& aValue = EmptyString()); |
|
125 DBOperation(const OperationType aType, |
|
126 DOMStorageUsageBridge* aUsage); |
|
127 DBOperation(const OperationType aType, |
|
128 const nsACString& aScope); |
|
129 ~DBOperation(); |
|
130 |
|
131 // Executes the operation, doesn't necessarity have to be called on the I/O thread |
|
132 void PerformAndFinalize(DOMStorageDBThread* aThread); |
|
133 |
|
134 // Finalize the operation, i.e. do any internal cleanup and finish calls |
|
135 void Finalize(nsresult aRv); |
|
136 |
|
137 // The operation type |
|
138 OperationType Type() { return mType; } |
|
139 |
|
140 // The operation scope (=origin) |
|
141 const nsCString Scope(); |
|
142 |
|
143 // |Scope + key| the operation is working with |
|
144 const nsCString Target(); |
|
145 |
|
146 private: |
|
147 // The operation implementation body |
|
148 nsresult Perform(DOMStorageDBThread* aThread); |
|
149 |
|
150 friend class PendingOperations; |
|
151 OperationType mType; |
|
152 nsRefPtr<DOMStorageCacheBridge> mCache; |
|
153 nsRefPtr<DOMStorageUsageBridge> mUsage; |
|
154 nsString mKey; |
|
155 nsString mValue; |
|
156 nsCString mScope; |
|
157 }; |
|
158 |
|
159 // Encapsulation of collective and coalescing logic for all pending operations |
|
160 // except preloads that are handled separately as priority operations |
|
161 class PendingOperations { |
|
162 public: |
|
163 PendingOperations(); |
|
164 |
|
165 // Method responsible for coalescing redundant update operations with the same |
|
166 // |Target()| or clear operations with the same or matching |Scope()| |
|
167 void Add(DBOperation* aOperation); |
|
168 |
|
169 // True when there are some scheduled operations to flush on disk |
|
170 bool HasTasks(); |
|
171 |
|
172 // Moves collected operations to a local flat list to allow execution of the operation |
|
173 // list out of the thread lock |
|
174 bool Prepare(); |
|
175 |
|
176 // Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't |
|
177 // handle it in any way in case of a failure |
|
178 nsresult Execute(DOMStorageDBThread* aThread); |
|
179 |
|
180 // Finalizes the pending operation list, returns false when too many operations failed |
|
181 // to flush what indicates a long standing issue with the database access. |
|
182 bool Finalize(nsresult aRv); |
|
183 |
|
184 // true when a clear that deletes the given |scope| is among the pending operations; |
|
185 // when a preload for that scope is being scheduled, it must be finished right away |
|
186 bool IsScopeClearPending(const nsACString& aScope); |
|
187 |
|
188 // Checks whether there is a pending update (or clear, actually) operation for this scope. |
|
189 bool IsScopeUpdatePending(const nsACString& aScope); |
|
190 |
|
191 private: |
|
192 // Returns true iff new operation is of type newType and there is a pending |
|
193 // operation of type pendingType for the same key (target). |
|
194 bool CheckForCoalesceOpportunity(DBOperation* aNewOp, |
|
195 DBOperation::OperationType aPendingType, |
|
196 DBOperation::OperationType aNewType); |
|
197 |
|
198 // List of all clearing operations, executed first |
|
199 nsClassHashtable<nsCStringHashKey, DBOperation> mClears; |
|
200 |
|
201 // List of all update/insert operations, executed as second |
|
202 nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates; |
|
203 |
|
204 // Collection of all tasks, valid only between Prepare() and Execute() |
|
205 nsTArray<nsAutoPtr<DBOperation> > mExecList; |
|
206 |
|
207 // Number of failing flush attempts |
|
208 uint32_t mFlushFailureCount; |
|
209 }; |
|
210 |
|
211 public: |
|
212 DOMStorageDBThread(); |
|
213 virtual ~DOMStorageDBThread() {} |
|
214 |
|
215 virtual nsresult Init(); |
|
216 virtual nsresult Shutdown(); |
|
217 |
|
218 virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) |
|
219 { InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); } |
|
220 |
|
221 virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false); |
|
222 |
|
223 virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage) |
|
224 { InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); } |
|
225 |
|
226 virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) |
|
227 { return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); } |
|
228 |
|
229 virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) |
|
230 { return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); } |
|
231 |
|
232 virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) |
|
233 { return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); } |
|
234 |
|
235 virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) |
|
236 { return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); } |
|
237 |
|
238 virtual void AsyncClearAll() |
|
239 { InsertDBOp(new DBOperation(DBOperation::opClearAll)); } |
|
240 |
|
241 virtual void AsyncClearMatchingScope(const nsACString& aScope) |
|
242 { InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); } |
|
243 |
|
244 virtual void AsyncFlush(); |
|
245 |
|
246 virtual bool ShouldPreloadScope(const nsACString& aScope); |
|
247 virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes); |
|
248 |
|
249 private: |
|
250 nsCOMPtr<nsIFile> mDatabaseFile; |
|
251 PRThread* mThread; |
|
252 |
|
253 // The monitor we drive the thread with |
|
254 Monitor mMonitor; |
|
255 |
|
256 // Flag to stop, protected by the monitor |
|
257 bool mStopIOThread; |
|
258 |
|
259 // Whether WAL is enabled |
|
260 bool mWALModeEnabled; |
|
261 |
|
262 // Whether DB has already been open, avoid races between main thread reads |
|
263 // and pending DB init in the background I/O thread |
|
264 bool mDBReady; |
|
265 |
|
266 // State of the database initiation |
|
267 nsresult mStatus; |
|
268 |
|
269 // List of scopes having data, for optimization purposes only |
|
270 nsTHashtable<nsCStringHashKey> mScopesHavingData; |
|
271 |
|
272 StatementCache mWorkerStatements; |
|
273 StatementCache mReaderStatements; |
|
274 |
|
275 // Connection used by the worker thread for all read and write ops |
|
276 nsCOMPtr<mozIStorageConnection> mWorkerConnection; |
|
277 |
|
278 // Connection used only on the main thread for sync read operations |
|
279 nsCOMPtr<mozIStorageConnection> mReaderConnection; |
|
280 |
|
281 // Time the first pending operation has been added to the pending operations |
|
282 // list |
|
283 PRIntervalTime mDirtyEpoch; |
|
284 |
|
285 // Flag to force immediate flush of all pending operations |
|
286 bool mFlushImmediately; |
|
287 |
|
288 // List of preloading operations, in chronological or priority order. |
|
289 // Executed prioritly over pending update operations. |
|
290 nsTArray<DBOperation*> mPreloads; |
|
291 |
|
292 // Collector of pending update operations |
|
293 PendingOperations mPendingTasks; |
|
294 |
|
295 // Counter of calls for thread priority rising. |
|
296 int32_t mPriorityCounter; |
|
297 |
|
298 // Helper to direct an operation to one of the arrays above; |
|
299 // also checks IsScopeClearPending for preloads |
|
300 nsresult InsertDBOp(DBOperation* aOperation); |
|
301 |
|
302 // Opens the database, first thing we do after start of the thread. |
|
303 nsresult OpenDatabaseConnection(); |
|
304 nsresult InitDatabase(); |
|
305 nsresult ShutdownDatabase(); |
|
306 |
|
307 // Tries to establish WAL mode |
|
308 nsresult SetJournalMode(bool aIsWal); |
|
309 nsresult TryJournalMode(); |
|
310 |
|
311 // Sets the threshold for auto-checkpointing the WAL. |
|
312 nsresult ConfigureWALBehavior(); |
|
313 |
|
314 void SetHigherPriority(); |
|
315 void SetDefaultPriority(); |
|
316 |
|
317 // Ensures we flush pending tasks in some reasonble time |
|
318 void ScheduleFlush(); |
|
319 |
|
320 // Called when flush of pending tasks is being executed |
|
321 void UnscheduleFlush(); |
|
322 |
|
323 // This method is used for two purposes: |
|
324 // 1. as a value passed to monitor.Wait() method |
|
325 // 2. as in indicator that flush has to be performed |
|
326 // |
|
327 // Return: |
|
328 // - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled |
|
329 // - larger then zero when tasks have been scheduled, but it is |
|
330 // still not time to perform the flush ; it is actual interval |
|
331 // time to wait until the flush has to happen |
|
332 // - 0 when it is time to do the flush |
|
333 PRIntervalTime TimeUntilFlush(); |
|
334 |
|
335 // Notifies to the main thread that flush has completed |
|
336 void NotifyFlushCompletion(); |
|
337 |
|
338 // Thread loop |
|
339 static void ThreadFunc(void* aArg); |
|
340 void ThreadFunc(); |
|
341 }; |
|
342 |
|
343 } // ::dom |
|
344 } // ::mozilla |
|
345 |
|
346 #endif /* DOMStorageDBThread_h___ */ |