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