tor-browser

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

mozStorageConnection.h (19257B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
      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_storage_Connection_h
      8 #define mozilla_storage_Connection_h
      9 
     10 #include "nsCOMPtr.h"
     11 #include "mozilla/Atomics.h"
     12 #include "mozilla/Mutex.h"
     13 #include "nsProxyRelease.h"
     14 #include "nsThreadUtils.h"
     15 #include "nsIInterfaceRequestor.h"
     16 
     17 #include "nsTHashMap.h"
     18 #include "nsTHashSet.h"
     19 #include "mozIStorageProgressHandler.h"
     20 #include "SQLiteMutex.h"
     21 #include "mozIStorageConnection.h"
     22 #include "mozStorageService.h"
     23 #include "mozIStorageAsyncConnection.h"
     24 #include "mozIStorageCompletionCallback.h"
     25 
     26 #include "mozilla/Attributes.h"
     27 
     28 #include "sqlite3.h"
     29 
     30 class nsIFile;
     31 class nsIFileURL;
     32 class nsIEventTarget;
     33 class nsISerialEventTarget;
     34 class nsIThread;
     35 
     36 namespace mozilla::storage {
     37 
     38 class Connection final : public mozIStorageConnection,
     39                         public nsIInterfaceRequestor {
     40 public:
     41  NS_DECL_THREADSAFE_ISUPPORTS
     42  NS_DECL_MOZISTORAGEASYNCCONNECTION
     43  NS_DECL_MOZISTORAGECONNECTION
     44  NS_DECL_NSIINTERFACEREQUESTOR
     45 
     46  /**
     47   * Indicates if a database operation is synchronous or asynchronous.
     48   *
     49   * - Async operations may be called from any thread for all connections.
     50   * - Sync operations may be called from any thread for sync connections, and
     51   *   from background threads for async connections.
     52   */
     53  enum ConnectionOperation { ASYNCHRONOUS, SYNCHRONOUS };
     54 
     55  /**
     56   * Structure used to describe user functions on the database connection.
     57   */
     58  struct FunctionInfo {
     59    nsCOMPtr<mozIStorageFunction> function;
     60    int32_t numArgs;
     61  };
     62 
     63  /**
     64   * @param aService
     65   *        Pointer to the storage service.  Held onto for the lifetime of the
     66   *        connection.
     67   * @param aFlags
     68   *        The flags to pass to sqlite3_open_v2.
     69   * @param aSupportedOperations
     70   *        The operation types supported on this connection. All connections
     71   *        implement both the async (`mozIStorageAsyncConnection`) and sync
     72   *        (`mozIStorageConnection`) interfaces, but async connections may not
     73   *        call sync operations from the main thread.
     74   * @param aInterruptible
     75   *        If |true|, the pending operations can be interrupted by invokind the
     76   *        Interrupt() method.
     77   *        If |false|, method Interrupt() must not be used.
     78   * @param aIgnoreLockingMode
     79   *        If |true|, ignore locks in force on the file. Only usable with
     80   *        read-only connections. Defaults to false.
     81   *        Use with extreme caution. If sqlite ignores locks, reads may fail
     82   *        indicating database corruption (the database won't actually be
     83   *        corrupt) or produce wrong results without any indication that has
     84   *        happened.
     85   */
     86  Connection(Service* aService, int aFlags,
     87             ConnectionOperation aSupportedOperations,
     88             const nsCString& aTelemetryFilename, bool aInterruptible = false,
     89             bool aIgnoreLockingMode = false, bool aOpenNotExclusive = false);
     90 
     91  /**
     92   * Creates the connection to an in-memory database.
     93   */
     94  nsresult initialize(const nsACString& aStorageKey, const nsACString& aName);
     95 
     96  /**
     97   * Creates the connection to the database.
     98   *
     99   * @param aDatabaseFile
    100   *        The nsIFile of the location of the database to open, or create if it
    101   *        does not exist.
    102   */
    103  nsresult initialize(nsIFile* aDatabaseFile);
    104 
    105  /**
    106   * Creates the connection to the database.
    107   *
    108   * @param aFileURL
    109   *        The nsIFileURL of the location of the database to open, or create if
    110   * it does not exist.
    111   */
    112  nsresult initialize(nsIFileURL* aFileURL);
    113 
    114  /**
    115   * Same as initialize, but to be used on the async thread.
    116   */
    117  nsresult initializeOnAsyncThread(nsIFile* aStorageFile);
    118 
    119  /**
    120   * Fetches runtime status information for this connection.
    121   *
    122   * @param aStatusOption One of the SQLITE_DBSTATUS options defined at
    123   *        http://www.sqlite.org/c3ref/c_dbstatus_options.html
    124   * @param [optional] aMaxValue if provided, will be set to the highest
    125   *        istantaneous value.
    126   * @return the current value for the specified option.
    127   */
    128  int32_t getSqliteRuntimeStatus(int32_t aStatusOption,
    129                                 int32_t* aMaxValue = nullptr);
    130  /**
    131   * Registers/unregisters a commit hook callback.
    132   *
    133   * @param aCallbackFn a callback function to be invoked on transactions
    134   *        commit.  Pass nullptr to unregister the current callback.
    135   * @param [optional] aData if provided, will be passed to the callback.
    136   * @see http://sqlite.org/c3ref/commit_hook.html
    137   */
    138  void setCommitHook(int (*aCallbackFn)(void*), void* aData = nullptr) {
    139    MOZ_ASSERT(mDBConn, "A connection must exist at this point");
    140    ::sqlite3_commit_hook(mDBConn, aCallbackFn, aData);
    141  };
    142 
    143  /**
    144   * Gets autocommit status.
    145   */
    146  bool getAutocommit() {
    147    return mDBConn && static_cast<bool>(::sqlite3_get_autocommit(mDBConn));
    148  };
    149 
    150  /**
    151   * Lazily creates and returns a background execution thread.  In the future,
    152   * the thread may be re-claimed if left idle, so you should call this
    153   * method just before you dispatch and not save the reference.
    154   *
    155   * This must be called from the opener thread.
    156   *
    157   * @return an event target suitable for asynchronous statement execution.
    158   * @note This method will return null once AsyncClose() has been called.
    159   */
    160  nsIEventTarget* getAsyncExecutionTarget();
    161 
    162  /**
    163   * Mutex used by asynchronous statements to protect state.  The mutex is
    164   * declared on the connection object because there is no contention between
    165   * asynchronous statements (they are serialized on mAsyncExecutionThread).
    166   * Currently protects:
    167   *  - Connection.mAsyncExecutionThreadShuttingDown
    168   *  - Connection.mConnectionClosed
    169   *  - AsyncExecuteStatements.mCancelRequested
    170   *  - Connection.mLoadedExtensions
    171   */
    172  Mutex sharedAsyncExecutionMutex MOZ_UNANNOTATED;
    173 
    174  /**
    175   * Wraps the mutex that SQLite gives us from sqlite3_db_mutex.  This is public
    176   * because we already expose the sqlite3* native connection and proper
    177   * operation of the deadlock detector requires everyone to use the same single
    178   * SQLiteMutex instance for correctness.
    179   */
    180  SQLiteMutex sharedDBMutex;
    181 
    182  /**
    183   * References the event target this database was opened on.
    184   */
    185  const nsCOMPtr<nsISerialEventTarget> eventTargetOpenedOn;
    186 
    187  /**
    188   * Closes the SQLite database, and warns about any non-finalized statements.
    189   */
    190  nsresult internalClose(sqlite3* aNativeconnection);
    191 
    192  /**
    193   * Shuts down the passed-in async thread.
    194   */
    195  void shutdownAsyncThread();
    196 
    197  /**
    198   * Obtains the filename of the connection.  Useful for logging.
    199   */
    200  nsCString getFilename();
    201 
    202  /**
    203   * Creates an sqlite3 prepared statement object from an SQL string.
    204   *
    205   * @param aNativeConnection
    206   *        The underlying Sqlite connection to prepare the statement with.
    207   * @param aSQL
    208   *        The SQL statement string to compile.
    209   * @param _stmt
    210   *        New sqlite3_stmt object.
    211   * @return the result from sqlite3_prepare_v2.
    212   */
    213  int prepareStatement(sqlite3* aNativeConnection, const nsCString& aSQL,
    214                       sqlite3_stmt** _stmt);
    215 
    216  /**
    217   * Performs a sqlite3_step on aStatement, while properly handling
    218   * SQLITE_LOCKED when not on the main thread by waiting until we are notified.
    219   *
    220   * @param aNativeConnection
    221   *        The underlying Sqlite connection to step the statement with.
    222   * @param aStatement
    223   *        A pointer to a sqlite3_stmt object.
    224   * @return the result from sqlite3_step.
    225   */
    226  int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement);
    227 
    228  /**
    229   * Raw connection transaction management.
    230   *
    231   * @see BeginTransactionAs, CommitTransaction, RollbackTransaction.
    232   */
    233  nsresult beginTransactionInternal(
    234      const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection,
    235      int32_t aTransactionType = TRANSACTION_DEFERRED);
    236  nsresult commitTransactionInternal(const SQLiteMutexAutoLock& aProofOfLock,
    237                                     sqlite3* aNativeConnection);
    238  nsresult rollbackTransactionInternal(const SQLiteMutexAutoLock& aProofOfLock,
    239                                       sqlite3* aNativeConnection);
    240 
    241  /**
    242   * Indicates if this database connection is open.
    243   */
    244  inline bool connectionReady() { return mDBConn != nullptr; }
    245 
    246  /**
    247   * Indicates if this database connection has an open transaction. Because
    248   * multiple threads can execute statements on the same connection, this method
    249   * requires proof that the caller is holding `sharedDBMutex`.
    250   *
    251   * Per the SQLite docs, `sqlite3_get_autocommit` returns 0 if autocommit mode
    252   * is disabled. `BEGIN` disables autocommit mode, and `COMMIT`, `ROLLBACK`, or
    253   * an automatic rollback re-enables it.
    254   */
    255  inline bool transactionInProgress(const SQLiteMutexAutoLock& aProofOfLock) {
    256    return !getAutocommit();
    257  }
    258 
    259  /**
    260   * Indicates if this database connection supports the given operation.
    261   *
    262   * @param  aOperationType
    263   *         The operation type, sync or async.
    264   * @return `true` if the operation is supported, `false` otherwise.
    265   */
    266  bool operationSupported(ConnectionOperation aOperationType);
    267 
    268  /**
    269   * Thread-aware version of connectionReady, results per caller's thread are:
    270   *  - owner thread: Same as connectionReady().  True means we have a valid,
    271   *    un-closed database connection and it's not going away until you invoke
    272   *    Close() or AsyncClose().
    273   *  - async thread: Returns true at all times because you can't schedule
    274   *    runnables against the async thread after AsyncClose() has been called.
    275   *    Therefore, the connection is still around if your code is running.
    276   *  - any other thread: Race-prone Lies!  If you are main-thread code in
    277   *    mozStorageService iterating over the list of connections, you need to
    278   *    acquire the sharedAsyncExecutionMutex for the connection, invoke
    279   *    connectionReady() while holding it, and then continue to hold it while
    280   *    you do whatever you need to do.  This is because of off-main-thread
    281   *    consumers like dom/cache and IndexedDB and other QuotaManager clients.
    282   */
    283  bool isConnectionReadyOnThisThread();
    284 
    285  /**
    286   * True if this connection has inited shutdown.
    287   */
    288  bool isClosing();
    289 
    290  /**
    291   * True if the underlying connection is closed.
    292   * Any sqlite resources may be lost when this returns true, so nothing should
    293   * try to use them.
    294   * This locks on sharedAsyncExecutionMutex.
    295   */
    296  bool isClosed();
    297 
    298  /**
    299   * Same as isClosed(), but takes a proof-of-lock instead of locking
    300   * internally.
    301   */
    302  bool isClosed(MutexAutoLock& lock);
    303 
    304  /**
    305   * True if the async execution thread is alive and able to be used (i.e., it
    306   * is not in the process of shutting down.)
    307   *
    308   * This must be called from the opener thread.
    309   */
    310  bool isAsyncExecutionThreadAvailable();
    311 
    312  nsresult initializeClone(Connection* aClone, bool aReadOnly);
    313 
    314  /**
    315   * Records a status from a sqlite statement.
    316   *
    317   * @param srv The sqlite result for the failure or SQLITE_OK.
    318   */
    319  void RecordQueryStatus(int srv);
    320 
    321  /**
    322   * Returns the number of pages in the free list that can be removed.
    323   *
    324   * A database may use chunked growth to reduce filesystem fragmentation, then
    325   * Sqlite will allocate and release multiple pages in chunks. We want to
    326   * preserve the chunked space to reduce the likelihood of fragmentation,
    327   * releasing free pages only when there's a large amount of them. This can be
    328   * used to decide if it's worth vacuuming the database and how many pages can
    329   * be vacuumed in case of incremental vacuum.
    330   * Note this returns 0, and asserts, in case of errors.
    331   */
    332  int32_t RemovablePagesInFreeList(const nsACString& aSchemaName);
    333 
    334  /**
    335   * Whether the statement currently running on the helper thread can be
    336   * interrupted.
    337   */
    338  Atomic<bool> mIsStatementOnHelperThreadInterruptible;
    339 
    340 private:
    341  ~Connection();
    342  nsresult initializeInternal();
    343  void initializeFailed();
    344 
    345  /**
    346   * Records the status of an attempt to load a sqlite database to telemetry.
    347   *
    348   * @param rv The state of the load, success or failure.
    349   */
    350  void RecordOpenStatus(nsresult rv);
    351 
    352  /**
    353   * Sets the database into a closed state so no further actions can be
    354   * performed.
    355   *
    356   * @note mDBConn is set to nullptr in this method.
    357   */
    358  nsresult setClosedState();
    359 
    360  /**
    361   * Helper for calls to sqlite3_exec. Reports long delays to Telemetry.
    362   *
    363   * @param aNativeConnection
    364   *        The underlying Sqlite connection to execute the query with.
    365   * @param aSqlString
    366   *        SQL string to execute
    367   * @return the result from sqlite3_exec.
    368   */
    369  int executeSql(sqlite3* aNativeConnection, const char* aSqlString);
    370 
    371  /**
    372   * Describes a certain primitive type in the database.
    373   *
    374   * Possible Values Are:
    375   *  INDEX - To check for the existence of an index
    376   *  TABLE - To check for the existence of a table
    377   */
    378  enum DatabaseElementType { INDEX, TABLE };
    379 
    380  /**
    381   * Determines if the specified primitive exists.
    382   *
    383   * @param aElementType
    384   *        The type of element to check the existence of
    385   * @param aElementName
    386   *        The name of the element to check for
    387   * @returns true if element exists, false otherwise
    388   */
    389  nsresult databaseElementExists(enum DatabaseElementType aElementType,
    390                                 const nsACString& aElementName, bool* _exists);
    391 
    392  bool findFunctionByInstance(mozIStorageFunction* aInstance);
    393 
    394  static int sProgressHelper(void* aArg);
    395  // Generic progress handler
    396  // Dispatch call to registered progress handler,
    397  // if there is one. Do nothing in other cases.
    398  int progressHandler();
    399 
    400  /**
    401   * Like `operationSupported`, but throws (and, in a debug build, asserts) if
    402   * the operation is unsupported.
    403   */
    404  nsresult ensureOperationSupported(ConnectionOperation aOperationType);
    405 
    406  sqlite3* mDBConn;
    407  nsCString mStorageKey;
    408  nsCString mName;
    409  nsCOMPtr<nsIFileURL> mFileURL;
    410  nsCOMPtr<nsIFile> mDatabaseFile;
    411 
    412  /**
    413   * Lazily created thread for asynchronous statement execution.  Consumers
    414   * should use getAsyncExecutionTarget rather than directly accessing this
    415   * field.
    416   *
    417   * This must be modified only on the opener thread.
    418   */
    419  nsCOMPtr<nsIThread> mAsyncExecutionThread;
    420 
    421  /**
    422   * The filename that will be reported to telemetry for this connection. By
    423   * default this will be the leaf of the path to the database file.
    424   */
    425  nsCString mTelemetryFilename;
    426 
    427  /**
    428   * Stores the default behavior for all transactions run on this connection.
    429   */
    430  mozilla::Atomic<int32_t> mDefaultTransactionType;
    431 
    432  /**
    433   * Used to trigger cleanup logic only the first time our refcount hits 1.  We
    434   * may trigger a failsafe Close() that invokes SpinningSynchronousClose()
    435   * which invokes AsyncClose() which may bump our refcount back up to 2 (and
    436   * which will then fall back down to 1 again).  It's also possible that the
    437   * Service may bump our refcount back above 1 if getConnections() runs before
    438   * we invoke unregisterConnection().
    439   */
    440  mozilla::Atomic<bool> mDestroying;
    441 
    442  /**
    443   * Stores the mapping of a given function by name to its instance.  Access is
    444   * protected by sharedDBMutex.
    445   */
    446  nsTHashMap<nsCStringHashKey, FunctionInfo> mFunctions;
    447 
    448  /**
    449   * Stores the registered progress handler for the database connection.  Access
    450   * is protected by sharedDBMutex.
    451   */
    452  nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
    453 
    454  // This is here for two reasons: 1) It's used to make sure that the
    455  // connections do not outlive the service.  2) Our custom collating functions
    456  // call its localeCompareStrings() method.
    457  RefPtr<Service> mStorageService;
    458 
    459  nsresult synchronousClose();
    460 
    461  /**
    462   * Stores the flags we passed to sqlite3_open_v2.
    463   */
    464  const int mFlags;
    465 
    466  uint32_t mTransactionNestingLevel;
    467 
    468  /**
    469   * Indicates which operations are supported on this connection.
    470   */
    471  const ConnectionOperation mSupportedOperations;
    472 
    473  /**
    474   * Stores whether this connection is interruptible.
    475   */
    476  const bool mInterruptible;
    477 
    478  /**
    479   * Stores whether we should ask sqlite3_open_v2 to ignore locking.
    480   */
    481  const bool mIgnoreLockingMode;
    482 
    483  /**
    484   * Stores whether we should ask sqlite3_open_v2 to open without an exclusive
    485   * lock.
    486   */
    487  const bool mOpenNotExclusive;
    488 
    489  /**
    490   * Set to true by Close() or AsyncClose() prior to shutdown.
    491   *
    492   * If false, we guarantee both that the underlying sqlite3 database
    493   * connection is still open and that getAsyncExecutionTarget() can
    494   * return a thread. Once true, either the sqlite3 database
    495   * connection is being shutdown or it has been
    496   * shutdown. Additionally, once true, getAsyncExecutionTarget()
    497   * returns null.
    498   *
    499   * This variable should be accessed while holding the
    500   * sharedAsyncExecutionMutex.
    501   */
    502  bool mAsyncExecutionThreadShuttingDown;
    503 
    504  /**
    505   * Set to true just prior to calling sqlite3_close on the
    506   * connection.
    507   *
    508   * This variable should be accessed while holding the
    509   * sharedAsyncExecutionMutex.
    510   */
    511  bool mConnectionClosed;
    512 
    513  /**
    514   * Stores the growth increment chunk size, set through SetGrowthIncrement().
    515   */
    516  Atomic<int32_t> mGrowthChunkSize;
    517 
    518  /**
    519   * Stores a list of the SQLite extensions loaded for this connections.
    520   * This is used to properly clone the connection.
    521   * @note Hold sharedAsyncExecutionMutex while using this.
    522   */
    523  nsTHashSet<nsCString> mLoadedExtensions
    524      MOZ_GUARDED_BY(sharedAsyncExecutionMutex);
    525 };
    526 
    527 /**
    528 * A Runnable designed to call a mozIStorageCompletionCallback on
    529 * the appropriate thread.
    530 */
    531 class CallbackComplete final : public Runnable {
    532 public:
    533  /**
    534   * @param aValue The result to pass to the callback. It must
    535   *               already be owned by the main thread.
    536   * @param aCallback The callback. It must already be owned by the
    537   *                  main thread.
    538   */
    539  CallbackComplete(nsresult aStatus, nsISupports* aValue,
    540                   already_AddRefed<mozIStorageCompletionCallback> aCallback)
    541      : Runnable("storage::CallbackComplete"),
    542        mStatus(aStatus),
    543        mValue(aValue),
    544        mCallback(aCallback) {}
    545 
    546  NS_IMETHOD Run() override {
    547    MOZ_ASSERT(NS_IsMainThread());
    548    nsresult rv = mCallback->Complete(mStatus, mValue);
    549 
    550    // Ensure that we release on the main thread
    551    mValue = nullptr;
    552    mCallback = nullptr;
    553    return rv;
    554  }
    555 
    556 private:
    557  nsresult mStatus;
    558  nsCOMPtr<nsISupports> mValue;
    559  // This is a RefPtr<T> and not a nsCOMPtr<T> because
    560  // nsCOMP<T> would cause an off-main thread QI, which
    561  // is not a good idea (and crashes XPConnect).
    562  RefPtr<mozIStorageCompletionCallback> mCallback;
    563 };
    564 
    565 }  // namespace mozilla::storage
    566 
    567 /**
    568 * Casting Connection to nsISupports is ambiguous.
    569 * This method handles that.
    570 */
    571 inline nsISupports* ToSupports(mozilla::storage::Connection* p) {
    572  return NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, p);
    573 }
    574 
    575 #endif  // mozilla_storage_Connection_h