/src/mozilla-central/dom/storage/StorageDBThread.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef mozilla_dom_StorageDBThread_h |
8 | | #define mozilla_dom_StorageDBThread_h |
9 | | |
10 | | #include "prthread.h" |
11 | | #include "prinrval.h" |
12 | | #include "nsTArray.h" |
13 | | #include "mozilla/Atomics.h" |
14 | | #include "mozilla/Monitor.h" |
15 | | #include "mozilla/BasePrincipal.h" |
16 | | #include "mozilla/storage/StatementCache.h" |
17 | | #include "mozilla/TimeStamp.h" |
18 | | #include "nsAutoPtr.h" |
19 | | #include "nsString.h" |
20 | | #include "nsCOMPtr.h" |
21 | | #include "nsClassHashtable.h" |
22 | | #include "nsIFile.h" |
23 | | #include "nsIThreadInternal.h" |
24 | | |
25 | | class mozIStorageConnection; |
26 | | |
27 | | namespace mozilla { |
28 | | namespace dom { |
29 | | |
30 | | class LocalStorageCacheBridge; |
31 | | class StorageUsageBridge; |
32 | | class StorageUsage; |
33 | | |
34 | | typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache; |
35 | | |
36 | | // XXX Fix me! |
37 | | // 1. Move comments to StorageDBThread/StorageDBChild. |
38 | | // 2. Devirtualize relevant methods in StorageDBThread/StorageDBChild. |
39 | | // 3. Remove relevant methods in StorageDBThread/StorageDBChild that are |
40 | | // unused. |
41 | | // 4. Remove this class completely. |
42 | | // |
43 | | // See bug 1387636 for more details. |
44 | | #if 0 |
45 | | // Interface used by the cache to post operations to the asynchronous |
46 | | // database thread or process. |
47 | | class StorageDBBridge |
48 | | { |
49 | | public: |
50 | | StorageDBBridge(); |
51 | | virtual ~StorageDBBridge() {} |
52 | | |
53 | | // Ensures the database engine is started |
54 | | virtual nsresult Init() = 0; |
55 | | |
56 | | // Releases the database and disallows its usage |
57 | | virtual nsresult Shutdown() = 0; |
58 | | |
59 | | // Asynchronously fills the cache with data from the database for first use. |
60 | | // When |aPriority| is true, the preload operation is scheduled as the first |
61 | | // one. This method is responsible to keep hard reference to the cache for |
62 | | // the time of the preload or, when preload cannot be performed, call |
63 | | // LoadDone() immediately. |
64 | | virtual void AsyncPreload(LocalStorageCacheBridge* aCache, |
65 | | bool aPriority = false) = 0; |
66 | | |
67 | | // Asynchronously fill the |usage| object with actual usage of data by its |
68 | | // scope. The scope is eTLD+1 tops, never deeper subdomains. |
69 | | virtual void AsyncGetUsage(StorageUsageBridge* aUsage) = 0; |
70 | | |
71 | | // Synchronously fills the cache, when |aForceSync| is false and cache already |
72 | | // got some data before, the method waits for the running preload to finish |
73 | | virtual void SyncPreload(LocalStorageCacheBridge* aCache, |
74 | | bool aForceSync = false) = 0; |
75 | | |
76 | | // Called when an existing key is modified in the storage, schedules update to |
77 | | // the database |
78 | | virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache, |
79 | | const nsAString& aKey, |
80 | | const nsAString& aValue) = 0; |
81 | | |
82 | | // Called when an existing key is modified in the storage, schedules update to |
83 | | // the database |
84 | | virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache, |
85 | | const nsAString& aKey, |
86 | | const nsAString& aValue) = 0; |
87 | | |
88 | | // Called when an item is removed from the storage, schedules delete of the |
89 | | // key |
90 | | virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache, |
91 | | const nsAString& aKey) = 0; |
92 | | |
93 | | // Called when the whole storage is cleared by the DOM API, schedules delete |
94 | | // of the scope |
95 | | virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) = 0; |
96 | | |
97 | | // Called when chrome deletes e.g. cookies, schedules delete of the whole |
98 | | // database |
99 | | virtual void AsyncClearAll() = 0; |
100 | | |
101 | | // Called when only a domain and its subdomains is about to clear |
102 | | virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) = 0; |
103 | | |
104 | | // Called when data matching an origin pattern have to be cleared |
105 | | virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern) = 0; |
106 | | |
107 | | // Forces scheduled DB operations to be early flushed to the disk |
108 | | virtual void AsyncFlush() = 0; |
109 | | |
110 | | // Check whether the scope has any data stored on disk and is thus allowed to |
111 | | // preload |
112 | | virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0; |
113 | | }; |
114 | | #endif |
115 | | |
116 | | // The implementation of the the database engine, this directly works |
117 | | // with the sqlite or any other db API we are based on |
118 | | // This class is resposible for collecting and processing asynchronous |
119 | | // DB operations over caches (LocalStorageCache) communicating though |
120 | | // LocalStorageCacheBridge interface class |
121 | | class StorageDBThread final |
122 | | { |
123 | | public: |
124 | | class PendingOperations; |
125 | | |
126 | | // Representation of a singe database task, like adding and removing keys, |
127 | | // (pre)loading the whole origin data, cleaning. |
128 | | class DBOperation |
129 | | { |
130 | | public: |
131 | | typedef enum { |
132 | | // Only operation that reads data from the database |
133 | | opPreload, |
134 | | // The same as opPreload, just executed with highest priority |
135 | | opPreloadUrgent, |
136 | | |
137 | | // Load usage of a scope |
138 | | opGetUsage, |
139 | | |
140 | | // Operations invoked by the DOM content API |
141 | | opAddItem, |
142 | | opUpdateItem, |
143 | | opRemoveItem, |
144 | | // Clears a specific single origin data |
145 | | opClear, |
146 | | |
147 | | // Operations invoked by chrome |
148 | | |
149 | | // Clear all the data stored in the database, for all scopes, no |
150 | | // exceptions |
151 | | opClearAll, |
152 | | // Clear data under a domain and all its subdomains regardless |
153 | | // OriginAttributes value |
154 | | opClearMatchingOrigin, |
155 | | // Clear all data matching an OriginAttributesPattern regardless a domain |
156 | | opClearMatchingOriginAttributes, |
157 | | } OperationType; |
158 | | |
159 | | explicit DBOperation(const OperationType aType, |
160 | | LocalStorageCacheBridge* aCache = nullptr, |
161 | | const nsAString& aKey = EmptyString(), |
162 | | const nsAString& aValue = EmptyString()); |
163 | | DBOperation(const OperationType aType, |
164 | | StorageUsageBridge* aUsage); |
165 | | DBOperation(const OperationType aType, |
166 | | const nsACString& aOriginNoSuffix); |
167 | | DBOperation(const OperationType aType, |
168 | | const OriginAttributesPattern& aOriginNoSuffix); |
169 | | ~DBOperation(); |
170 | | |
171 | | // Executes the operation, doesn't necessarity have to be called on the I/O |
172 | | // thread |
173 | | void PerformAndFinalize(StorageDBThread* aThread); |
174 | | |
175 | | // Finalize the operation, i.e. do any internal cleanup and finish calls |
176 | | void Finalize(nsresult aRv); |
177 | | |
178 | | // The operation type |
179 | 0 | OperationType Type() const { return mType; } |
180 | | |
181 | | // The origin in the database usage format (reversed) |
182 | | const nsCString OriginNoSuffix() const; |
183 | | |
184 | | // The origin attributes suffix |
185 | | const nsCString OriginSuffix() const; |
186 | | |
187 | | // |origin suffix + origin key| the operation is working with or a scope |
188 | | // pattern to delete with simple SQL's "LIKE %" from the database. |
189 | | const nsCString Origin() const; |
190 | | |
191 | | // |origin suffix + origin key + key| the operation is working with |
192 | | const nsCString Target() const; |
193 | | |
194 | | // Pattern to delete matching data with this op |
195 | | const OriginAttributesPattern& OriginPattern() const |
196 | 0 | { |
197 | 0 | return mOriginPattern; |
198 | 0 | } |
199 | | |
200 | | private: |
201 | | // The operation implementation body |
202 | | nsresult Perform(StorageDBThread* aThread); |
203 | | |
204 | | friend class PendingOperations; |
205 | | OperationType mType; |
206 | | RefPtr<LocalStorageCacheBridge> mCache; |
207 | | RefPtr<StorageUsageBridge> mUsage; |
208 | | nsString const mKey; |
209 | | nsString const mValue; |
210 | | nsCString const mOrigin; |
211 | | OriginAttributesPattern const mOriginPattern; |
212 | | }; |
213 | | |
214 | | // Encapsulation of collective and coalescing logic for all pending operations |
215 | | // except preloads that are handled separately as priority operations |
216 | | class PendingOperations { |
217 | | public: |
218 | | PendingOperations(); |
219 | | |
220 | | // Method responsible for coalescing redundant update operations with the |
221 | | // same |Target()| or clear operations with the same or matching |Origin()| |
222 | | void Add(DBOperation* aOperation); |
223 | | |
224 | | // True when there are some scheduled operations to flush on disk |
225 | | bool HasTasks() const; |
226 | | |
227 | | // Moves collected operations to a local flat list to allow execution of the |
228 | | // operation list out of the thread lock |
229 | | bool Prepare(); |
230 | | |
231 | | // Executes the previously |Prepared()'ed| list of operations, returns |
232 | | // result, but doesn't handle it in any way in case of a failure |
233 | | nsresult Execute(StorageDBThread* aThread); |
234 | | |
235 | | // Finalizes the pending operation list, returns false when too many |
236 | | // operations failed to flush what indicates a long standing issue with the |
237 | | // database access. |
238 | | bool Finalize(nsresult aRv); |
239 | | |
240 | | // true when a clear that deletes the given origin attr pattern and/or |
241 | | // origin key is among the pending operations; when a preload for that scope |
242 | | // is being scheduled, it must be finished right away |
243 | | bool IsOriginClearPending(const nsACString& aOriginSuffix, |
244 | | const nsACString& aOriginNoSuffix) const; |
245 | | |
246 | | // Checks whether there is a pending update operation for this scope. |
247 | | bool IsOriginUpdatePending(const nsACString& aOriginSuffix, |
248 | | const nsACString& aOriginNoSuffix) const; |
249 | | |
250 | | private: |
251 | | // Returns true iff new operation is of type newType and there is a pending |
252 | | // operation of type pendingType for the same key (target). |
253 | | bool CheckForCoalesceOpportunity(DBOperation* aNewOp, |
254 | | DBOperation::OperationType aPendingType, |
255 | | DBOperation::OperationType aNewType); |
256 | | |
257 | | // List of all clearing operations, executed first |
258 | | nsClassHashtable<nsCStringHashKey, DBOperation> mClears; |
259 | | |
260 | | // List of all update/insert operations, executed as second |
261 | | nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates; |
262 | | |
263 | | // Collection of all tasks, valid only between Prepare() and Execute() |
264 | | nsTArray<nsAutoPtr<DBOperation> > mExecList; |
265 | | |
266 | | // Number of failing flush attempts |
267 | | uint32_t mFlushFailureCount; |
268 | | }; |
269 | | |
270 | | class ThreadObserver final : public nsIThreadObserver |
271 | | { |
272 | | NS_DECL_THREADSAFE_ISUPPORTS |
273 | | NS_DECL_NSITHREADOBSERVER |
274 | | |
275 | | ThreadObserver() |
276 | | : mHasPendingEvents(false) |
277 | | , mMonitor("StorageThreadMonitor") |
278 | 0 | { |
279 | 0 | } |
280 | | |
281 | 0 | bool HasPendingEvents() { |
282 | 0 | mMonitor.AssertCurrentThreadOwns(); |
283 | 0 | return mHasPendingEvents; |
284 | 0 | } |
285 | 0 | void ClearPendingEvents() { |
286 | 0 | mMonitor.AssertCurrentThreadOwns(); |
287 | 0 | mHasPendingEvents = false; |
288 | 0 | } |
289 | 0 | Monitor& GetMonitor() { return mMonitor; } |
290 | | |
291 | | private: |
292 | 0 | virtual ~ThreadObserver() {} |
293 | | bool mHasPendingEvents; |
294 | | // The monitor we drive the thread with |
295 | | Monitor mMonitor; |
296 | | }; |
297 | | |
298 | | class InitHelper; |
299 | | |
300 | | class NoteBackgroundThreadRunnable; |
301 | | |
302 | | class ShutdownRunnable : public Runnable |
303 | | { |
304 | | // Only touched on the main thread. |
305 | | bool& mDone; |
306 | | |
307 | | public: |
308 | | explicit ShutdownRunnable(bool& aDone) |
309 | | : Runnable("dom::StorageDBThread::ShutdownRunnable") |
310 | | , mDone(aDone) |
311 | 0 | { |
312 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
313 | 0 | } |
314 | | |
315 | | private: |
316 | | ~ShutdownRunnable() |
317 | 0 | { } |
318 | | |
319 | | NS_DECL_NSIRUNNABLE |
320 | | }; |
321 | | |
322 | | public: |
323 | | StorageDBThread(); |
324 | 0 | virtual ~StorageDBThread() {} |
325 | | |
326 | | static StorageDBThread* |
327 | | Get(); |
328 | | |
329 | | static StorageDBThread* |
330 | | GetOrCreate(const nsString& aProfilePath); |
331 | | |
332 | | static nsresult |
333 | | GetProfilePath(nsString& aProfilePath); |
334 | | |
335 | | virtual nsresult Init(const nsString& aProfilePath); |
336 | | |
337 | | // Flushes all uncommited data and stops the I/O thread. |
338 | | virtual nsresult Shutdown(); |
339 | | |
340 | | virtual void AsyncPreload(LocalStorageCacheBridge* aCache, |
341 | | bool aPriority = false) |
342 | 0 | { |
343 | 0 | InsertDBOp(new DBOperation(aPriority |
344 | 0 | ? DBOperation::opPreloadUrgent |
345 | 0 | : DBOperation::opPreload, |
346 | 0 | aCache)); |
347 | 0 | } |
348 | | |
349 | | virtual void SyncPreload(LocalStorageCacheBridge* aCache, |
350 | | bool aForce = false); |
351 | | |
352 | | virtual void AsyncGetUsage(StorageUsageBridge* aUsage) |
353 | 0 | { |
354 | 0 | InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); |
355 | 0 | } |
356 | | |
357 | | virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache, |
358 | | const nsAString& aKey, |
359 | | const nsAString& aValue) |
360 | 0 | { |
361 | 0 | return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, |
362 | 0 | aValue)); |
363 | 0 | } |
364 | | |
365 | | virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache, |
366 | | const nsAString& aKey, |
367 | | const nsAString& aValue) |
368 | 0 | { |
369 | 0 | return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, |
370 | 0 | aValue)); |
371 | 0 | } |
372 | | |
373 | | virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache, |
374 | | const nsAString& aKey) |
375 | 0 | { |
376 | 0 | return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); |
377 | 0 | } |
378 | | |
379 | | virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) |
380 | 0 | { |
381 | 0 | return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); |
382 | 0 | } |
383 | | |
384 | | virtual void AsyncClearAll() |
385 | 0 | { |
386 | 0 | InsertDBOp(new DBOperation(DBOperation::opClearAll)); |
387 | 0 | } |
388 | | |
389 | | virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) |
390 | 0 | { |
391 | 0 | InsertDBOp(new DBOperation(DBOperation::opClearMatchingOrigin, |
392 | 0 | aOriginNoSuffix)); |
393 | 0 | } |
394 | | |
395 | | virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern) |
396 | 0 | { |
397 | 0 | InsertDBOp(new DBOperation(DBOperation::opClearMatchingOriginAttributes, |
398 | 0 | aPattern)); |
399 | 0 | } |
400 | | |
401 | | virtual void AsyncFlush(); |
402 | | |
403 | | virtual bool ShouldPreloadOrigin(const nsACString& aOrigin); |
404 | | |
405 | | // Get the complete list of scopes having data. |
406 | | void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins); |
407 | | |
408 | | private: |
409 | | nsCOMPtr<nsIFile> mDatabaseFile; |
410 | | PRThread* mThread; |
411 | | |
412 | | // Used to observe runnables dispatched to our thread and to monitor it. |
413 | | RefPtr<ThreadObserver> mThreadObserver; |
414 | | |
415 | | // Flag to stop, protected by the monitor returned by |
416 | | // mThreadObserver->GetMonitor(). |
417 | | bool mStopIOThread; |
418 | | |
419 | | // Whether WAL is enabled |
420 | | bool mWALModeEnabled; |
421 | | |
422 | | // Whether DB has already been open, avoid races between main thread reads |
423 | | // and pending DB init in the background I/O thread |
424 | | Atomic<bool, ReleaseAcquire> mDBReady; |
425 | | |
426 | | // State of the database initiation |
427 | | nsresult mStatus; |
428 | | |
429 | | // List of origins (including origin attributes suffix) having data, for |
430 | | // optimization purposes only |
431 | | nsTHashtable<nsCStringHashKey> mOriginsHavingData; |
432 | | |
433 | | // Connection used by the worker thread for all read and write ops |
434 | | nsCOMPtr<mozIStorageConnection> mWorkerConnection; |
435 | | |
436 | | // Connection used only on the main thread for sync read operations |
437 | | nsCOMPtr<mozIStorageConnection> mReaderConnection; |
438 | | |
439 | | StatementCache mWorkerStatements; |
440 | | StatementCache mReaderStatements; |
441 | | |
442 | | // Time the first pending operation has been added to the pending operations |
443 | | // list |
444 | | TimeStamp mDirtyEpoch; |
445 | | |
446 | | // Flag to force immediate flush of all pending operations |
447 | | bool mFlushImmediately; |
448 | | |
449 | | // List of preloading operations, in chronological or priority order. |
450 | | // Executed prioritly over pending update operations. |
451 | | nsTArray<DBOperation*> mPreloads; |
452 | | |
453 | | // Collector of pending update operations |
454 | | PendingOperations mPendingTasks; |
455 | | |
456 | | // Counter of calls for thread priority rising. |
457 | | int32_t mPriorityCounter; |
458 | | |
459 | | // Helper to direct an operation to one of the arrays above; |
460 | | // also checks IsOriginClearPending for preloads |
461 | | nsresult InsertDBOp(DBOperation* aOperation); |
462 | | |
463 | | // Opens the database, first thing we do after start of the thread. |
464 | | nsresult OpenDatabaseConnection(); |
465 | | nsresult OpenAndUpdateDatabase(); |
466 | | nsresult InitDatabase(); |
467 | | nsresult ShutdownDatabase(); |
468 | | |
469 | | // Tries to establish WAL mode |
470 | | nsresult SetJournalMode(bool aIsWal); |
471 | | nsresult TryJournalMode(); |
472 | | |
473 | | // Sets the threshold for auto-checkpointing the WAL. |
474 | | nsresult ConfigureWALBehavior(); |
475 | | |
476 | | void SetHigherPriority(); |
477 | | void SetDefaultPriority(); |
478 | | |
479 | | // Ensures we flush pending tasks in some reasonble time |
480 | | void ScheduleFlush(); |
481 | | |
482 | | // Called when flush of pending tasks is being executed |
483 | | void UnscheduleFlush(); |
484 | | |
485 | | // This method is used for two purposes: |
486 | | // 1. as a value passed to monitor.Wait() method |
487 | | // 2. as in indicator that flush has to be performed |
488 | | // |
489 | | // Return: |
490 | | // - TimeDuration::Forever() when no pending tasks are scheduled |
491 | | // - Non-zero TimeDuration when tasks have been scheduled, but it |
492 | | // is still not time to perform the flush ; it is actual time to |
493 | | // wait until the flush has to happen. |
494 | | // - 0 TimeDuration when it is time to do the flush |
495 | | TimeDuration TimeUntilFlush(); |
496 | | |
497 | | // Notifies to the main thread that flush has completed |
498 | | void NotifyFlushCompletion(); |
499 | | |
500 | | // Thread loop |
501 | | static void ThreadFunc(void* aArg); |
502 | | void ThreadFunc(); |
503 | | }; |
504 | | |
505 | | } // namespace dom |
506 | | } // namespace mozilla |
507 | | |
508 | | #endif // mozilla_dom_StorageDBThread_h |