forked from mirrors/gecko-dev
		
	 d6d515bcdd
			
		
	
	
		d6d515bcdd
		
	
	
	
	
		
			
			Depends on D145764 Differential Revision: https://phabricator.services.mozilla.com/D145766
		
			
				
	
	
		
			493 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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
 |