tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StorageDBThread.h (17253B)


      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 "mozilla/Atomics.h"
     11 #include "mozilla/BasePrincipal.h"
     12 #include "mozilla/Monitor.h"
     13 #include "mozilla/OriginAttributes.h"
     14 #include "mozilla/TimeStamp.h"
     15 #include "mozilla/UniquePtr.h"
     16 #include "mozilla/storage/StatementCache.h"
     17 #include "nsCOMPtr.h"
     18 #include "nsClassHashtable.h"
     19 #include "nsIFile.h"
     20 #include "nsIThreadInternal.h"
     21 #include "nsString.h"
     22 #include "nsTArray.h"
     23 #include "nsTHashSet.h"
     24 #include "nsThreadUtils.h"
     25 #include "prinrval.h"
     26 #include "prthread.h"
     27 
     28 class mozIStorageConnection;
     29 
     30 namespace mozilla::dom {
     31 
     32 class LocalStorageCacheBridge;
     33 class StorageUsageBridge;
     34 class StorageUsage;
     35 
     36 using StatementCache = mozilla::storage::StatementCache<mozIStorageStatement>;
     37 
     38 // XXX Fix me!
     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
     42 //        unused.
     43 //     4. Remove this class completely.
     44 //
     45 //     See bug 1387636 for more details.
     46 #if 0
     47 // Interface used by the cache to post operations to the asynchronous
     48 // database thread or process.
     49 class StorageDBBridge
     50 {
     51 public:
     52  StorageDBBridge();
     53  virtual ~StorageDBBridge() {}
     54 
     55  // Ensures the database engine is started
     56  virtual nsresult Init() = 0;
     57 
     58  // Releases the database and disallows its usage
     59  virtual nsresult Shutdown() = 0;
     60 
     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;
     68 
     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;
     72 
     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;
     77 
     78  // Called when an existing key is modified in the storage, schedules update to
     79  // the database
     80  virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache,
     81                                const nsAString& aKey,
     82                                const nsAString& aValue) = 0;
     83 
     84  // Called when an existing key is modified in the storage, schedules update to
     85  // the database
     86  virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache,
     87                                   const nsAString& aKey,
     88                                   const nsAString& aValue) = 0;
     89 
     90  // Called when an item is removed from the storage, schedules delete of the
     91  // key
     92  virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache,
     93                                   const nsAString& aKey) = 0;
     94 
     95  // Called when the whole storage is cleared by the DOM API, schedules delete
     96  // of the scope
     97  virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) = 0;
     98 
     99  // Called when chrome deletes e.g. cookies, schedules delete of the whole
    100  // database
    101  virtual void AsyncClearAll() = 0;
    102 
    103  // Called when only a domain and its subdomains is about to clear
    104  virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) = 0;
    105 
    106  // Called when data matching an origin pattern have to be cleared
    107  virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern) = 0;
    108 
    109  // Forces scheduled DB operations to be early flushed to the disk
    110  virtual void AsyncFlush() = 0;
    111 
    112  // Check whether the scope has any data stored on disk and is thus allowed to
    113  // preload
    114  virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0;
    115 };
    116 #endif
    117 
    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 {
    124 public:
    125  class PendingOperations;
    126 
    127  // Representation of a singe database task, like adding and removing keys,
    128  // (pre)loading the whole origin data, cleaning.
    129  class DBOperation {
    130   public:
    131    enum OperationType {
    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    };
    158 
    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);
    167    ~DBOperation();
    168 
    169    // Executes the operation, doesn't necessarity have to be called on the I/O
    170    // thread
    171    void PerformAndFinalize(StorageDBThread* aThread);
    172 
    173    // Finalize the operation, i.e. do any internal cleanup and finish calls
    174    void Finalize(nsresult aRv);
    175 
    176    // The operation type
    177    OperationType Type() const { return mType; }
    178 
    179    // The origin in the database usage format (reversed)
    180    const nsCString OriginNoSuffix() const;
    181 
    182    // The origin attributes suffix
    183    const nsCString OriginSuffix() const;
    184 
    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;
    188 
    189    // |origin suffix + origin key + key| the operation is working with
    190    const nsCString Target() const;
    191 
    192    // Pattern to delete matching data with this op
    193    const OriginAttributesPattern& OriginPattern() const {
    194      return mOriginPattern;
    195    }
    196 
    197   private:
    198    // The operation implementation body
    199    nsresult Perform(StorageDBThread* aThread);
    200 
    201    friend class PendingOperations;
    202    OperationType mType;
    203    RefPtr<LocalStorageCacheBridge> mCache;
    204    RefPtr<StorageUsageBridge> mUsage;
    205    nsString const mKey;
    206    nsString const mValue;
    207    nsCString const mOrigin;
    208    OriginAttributesPattern const mOriginPattern;
    209  };
    210 
    211  // Encapsulation of collective and coalescing logic for all pending operations
    212  // except preloads that are handled separately as priority operations
    213  class PendingOperations {
    214   public:
    215    PendingOperations();
    216 
    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);
    220 
    221    // True when there are some scheduled operations to flush on disk
    222    bool HasTasks() const;
    223 
    224    // Moves collected operations to a local flat list to allow execution of the
    225    // operation list out of the thread lock
    226    bool Prepare();
    227 
    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);
    231 
    232    // Finalizes the pending operation list, returns false when too many
    233    // operations failed to flush what indicates a long standing issue with the
    234    // database access.
    235    bool Finalize(nsresult aRv);
    236 
    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;
    242 
    243    // Checks whether there is a pending update operation for this scope.
    244    bool IsOriginUpdatePending(const nsACString& aOriginSuffix,
    245                               const nsACString& aOriginNoSuffix) const;
    246 
    247   private:
    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);
    253 
    254    // List of all clearing operations, executed first
    255    nsClassHashtable<nsCStringHashKey, DBOperation> mClears;
    256 
    257    // List of all update/insert operations, executed as second
    258    nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates;
    259 
    260    // Collection of all tasks, valid only between Prepare() and Execute()
    261    nsTArray<UniquePtr<DBOperation> > mExecList;
    262 
    263    // Number of failing flush attempts
    264    uint32_t mFlushFailureCount;
    265  };
    266 
    267  class ThreadObserver final : public nsIThreadObserver {
    268    NS_DECL_THREADSAFE_ISUPPORTS
    269    NS_DECL_NSITHREADOBSERVER
    270 
    271    ThreadObserver()
    272        : mHasPendingEvents(false), mMonitor("StorageThreadMonitor") {}
    273 
    274    bool HasPendingEvents() {
    275      mMonitor.AssertCurrentThreadOwns();
    276      return mHasPendingEvents;
    277    }
    278    void ClearPendingEvents() {
    279      mMonitor.AssertCurrentThreadOwns();
    280      mHasPendingEvents = false;
    281    }
    282    Monitor& GetMonitor() { return mMonitor; }
    283 
    284   private:
    285    virtual ~ThreadObserver() = default;
    286    bool mHasPendingEvents;
    287    // The monitor we drive the thread with
    288    Monitor mMonitor MOZ_UNANNOTATED;
    289  };
    290 
    291  class InitHelper;
    292 
    293  class NoteBackgroundThreadRunnable;
    294 
    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.
    299    bool& mDone;
    300 
    301   public:
    302    explicit ShutdownRunnable(const uint32_t aPrivateBrowsingId, bool& aDone)
    303        : Runnable("dom::StorageDBThread::ShutdownRunnable"),
    304          mPrivateBrowsingId(aPrivateBrowsingId),
    305          mDone(aDone) {
    306      MOZ_ASSERT(NS_IsMainThread());
    307    }
    308 
    309   private:
    310    ~ShutdownRunnable() = default;
    311 
    312    NS_DECL_NSIRUNNABLE
    313  };
    314 
    315 public:
    316  explicit StorageDBThread(uint32_t aPrivateBrowsingId);
    317  virtual ~StorageDBThread() = default;
    318 
    319  static StorageDBThread* Get(uint32_t aPrivateBrowsingId);
    320 
    321  static StorageDBThread* GetOrCreate(const nsString& aProfilePath,
    322                                      uint32_t aPrivateBrowsingId);
    323 
    324  static nsresult GetProfilePath(nsString& aProfilePath);
    325 
    326  virtual nsresult Init(const nsString& aProfilePath);
    327 
    328  // Flushes all uncommited data and stops the I/O thread.
    329  virtual nsresult Shutdown();
    330 
    331  virtual void AsyncPreload(LocalStorageCacheBridge* aCache,
    332                            bool aPriority = false) {
    333    InsertDBOp(MakeUnique<DBOperation>(
    334        aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload,
    335        aCache));
    336  }
    337 
    338  virtual void SyncPreload(LocalStorageCacheBridge* aCache,
    339                           bool aForce = false);
    340 
    341  virtual void AsyncGetUsage(StorageUsageBridge* aUsage) {
    342    InsertDBOp(MakeUnique<DBOperation>(DBOperation::opGetUsage, aUsage));
    343  }
    344 
    345  virtual nsresult AsyncAddItem(LocalStorageCacheBridge* aCache,
    346                                const nsAString& aKey,
    347                                const nsAString& aValue) {
    348    return InsertDBOp(
    349        MakeUnique<DBOperation>(DBOperation::opAddItem, aCache, aKey, aValue));
    350  }
    351 
    352  virtual nsresult AsyncUpdateItem(LocalStorageCacheBridge* aCache,
    353                                   const nsAString& aKey,
    354                                   const nsAString& aValue) {
    355    return InsertDBOp(MakeUnique<DBOperation>(DBOperation::opUpdateItem, aCache,
    356                                              aKey, aValue));
    357  }
    358 
    359  virtual nsresult AsyncRemoveItem(LocalStorageCacheBridge* aCache,
    360                                   const nsAString& aKey) {
    361    return InsertDBOp(
    362        MakeUnique<DBOperation>(DBOperation::opRemoveItem, aCache, aKey));
    363  }
    364 
    365  virtual nsresult AsyncClear(LocalStorageCacheBridge* aCache) {
    366    return InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClear, aCache));
    367  }
    368 
    369  virtual void AsyncClearAll() {
    370    InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClearAll));
    371  }
    372 
    373  virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix) {
    374    InsertDBOp(MakeUnique<DBOperation>(DBOperation::opClearMatchingOrigin,
    375                                       aOriginNoSuffix));
    376  }
    377 
    378  virtual void AsyncClearMatchingOriginAttributes(
    379      const OriginAttributesPattern& aPattern) {
    380    InsertDBOp(MakeUnique<DBOperation>(
    381        DBOperation::opClearMatchingOriginAttributes, aPattern));
    382  }
    383 
    384  virtual void AsyncFlush();
    385 
    386  virtual bool ShouldPreloadOrigin(const nsACString& aOrigin);
    387 
    388  // Get the complete list of scopes having data.
    389  void GetOriginsHavingData(nsTArray<nsCString>* aOrigins);
    390 
    391 private:
    392  nsCOMPtr<nsIFile> mDatabaseFile;
    393  PRThread* mThread;
    394 
    395  // Used to observe runnables dispatched to our thread and to monitor it.
    396  RefPtr<ThreadObserver> mThreadObserver;
    397 
    398  // Flag to stop, protected by the monitor returned by
    399  // mThreadObserver->GetMonitor().
    400  bool mStopIOThread;
    401 
    402  // Whether WAL is enabled
    403  bool mWALModeEnabled;
    404 
    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;
    408 
    409  // State of the database initiation
    410  nsresult mStatus;
    411 
    412  // List of origins (including origin attributes suffix) having data, for
    413  // optimization purposes only
    414  nsTHashSet<nsCString> mOriginsHavingData;
    415 
    416  // Connection used by the worker thread for all read and write ops
    417  nsCOMPtr<mozIStorageConnection> mWorkerConnection;
    418 
    419  // Connection used only on the main thread for sync read operations
    420  nsCOMPtr<mozIStorageConnection> mReaderConnection;
    421 
    422  StatementCache mWorkerStatements;
    423  StatementCache mReaderStatements;
    424 
    425  // Time the first pending operation has been added to the pending operations
    426  // list
    427  TimeStamp mDirtyEpoch;
    428 
    429  // Flag to force immediate flush of all pending operations
    430  bool mFlushImmediately;
    431 
    432  // List of preloading operations, in chronological or priority order.
    433  // Executed prioritly over pending update operations.
    434  nsTArray<DBOperation*> mPreloads;
    435 
    436  // Collector of pending update operations
    437  PendingOperations mPendingTasks;
    438 
    439  // Expected to be only 0 or 1.
    440  const uint32_t mPrivateBrowsingId;
    441 
    442  // Counter of calls for thread priority rising.
    443  int32_t mPriorityCounter;
    444 
    445  // Helper to direct an operation to one of the arrays above;
    446  // also checks IsOriginClearPending for preloads
    447  nsresult InsertDBOp(UniquePtr<DBOperation> aOperation);
    448 
    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();
    454 
    455  // Tries to establish WAL mode
    456  nsresult SetJournalMode(bool aIsWal);
    457  nsresult TryJournalMode();
    458 
    459  // Sets the threshold for auto-checkpointing the WAL.
    460  nsresult ConfigureWALBehavior();
    461 
    462  void SetHigherPriority();
    463  void SetDefaultPriority();
    464 
    465  // Ensures we flush pending tasks in some reasonble time
    466  void ScheduleFlush();
    467 
    468  // Called when flush of pending tasks is being executed
    469  void UnscheduleFlush();
    470 
    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
    474  //
    475  // Return:
    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();
    482 
    483  // Notifies to the main thread that flush has completed
    484  void NotifyFlushCompletion();
    485 
    486  // Thread loop
    487  static void ThreadFunc(void* aArg);
    488  void ThreadFunc();
    489 };
    490 
    491 }  // namespace mozilla::dom
    492 
    493 #endif  // mozilla_dom_StorageDBThread_h