summaryrefslogtreecommitdiffstats
path: root/dom/storage/StorageDBThread.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/storage/StorageDBThread.h')
-rw-r--r--dom/storage/StorageDBThread.h493
1 files changed, 493 insertions, 0 deletions
diff --git a/dom/storage/StorageDBThread.h b/dom/storage/StorageDBThread.h
new file mode 100644
index 0000000000..7708de5a97
--- /dev/null
+++ b/dom/storage/StorageDBThread.h
@@ -0,0 +1,493 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_StorageDBThread_h
+#define mozilla_dom_StorageDBThread_h
+
+#include "prthread.h"
+#include "prinrval.h"
+#include "nsTArray.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/OriginAttributes.h"
+#include "mozilla/storage/StatementCache.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsClassHashtable.h"
+#include "nsIFile.h"
+#include "nsIThreadInternal.h"
+#include "nsThreadUtils.h"
+#include "nsTHashSet.h"
+
+class mozIStorageConnection;
+
+namespace mozilla::dom {
+
+class LocalStorageCacheBridge;
+class StorageUsageBridge;
+class StorageUsage;
+
+using StatementCache = mozilla::storage::StatementCache<mozIStorageStatement>;
+
+// XXX Fix me!
+// 1. Move comments to StorageDBThread/StorageDBChild.
+// 2. Devirtualize relevant methods in StorageDBThread/StorageDBChild.
+// 3. Remove relevant methods in StorageDBThread/StorageDBChild that are
+// unused.
+// 4. Remove this class completely.
+//
+// See bug 1387636 for more details.
+#if 0
+// Interface used by the cache to post operations to the asynchronous
+// database thread or process.
+class StorageDBBridge
+{
+public:
+ StorageDBBridge();
+ virtual ~StorageDBBridge() {}
+
+ // Ensures the database engine is started
+ virtual nsresult Init() = 0;
+
+ // Releases the database and disallows its usage
+ virtual nsresult Shutdown() = 0;
+
+ // Asynchronously fills the cache with data from the database for first use.
+ // When |aPriority| is true, the preload operation is scheduled as the first
+ // one. This method is responsible to keep hard reference to the cache for
+ // the time of the preload or, when preload cannot be performed, call
+ // LoadDone() immediately.
+ virtual void AsyncPreload(LocalStorageCacheBridge* aCache,
+ bool aPriority = false) = 0;
+
+ // Asynchronously fill the |usage| object with actual usage of data by its
+ // scope. The scope is eTLD+1 tops, never deeper subdomains.
+ virtual void AsyncGetUsage(StorageUsageBridge* aUsage) = 0;
+
+ // Synchronously fills the cache, when |aForceSync| is false and cache already
+ // got some data before, the method waits for the running preload to finish
+ virtual void SyncPreload(LocalStorageCacheBridge* aCache,
+ bool aForceSync = false) = 0;
+
+ // Called when an existing key is modified in the storage, schedules update to
+ // the database
+ virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey,
+ const nsAString& aValue) = 0;
+
+ // Called when an existing key is modified in the storage, schedules update to
+ // the database
+ virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey,
+ const nsAString& aValue) = 0;
+
+ // Called when an item is removed from the storage, schedules delete of the
+ // key
+ virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey) = 0;
+
+ // Called when the whole storage is cleared by the DOM API, schedules delete
+ // of the scope
+ virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) = 0;
+
+ // Called when chrome deletes e.g. cookies, schedules delete of the whole
+ // database
+ virtual void AsyncClearAll() = 0;
+
+ // Called when only a domain and its subdomains is about to clear
+ virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) = 0;
+
+ // Called when data matching an origin pattern have to be cleared
+ virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern) = 0;
+
+ // Forces scheduled DB operations to be early flushed to the disk
+ virtual void AsyncFlush() = 0;
+
+ // Check whether the scope has any data stored on disk and is thus allowed to
+ // preload
+ virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0;
+};
+#endif
+
+// The implementation of the the database engine, this directly works
+// with the sqlite or any other db API we are based on
+// This class is resposible for collecting and processing asynchronous
+// DB operations over caches (LocalStorageCache) communicating though
+// LocalStorageCacheBridge interface class
+class StorageDBThread final {
+ public:
+ class PendingOperations;
+
+ // Representation of a singe database task, like adding and removing keys,
+ // (pre)loading the whole origin data, cleaning.
+ class DBOperation {
+ public:
+ enum OperationType {
+ // Only operation that reads data from the database
+ opPreload,
+ // The same as opPreload, just executed with highest priority
+ opPreloadUrgent,
+
+ // Load usage of a scope
+ opGetUsage,
+
+ // Operations invoked by the DOM content API
+ opAddItem,
+ opUpdateItem,
+ opRemoveItem,
+ // Clears a specific single origin data
+ opClear,
+
+ // Operations invoked by chrome
+
+ // Clear all the data stored in the database, for all scopes, no
+ // exceptions
+ opClearAll,
+ // Clear data under a domain and all its subdomains regardless
+ // OriginAttributes value
+ opClearMatchingOrigin,
+ // Clear all data matching an OriginAttributesPattern regardless a domain
+ opClearMatchingOriginAttributes,
+ };
+
+ explicit DBOperation(const OperationType aType,
+ LocalStorageCacheBridge* aCache = nullptr,
+ const nsAString& aKey = u""_ns,
+ const nsAString& aValue = u""_ns);
+ DBOperation(const OperationType aType, StorageUsageBridge* aUsage);
+ DBOperation(const OperationType aType, const nsACString& aOriginNoSuffix);
+ DBOperation(const OperationType aType,
+ const OriginAttributesPattern& aOriginNoSuffix);
+ ~DBOperation();
+
+ // Executes the operation, doesn't necessarity have to be called on the I/O
+ // thread
+ void PerformAndFinalize(StorageDBThread* aThread);
+
+ // Finalize the operation, i.e. do any internal cleanup and finish calls
+ void Finalize(nsresult aRv);
+
+ // The operation type
+ OperationType Type() const { return mType; }
+
+ // The origin in the database usage format (reversed)
+ const nsCString OriginNoSuffix() const;
+
+ // The origin attributes suffix
+ const nsCString OriginSuffix() const;
+
+ // |origin suffix + origin key| the operation is working with or a scope
+ // pattern to delete with simple SQL's "LIKE %" from the database.
+ const nsCString Origin() const;
+
+ // |origin suffix + origin key + key| the operation is working with
+ const nsCString Target() const;
+
+ // Pattern to delete matching data with this op
+ const OriginAttributesPattern& OriginPattern() const {
+ return mOriginPattern;
+ }
+
+ private:
+ // The operation implementation body
+ nsresult Perform(StorageDBThread* aThread);
+
+ friend class PendingOperations;
+ OperationType mType;
+ RefPtr<LocalStorageCacheBridge> mCache;
+ RefPtr<StorageUsageBridge> mUsage;
+ nsString const mKey;
+ nsString const mValue;
+ nsCString const mOrigin;
+ OriginAttributesPattern const mOriginPattern;
+ };
+
+ // Encapsulation of collective and coalescing logic for all pending operations
+ // except preloads that are handled separately as priority operations
+ class PendingOperations {
+ public:
+ PendingOperations();
+
+ // Method responsible for coalescing redundant update operations with the
+ // same |Target()| or clear operations with the same or matching |Origin()|
+ void Add(UniquePtr<DBOperation> aOperation);
+
+ // True when there are some scheduled operations to flush on disk
+ bool HasTasks() const;
+
+ // Moves collected operations to a local flat list to allow execution of the
+ // operation list out of the thread lock
+ bool Prepare();
+
+ // Executes the previously |Prepared()'ed| list of operations, returns
+ // result, but doesn't handle it in any way in case of a failure
+ nsresult Execute(StorageDBThread* aThread);
+
+ // Finalizes the pending operation list, returns false when too many
+ // operations failed to flush what indicates a long standing issue with the
+ // database access.
+ bool Finalize(nsresult aRv);
+
+ // true when a clear that deletes the given origin attr pattern and/or
+ // origin key is among the pending operations; when a preload for that scope
+ // is being scheduled, it must be finished right away
+ bool IsOriginClearPending(const nsACString& aOriginSuffix,
+ const nsACString& aOriginNoSuffix) const;
+
+ // Checks whether there is a pending update operation for this scope.
+ bool IsOriginUpdatePending(const nsACString& aOriginSuffix,
+ const nsACString& aOriginNoSuffix) const;
+
+ private:
+ // Returns true iff new operation is of type newType and there is a pending
+ // operation of type pendingType for the same key (target).
+ bool CheckForCoalesceOpportunity(DBOperation* aNewOp,
+ DBOperation::OperationType aPendingType,
+ DBOperation::OperationType aNewType);
+
+ // List of all clearing operations, executed first
+ nsClassHashtable<nsCStringHashKey, DBOperation> mClears;
+
+ // List of all update/insert operations, executed as second
+ nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates;
+
+ // Collection of all tasks, valid only between Prepare() and Execute()
+ nsTArray<UniquePtr<DBOperation> > mExecList;
+
+ // Number of failing flush attempts
+ uint32_t mFlushFailureCount;
+ };
+
+ class ThreadObserver final : public nsIThreadObserver {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITHREADOBSERVER
+
+ ThreadObserver()
+ : mHasPendingEvents(false), mMonitor("StorageThreadMonitor") {}
+
+ bool HasPendingEvents() {
+ mMonitor.AssertCurrentThreadOwns();
+ return mHasPendingEvents;
+ }
+ void ClearPendingEvents() {
+ mMonitor.AssertCurrentThreadOwns();
+ mHasPendingEvents = false;
+ }
+ Monitor& GetMonitor() { return mMonitor; }
+
+ private:
+ virtual ~ThreadObserver() = default;
+ bool mHasPendingEvents;
+ // The monitor we drive the thread with
+ Monitor mMonitor MOZ_UNANNOTATED;
+ };
+
+ class InitHelper;
+
+ class NoteBackgroundThreadRunnable;
+
+ class ShutdownRunnable : public Runnable {
+ // Expected to be only 0 or 1.
+ const uint32_t mPrivateBrowsingId;
+ // Only touched on the main thread.
+ bool& mDone;
+
+ public:
+ explicit ShutdownRunnable(const uint32_t aPrivateBrowsingId, bool& aDone)
+ : Runnable("dom::StorageDBThread::ShutdownRunnable"),
+ mPrivateBrowsingId(aPrivateBrowsingId),
+ mDone(aDone) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ private:
+ ~ShutdownRunnable() = default;
+
+ NS_DECL_NSIRUNNABLE
+ };
+
+ public:
+ explicit StorageDBThread(uint32_t aPrivateBrowsingId);
+ virtual ~StorageDBThread() = default;
+
+ static StorageDBThread* Get(uint32_t aPrivateBrowsingId);
+
+ static StorageDBThread* GetOrCreate(const nsString& aProfilePath,
+ uint32_t aPrivateBrowsingId);
+
+ static nsresult GetProfilePath(nsString& aProfilePath);
+
+ virtual nsresult Init(const nsString& aProfilePath);
+
+ // Flushes all uncommited data and stops the I/O thread.
+ virtual nsresult Shutdown();
+
+ virtual void AsyncPreload(LocalStorageCacheBridge* aCache,
+ bool aPriority = false) {
+ InsertDBOp(MakeUnique<DBOperation>(
+ aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload,
+ aCache));
+ }
+
+ virtual void SyncPreload(LocalStorageCacheBridge* aCache,
+ bool aForce = false);
+
+ virtual void AsyncGetUsage(StorageUsageBridge* aUsage) {
+ InsertDBOp(MakeUnique<DBOperation>(DBOperation::opGetUsage, aUsage));
+ }
+
+ virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey,
+ const nsAString& aValue) {
+ return InsertDBOp(
+ MakeUnique<DBOperation>(DBOperation::opAddItem, aCache, aKey, aValue));
+ }
+
+ virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey,
+ const nsAString& aValue) {
+ return InsertDBOp(MakeUnique<DBOperation>(DBOperation::opUpdateItem, aCache,
+ aKey, aValue));
+ }
+
+ virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache,
+ const nsAString& aKey) {
+ return InsertDBOp(
+ MakeUnique<DBOperation>(DBOperation::opRemoveItem, aCache, aKey));
+ }
+
+ virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) {
+ return InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClear, aCache));
+ }
+
+ virtual void AsyncClearAll() {
+ InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClearAll));
+ }
+
+ virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) {
+ InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClearMatchingOrigin,
+ aOriginNoSuffix));
+ }
+
+ virtual void AsyncClearMatchingOriginAttributes(
+ const OriginAttributesPattern& aPattern) {
+ InsertDBOp(MakeUnique<DBOperation>(
+ DBOperation::opClearMatchingOriginAttributes, aPattern));
+ }
+
+ virtual void AsyncFlush();
+
+ virtual bool ShouldPreloadOrigin(const nsACString& aOrigin);
+
+ // Get the complete list of scopes having data.
+ void GetOriginsHavingData(nsTArray<nsCString>* aOrigins);
+
+ private:
+ nsCOMPtr<nsIFile> mDatabaseFile;
+ PRThread* mThread;
+
+ // Used to observe runnables dispatched to our thread and to monitor it.
+ RefPtr<ThreadObserver> mThreadObserver;
+
+ // Flag to stop, protected by the monitor returned by
+ // mThreadObserver->GetMonitor().
+ bool mStopIOThread;
+
+ // Whether WAL is enabled
+ bool mWALModeEnabled;
+
+ // Whether DB has already been open, avoid races between main thread reads
+ // and pending DB init in the background I/O thread
+ Atomic<bool, ReleaseAcquire> mDBReady;
+
+ // State of the database initiation
+ nsresult mStatus;
+
+ // List of origins (including origin attributes suffix) having data, for
+ // optimization purposes only
+ nsTHashSet<nsCString> mOriginsHavingData;
+
+ // Connection used by the worker thread for all read and write ops
+ nsCOMPtr<mozIStorageConnection> mWorkerConnection;
+
+ // Connection used only on the main thread for sync read operations
+ nsCOMPtr<mozIStorageConnection> mReaderConnection;
+
+ StatementCache mWorkerStatements;
+ StatementCache mReaderStatements;
+
+ // Time the first pending operation has been added to the pending operations
+ // list
+ TimeStamp mDirtyEpoch;
+
+ // Flag to force immediate flush of all pending operations
+ bool mFlushImmediately;
+
+ // List of preloading operations, in chronological or priority order.
+ // Executed prioritly over pending update operations.
+ nsTArray<DBOperation*> mPreloads;
+
+ // Collector of pending update operations
+ PendingOperations mPendingTasks;
+
+ // Expected to be only 0 or 1.
+ const uint32_t mPrivateBrowsingId;
+
+ // Counter of calls for thread priority rising.
+ int32_t mPriorityCounter;
+
+ // Helper to direct an operation to one of the arrays above;
+ // also checks IsOriginClearPending for preloads
+ nsresult InsertDBOp(UniquePtr<DBOperation> aOperation);
+
+ // Opens the database, first thing we do after start of the thread.
+ nsresult OpenDatabaseConnection();
+ nsresult OpenAndUpdateDatabase();
+ nsresult InitDatabase();
+ nsresult ShutdownDatabase();
+
+ // Tries to establish WAL mode
+ nsresult SetJournalMode(bool aIsWal);
+ nsresult TryJournalMode();
+
+ // Sets the threshold for auto-checkpointing the WAL.
+ nsresult ConfigureWALBehavior();
+
+ void SetHigherPriority();
+ void SetDefaultPriority();
+
+ // Ensures we flush pending tasks in some reasonble time
+ void ScheduleFlush();
+
+ // Called when flush of pending tasks is being executed
+ void UnscheduleFlush();
+
+ // This method is used for two purposes:
+ // 1. as a value passed to monitor.Wait() method
+ // 2. as in indicator that flush has to be performed
+ //
+ // Return:
+ // - TimeDuration::Forever() when no pending tasks are scheduled
+ // - Non-zero TimeDuration when tasks have been scheduled, but it
+ // is still not time to perform the flush ; it is actual time to
+ // wait until the flush has to happen.
+ // - 0 TimeDuration when it is time to do the flush
+ TimeDuration TimeUntilFlush();
+
+ // Notifies to the main thread that flush has completed
+ void NotifyFlushCompletion();
+
+ // Thread loop
+ static void ThreadFunc(void* aArg);
+ void ThreadFunc();
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_StorageDBThread_h