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/. */
7 #ifndef mozilla_dom_StorageDBThread_h
8 #define mozilla_dom_StorageDBThread_h
13 #include "mozilla/Atomics.h"
14 #include "mozilla/Monitor.h"
15 #include "mozilla/BasePrincipal.h"
16 #include "mozilla/OriginAttributes.h"
17 #include "mozilla/storage/StatementCache.h"
18 #include "mozilla/TimeStamp.h"
19 #include "mozilla/UniquePtr.h"
22 #include "nsClassHashtable.h"
24 #include "nsIThreadInternal.h"
25 #include "nsThreadUtils.h"
26 #include "nsTHashSet.h"
28 class mozIStorageConnection
;
30 namespace mozilla::dom
{
32 class LocalStorageCacheBridge
;
33 class StorageUsageBridge
;
36 using StatementCache
= mozilla::storage::StatementCache
<mozIStorageStatement
>;
39 // 1. Move comments to StorageDBThread/StorageDBChild.
40 // 2. Devirtualize relevant methods in StorageDBThread/StorageDBChild.
41 // 3. Remove relevant methods in StorageDBThread/StorageDBChild that are
43 // 4. Remove this class completely.
45 // See bug 1387636 for more details.
47 // Interface used by the cache to post operations to the asynchronous
48 // database thread or process.
53 virtual ~StorageDBBridge() {}
55 // Ensures the database engine is started
56 virtual nsresult
Init() = 0;
58 // Releases the database and disallows its usage
59 virtual nsresult
Shutdown() = 0;
61 // Asynchronously fills the cache with data from the database for first use.
62 // When |aPriority| is true, the preload operation is scheduled as the first
63 // one. This method is responsible to keep hard reference to the cache for
64 // the time of the preload or, when preload cannot be performed, call
65 // LoadDone() immediately.
66 virtual void AsyncPreload(LocalStorageCacheBridge
* aCache
,
67 bool aPriority
= false) = 0;
69 // Asynchronously fill the |usage| object with actual usage of data by its
70 // scope. The scope is eTLD+1 tops, never deeper subdomains.
71 virtual void AsyncGetUsage(StorageUsageBridge
* aUsage
) = 0;
73 // Synchronously fills the cache, when |aForceSync| is false and cache already
74 // got some data before, the method waits for the running preload to finish
75 virtual void SyncPreload(LocalStorageCacheBridge
* aCache
,
76 bool aForceSync
= false) = 0;
78 // Called when an existing key is modified in the storage, schedules update to
80 virtual nsresult
AsyncAddItem(LocalStorageCacheBridge
* aCache
,
81 const nsAString
& aKey
,
82 const nsAString
& aValue
) = 0;
84 // Called when an existing key is modified in the storage, schedules update to
86 virtual nsresult
AsyncUpdateItem(LocalStorageCacheBridge
* aCache
,
87 const nsAString
& aKey
,
88 const nsAString
& aValue
) = 0;
90 // Called when an item is removed from the storage, schedules delete of the
92 virtual nsresult
AsyncRemoveItem(LocalStorageCacheBridge
* aCache
,
93 const nsAString
& aKey
) = 0;
95 // Called when the whole storage is cleared by the DOM API, schedules delete
97 virtual nsresult
AsyncClear(LocalStorageCacheBridge
* aCache
) = 0;
99 // Called when chrome deletes e.g. cookies, schedules delete of the whole
101 virtual void AsyncClearAll() = 0;
103 // Called when only a domain and its subdomains is about to clear
104 virtual void AsyncClearMatchingOrigin(const nsACString
& aOriginNoSuffix
) = 0;
106 // Called when data matching an origin pattern have to be cleared
107 virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern
& aPattern
) = 0;
109 // Forces scheduled DB operations to be early flushed to the disk
110 virtual void AsyncFlush() = 0;
112 // Check whether the scope has any data stored on disk and is thus allowed to
114 virtual bool ShouldPreloadOrigin(const nsACString
& aOriginNoSuffix
) = 0;
118 // The implementation of the the database engine, this directly works
119 // with the sqlite or any other db API we are based on
120 // This class is resposible for collecting and processing asynchronous
121 // DB operations over caches (LocalStorageCache) communicating though
122 // LocalStorageCacheBridge interface class
123 class StorageDBThread final
{
125 class PendingOperations
;
127 // Representation of a singe database task, like adding and removing keys,
128 // (pre)loading the whole origin data, cleaning.
132 // Only operation that reads data from the database
134 // The same as opPreload, just executed with highest priority
137 // Load usage of a scope
140 // Operations invoked by the DOM content API
144 // Clears a specific single origin data
147 // Operations invoked by chrome
149 // Clear all the data stored in the database, for all scopes, no
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
,
159 explicit DBOperation(const OperationType aType
,
160 LocalStorageCacheBridge
* aCache
= nullptr,
161 const nsAString
& aKey
= u
""_ns
,
162 const nsAString
& aValue
= u
""_ns
);
163 DBOperation(const OperationType aType
, StorageUsageBridge
* aUsage
);
164 DBOperation(const OperationType aType
, const nsACString
& aOriginNoSuffix
);
165 DBOperation(const OperationType aType
,
166 const OriginAttributesPattern
& aOriginNoSuffix
);
169 // Executes the operation, doesn't necessarity have to be called on the I/O
171 void PerformAndFinalize(StorageDBThread
* aThread
);
173 // Finalize the operation, i.e. do any internal cleanup and finish calls
174 void Finalize(nsresult aRv
);
176 // The operation type
177 OperationType
Type() const { return mType
; }
179 // The origin in the database usage format (reversed)
180 const nsCString
OriginNoSuffix() const;
182 // The origin attributes suffix
183 const nsCString
OriginSuffix() const;
185 // |origin suffix + origin key| the operation is working with or a scope
186 // pattern to delete with simple SQL's "LIKE %" from the database.
187 const nsCString
Origin() const;
189 // |origin suffix + origin key + key| the operation is working with
190 const nsCString
Target() const;
192 // Pattern to delete matching data with this op
193 const OriginAttributesPattern
& OriginPattern() const {
194 return mOriginPattern
;
198 // The operation implementation body
199 nsresult
Perform(StorageDBThread
* aThread
);
201 friend class PendingOperations
;
203 RefPtr
<LocalStorageCacheBridge
> mCache
;
204 RefPtr
<StorageUsageBridge
> mUsage
;
206 nsString
const mValue
;
207 nsCString
const mOrigin
;
208 OriginAttributesPattern
const mOriginPattern
;
211 // Encapsulation of collective and coalescing logic for all pending operations
212 // except preloads that are handled separately as priority operations
213 class PendingOperations
{
217 // Method responsible for coalescing redundant update operations with the
218 // same |Target()| or clear operations with the same or matching |Origin()|
219 void Add(UniquePtr
<DBOperation
> aOperation
);
221 // True when there are some scheduled operations to flush on disk
222 bool HasTasks() const;
224 // Moves collected operations to a local flat list to allow execution of the
225 // operation list out of the thread lock
228 // Executes the previously |Prepared()'ed| list of operations, returns
229 // result, but doesn't handle it in any way in case of a failure
230 nsresult
Execute(StorageDBThread
* aThread
);
232 // Finalizes the pending operation list, returns false when too many
233 // operations failed to flush what indicates a long standing issue with the
235 bool Finalize(nsresult aRv
);
237 // true when a clear that deletes the given origin attr pattern and/or
238 // origin key is among the pending operations; when a preload for that scope
239 // is being scheduled, it must be finished right away
240 bool IsOriginClearPending(const nsACString
& aOriginSuffix
,
241 const nsACString
& aOriginNoSuffix
) const;
243 // Checks whether there is a pending update operation for this scope.
244 bool IsOriginUpdatePending(const nsACString
& aOriginSuffix
,
245 const nsACString
& aOriginNoSuffix
) const;
248 // Returns true iff new operation is of type newType and there is a pending
249 // operation of type pendingType for the same key (target).
250 bool CheckForCoalesceOpportunity(DBOperation
* aNewOp
,
251 DBOperation::OperationType aPendingType
,
252 DBOperation::OperationType aNewType
);
254 // List of all clearing operations, executed first
255 nsClassHashtable
<nsCStringHashKey
, DBOperation
> mClears
;
257 // List of all update/insert operations, executed as second
258 nsClassHashtable
<nsCStringHashKey
, DBOperation
> mUpdates
;
260 // Collection of all tasks, valid only between Prepare() and Execute()
261 nsTArray
<UniquePtr
<DBOperation
> > mExecList
;
263 // Number of failing flush attempts
264 uint32_t mFlushFailureCount
;
267 class ThreadObserver final
: public nsIThreadObserver
{
268 NS_DECL_THREADSAFE_ISUPPORTS
269 NS_DECL_NSITHREADOBSERVER
272 : mHasPendingEvents(false), mMonitor("StorageThreadMonitor") {}
274 bool HasPendingEvents() {
275 mMonitor
.AssertCurrentThreadOwns();
276 return mHasPendingEvents
;
278 void ClearPendingEvents() {
279 mMonitor
.AssertCurrentThreadOwns();
280 mHasPendingEvents
= false;
282 Monitor
& GetMonitor() { return mMonitor
; }
285 virtual ~ThreadObserver() = default;
286 bool mHasPendingEvents
;
287 // The monitor we drive the thread with
288 Monitor mMonitor MOZ_UNANNOTATED
;
293 class NoteBackgroundThreadRunnable
;
295 class ShutdownRunnable
: public Runnable
{
296 // Expected to be only 0 or 1.
297 const uint32_t mPrivateBrowsingId
;
298 // Only touched on the main thread.
302 explicit ShutdownRunnable(const uint32_t aPrivateBrowsingId
, bool& aDone
)
303 : Runnable("dom::StorageDBThread::ShutdownRunnable"),
304 mPrivateBrowsingId(aPrivateBrowsingId
),
306 MOZ_ASSERT(NS_IsMainThread());
310 ~ShutdownRunnable() = default;
316 explicit StorageDBThread(uint32_t aPrivateBrowsingId
);
317 virtual ~StorageDBThread() = default;
319 static StorageDBThread
* Get(uint32_t aPrivateBrowsingId
);
321 static StorageDBThread
* GetOrCreate(const nsString
& aProfilePath
,
322 uint32_t aPrivateBrowsingId
);
324 static nsresult
GetProfilePath(nsString
& aProfilePath
);
326 virtual nsresult
Init(const nsString
& aProfilePath
);
328 // Flushes all uncommited data and stops the I/O thread.
329 virtual nsresult
Shutdown();
331 virtual void AsyncPreload(LocalStorageCacheBridge
* aCache
,
332 bool aPriority
= false) {
333 InsertDBOp(MakeUnique
<DBOperation
>(
334 aPriority
? DBOperation::opPreloadUrgent
: DBOperation::opPreload
,
338 virtual void SyncPreload(LocalStorageCacheBridge
* aCache
,
339 bool aForce
= false);
341 virtual void AsyncGetUsage(StorageUsageBridge
* aUsage
) {
342 InsertDBOp(MakeUnique
<DBOperation
>(DBOperation::opGetUsage
, aUsage
));
345 virtual nsresult
AsyncAddItem(LocalStorageCacheBridge
* aCache
,
346 const nsAString
& aKey
,
347 const nsAString
& aValue
) {
349 MakeUnique
<DBOperation
>(DBOperation::opAddItem
, aCache
, aKey
, aValue
));
352 virtual nsresult
AsyncUpdateItem(LocalStorageCacheBridge
* aCache
,
353 const nsAString
& aKey
,
354 const nsAString
& aValue
) {
355 return InsertDBOp(MakeUnique
<DBOperation
>(DBOperation::opUpdateItem
, aCache
,
359 virtual nsresult
AsyncRemoveItem(LocalStorageCacheBridge
* aCache
,
360 const nsAString
& aKey
) {
362 MakeUnique
<DBOperation
>(DBOperation::opRemoveItem
, aCache
, aKey
));
365 virtual nsresult
AsyncClear(LocalStorageCacheBridge
* aCache
) {
366 return InsertDBOp(MakeUnique
<DBOperation
>(DBOperation::opClear
, aCache
));
369 virtual void AsyncClearAll() {
370 InsertDBOp(MakeUnique
<DBOperation
>(DBOperation::opClearAll
));
373 virtual void AsyncClearMatchingOrigin(const nsACString
& aOriginNoSuffix
) {
374 InsertDBOp(MakeUnique
<DBOperation
>(DBOperation::opClearMatchingOrigin
,
378 virtual void AsyncClearMatchingOriginAttributes(
379 const OriginAttributesPattern
& aPattern
) {
380 InsertDBOp(MakeUnique
<DBOperation
>(
381 DBOperation::opClearMatchingOriginAttributes
, aPattern
));
384 virtual void AsyncFlush();
386 virtual bool ShouldPreloadOrigin(const nsACString
& aOrigin
);
388 // Get the complete list of scopes having data.
389 void GetOriginsHavingData(nsTArray
<nsCString
>* aOrigins
);
392 nsCOMPtr
<nsIFile
> mDatabaseFile
;
395 // Used to observe runnables dispatched to our thread and to monitor it.
396 RefPtr
<ThreadObserver
> mThreadObserver
;
398 // Flag to stop, protected by the monitor returned by
399 // mThreadObserver->GetMonitor().
402 // Whether WAL is enabled
403 bool mWALModeEnabled
;
405 // Whether DB has already been open, avoid races between main thread reads
406 // and pending DB init in the background I/O thread
407 Atomic
<bool, ReleaseAcquire
> mDBReady
;
409 // State of the database initiation
412 // List of origins (including origin attributes suffix) having data, for
413 // optimization purposes only
414 nsTHashSet
<nsCString
> mOriginsHavingData
;
416 // Connection used by the worker thread for all read and write ops
417 nsCOMPtr
<mozIStorageConnection
> mWorkerConnection
;
419 // Connection used only on the main thread for sync read operations
420 nsCOMPtr
<mozIStorageConnection
> mReaderConnection
;
422 StatementCache mWorkerStatements
;
423 StatementCache mReaderStatements
;
425 // Time the first pending operation has been added to the pending operations
427 TimeStamp mDirtyEpoch
;
429 // Flag to force immediate flush of all pending operations
430 bool mFlushImmediately
;
432 // List of preloading operations, in chronological or priority order.
433 // Executed prioritly over pending update operations.
434 nsTArray
<DBOperation
*> mPreloads
;
436 // Collector of pending update operations
437 PendingOperations mPendingTasks
;
439 // Expected to be only 0 or 1.
440 const uint32_t mPrivateBrowsingId
;
442 // Counter of calls for thread priority rising.
443 int32_t mPriorityCounter
;
445 // Helper to direct an operation to one of the arrays above;
446 // also checks IsOriginClearPending for preloads
447 nsresult
InsertDBOp(UniquePtr
<DBOperation
> aOperation
);
449 // Opens the database, first thing we do after start of the thread.
450 nsresult
OpenDatabaseConnection();
451 nsresult
OpenAndUpdateDatabase();
452 nsresult
InitDatabase();
453 nsresult
ShutdownDatabase();
455 // Tries to establish WAL mode
456 nsresult
SetJournalMode(bool aIsWal
);
457 nsresult
TryJournalMode();
459 // Sets the threshold for auto-checkpointing the WAL.
460 nsresult
ConfigureWALBehavior();
462 void SetHigherPriority();
463 void SetDefaultPriority();
465 // Ensures we flush pending tasks in some reasonble time
466 void ScheduleFlush();
468 // Called when flush of pending tasks is being executed
469 void UnscheduleFlush();
471 // This method is used for two purposes:
472 // 1. as a value passed to monitor.Wait() method
473 // 2. as in indicator that flush has to be performed
476 // - TimeDuration::Forever() when no pending tasks are scheduled
477 // - Non-zero TimeDuration when tasks have been scheduled, but it
478 // is still not time to perform the flush ; it is actual time to
479 // wait until the flush has to happen.
480 // - 0 TimeDuration when it is time to do the flush
481 TimeDuration
TimeUntilFlush();
483 // Notifies to the main thread that flush has completed
484 void NotifyFlushCompletion();
487 static void ThreadFunc(void* aArg
);
491 } // namespace mozilla::dom
493 #endif // mozilla_dom_StorageDBThread_h