tor-browser

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

storage_test_harness.h (7362B)


      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 storage_test_harness_h__
      8 #define storage_test_harness_h__
      9 
     10 #include "gtest/gtest.h"
     11 
     12 #include "prthread.h"
     13 #include "nsAppDirectoryServiceDefs.h"
     14 #include "nsComponentManagerUtils.h"
     15 #include "nsDirectoryServiceDefs.h"
     16 #include "nsDirectoryServiceUtils.h"
     17 #include "nsServiceManagerUtils.h"
     18 #include "nsIThread.h"
     19 #include "nsThreadUtils.h"
     20 #include "mozilla/ReentrantMonitor.h"
     21 #include "mozilla/AutoSQLiteLifetime.h"
     22 
     23 #include "mozIStorageService.h"
     24 #include "mozIStorageConnection.h"
     25 #include "mozIStorageStatementCallback.h"
     26 #include "mozIStorageCompletionCallback.h"
     27 #include "mozIStorageBindingParamsArray.h"
     28 #include "mozIStorageBindingParams.h"
     29 #include "mozIStorageAsyncStatement.h"
     30 #include "mozIStorageStatement.h"
     31 #include "mozIStoragePendingStatement.h"
     32 #include "mozIStorageError.h"
     33 #include "nsIInterfaceRequestorUtils.h"
     34 #include "nsIEventTarget.h"
     35 
     36 #include "sqlite3.h"
     37 
     38 #define do_check_true(aCondition) EXPECT_TRUE(aCondition)
     39 
     40 #define do_check_false(aCondition) EXPECT_FALSE(aCondition)
     41 
     42 #define do_check_success(aResult) do_check_true(NS_SUCCEEDED(aResult))
     43 
     44 #define do_check_eq(aExpected, aActual) do_check_true(aExpected == aActual)
     45 
     46 #define do_check_ok(aInvoc) do_check_true((aInvoc) == SQLITE_OK)
     47 
     48 already_AddRefed<mozIStorageService> getService();
     49 
     50 already_AddRefed<mozIStorageConnection> getMemoryDatabase();
     51 
     52 already_AddRefed<mozIStorageConnection> getDatabase(
     53    nsIFile* aDBFile = nullptr,
     54    uint32_t aConnectionFlags = mozIStorageService::CONNECTION_DEFAULT);
     55 
     56 class AsyncStatementSpinner : public mozIStorageStatementCallback,
     57                              public mozIStorageCompletionCallback {
     58 public:
     59  NS_DECL_ISUPPORTS
     60  NS_DECL_MOZISTORAGESTATEMENTCALLBACK
     61  NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
     62 
     63  AsyncStatementSpinner();
     64 
     65  void SpinUntilCompleted();
     66 
     67  uint16_t completionReason;
     68 
     69 protected:
     70  virtual ~AsyncStatementSpinner() = default;
     71  volatile bool mCompleted;
     72 };
     73 
     74 class AsyncCompletionSpinner : public mozIStorageCompletionCallback {
     75 public:
     76  NS_DECL_ISUPPORTS
     77  NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
     78 
     79  AsyncCompletionSpinner();
     80 
     81  void SpinUntilCompleted();
     82 
     83  nsresult mCompletionReason;
     84  nsCOMPtr<nsISupports> mCompletionValue;
     85 
     86 protected:
     87  virtual ~AsyncCompletionSpinner() = default;
     88  volatile bool mCompleted;
     89 };
     90 
     91 #define NS_DECL_ASYNCSTATEMENTSPINNER \
     92  NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override;
     93 
     94 ////////////////////////////////////////////////////////////////////////////////
     95 //// Async Helpers
     96 
     97 /**
     98 * Execute an async statement, blocking the main thread until we get the
     99 * callback completion notification.
    100 */
    101 void blocking_async_execute(mozIStorageBaseStatement* stmt);
    102 
    103 /**
    104 * Invoke AsyncClose on the given connection, blocking the main thread until we
    105 * get the completion notification.
    106 */
    107 void blocking_async_close(mozIStorageConnection* db);
    108 
    109 ////////////////////////////////////////////////////////////////////////////////
    110 //// Mutex Watching
    111 
    112 /**
    113 * Verify that mozIStorageAsyncStatement's life-cycle never triggers a mutex on
    114 * the caller (generally main) thread.  We do this by decorating the sqlite
    115 * mutex logic with our own code that checks what thread it is being invoked on
    116 * and sets a flag if it is invoked on the main thread.  We are able to easily
    117 * decorate the SQLite mutex logic because SQLite allows us to retrieve the
    118 * current function pointers being used and then provide a new set.
    119 */
    120 
    121 extern sqlite3_mutex_methods orig_mutex_methods;
    122 extern sqlite3_mutex_methods wrapped_mutex_methods;
    123 
    124 extern bool mutex_used_on_watched_thread;
    125 extern PRThread* watched_thread;
    126 /**
    127 * Ugly hack to let us figure out what a connection's async thread is.  If we
    128 * were MOZILLA_INTERNAL_API and linked as such we could just include
    129 * mozStorageConnection.h and just ask Connection directly.  But that turns out
    130 * poorly.
    131 *
    132 * When the thread a mutex is invoked on isn't watched_thread we save it to this
    133 * variable.
    134 */
    135 extern nsIThread* last_non_watched_thread;
    136 
    137 /**
    138 * Set a flag if the mutex is used on the thread we are watching, but always
    139 * call the real mutex function.
    140 */
    141 extern "C" void wrapped_MutexEnter(sqlite3_mutex* mutex);
    142 
    143 extern "C" int wrapped_MutexTry(sqlite3_mutex* mutex);
    144 
    145 class HookSqliteMutex {
    146 public:
    147  HookSqliteMutex() {
    148    // We need to initialize and teardown SQLite to get it to set up the
    149    // default mutex handlers for us so we can steal them and wrap them.
    150    do_check_ok(sqlite3_initialize());
    151    do_check_ok(sqlite3_shutdown());
    152    do_check_ok(::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &orig_mutex_methods));
    153    do_check_ok(
    154        ::sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped_mutex_methods));
    155    wrapped_mutex_methods.xMutexEnter = wrapped_MutexEnter;
    156    wrapped_mutex_methods.xMutexTry = wrapped_MutexTry;
    157    do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped_mutex_methods));
    158  }
    159 
    160  ~HookSqliteMutex() {
    161    do_check_ok(sqlite3_shutdown());
    162    do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &orig_mutex_methods));
    163    mozilla::AutoSQLiteLifetime::Init();
    164    int rc = mozilla::AutoSQLiteLifetime::getInitResult();
    165    MOZ_RELEASE_ASSERT(rc == SQLITE_OK);
    166  }
    167 };
    168 
    169 /**
    170 * Call to clear the watch state and to set the watching against this thread.
    171 *
    172 * Check |mutex_used_on_watched_thread| to see if the mutex has fired since
    173 * this method was last called.  Since we're talking about the current thread,
    174 * there are no race issues to be concerned about
    175 */
    176 void watch_for_mutex_use_on_this_thread();
    177 
    178 ////////////////////////////////////////////////////////////////////////////////
    179 //// Thread Wedgers
    180 
    181 /**
    182 * A runnable that blocks until code on another thread invokes its unwedge
    183 * method.  By dispatching this to a thread you can ensure that no subsequent
    184 * runnables dispatched to the thread will execute until you invoke unwedge.
    185 *
    186 * The wedger is self-dispatching, just construct it with its target.
    187 */
    188 class ThreadWedger : public mozilla::Runnable {
    189 public:
    190  explicit ThreadWedger(nsIEventTarget* aTarget)
    191      : mozilla::Runnable("ThreadWedger"),
    192        mReentrantMonitor("thread wedger"),
    193        unwedged(false) {
    194    aTarget->Dispatch(this, aTarget->NS_DISPATCH_NORMAL);
    195  }
    196 
    197  NS_IMETHOD Run() override {
    198    mozilla::ReentrantMonitorAutoEnter automon(mReentrantMonitor);
    199 
    200    if (!unwedged) automon.Wait();
    201 
    202    return NS_OK;
    203  }
    204 
    205  void unwedge() {
    206    mozilla::ReentrantMonitorAutoEnter automon(mReentrantMonitor);
    207    unwedged = true;
    208    automon.Notify();
    209  }
    210 
    211 private:
    212  mozilla::ReentrantMonitor mReentrantMonitor MOZ_UNANNOTATED;
    213  bool unwedged;
    214 };
    215 
    216 ////////////////////////////////////////////////////////////////////////////////
    217 //// Async Helpers
    218 
    219 /**
    220 * A horrible hack to figure out what the connection's async thread is.  By
    221 * creating a statement and async dispatching we can tell from the mutex who
    222 * is the async thread, PRThread style.  Then we map that to an nsIThread.
    223 */
    224 already_AddRefed<nsIThread> get_conn_async_thread(mozIStorageConnection* db);
    225 
    226 #endif  // storage_test_harness_h__