tor-browser

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

test_unlock_notify.cpp (6744B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim set: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 #include "storage_test_harness.h"
      8 
      9 #include "mozilla/ReentrantMonitor.h"
     10 #include "nsThreadUtils.h"
     11 #include "mozIStorageStatement.h"
     12 
     13 /**
     14 * This file tests that our implementation around sqlite3_unlock_notify works
     15 * as expected.
     16 */
     17 
     18 ////////////////////////////////////////////////////////////////////////////////
     19 //// Helpers
     20 
     21 enum State { STARTING, WRITE_LOCK, READ_LOCK, TEST_DONE };
     22 
     23 class DatabaseLocker : public mozilla::Runnable {
     24 public:
     25  explicit DatabaseLocker(const char* aSQL, nsIFile* aDBFile = nullptr)
     26      : mozilla::Runnable("DatabaseLocker"),
     27        monitor("DatabaseLocker::monitor"),
     28        mSQL(aSQL),
     29        mState(STARTING),
     30        mDBFile(aDBFile) {}
     31 
     32  void RunInBackground() {
     33    (void)NS_NewNamedThread("DatabaseLocker", getter_AddRefs(mThread));
     34    do_check_true(mThread);
     35 
     36    do_check_success(mThread->Dispatch(this, NS_DISPATCH_NORMAL));
     37  }
     38 
     39  void Shutdown() {
     40    if (mThread) {
     41      mThread->Shutdown();
     42    }
     43  }
     44 
     45  NS_IMETHOD Run() override {
     46    mozilla::ReentrantMonitorAutoEnter lock(monitor);
     47 
     48    nsCOMPtr<mozIStorageConnection> db(getDatabase(mDBFile));
     49 
     50    nsCString sql(mSQL);
     51    nsCOMPtr<mozIStorageStatement> stmt;
     52    do_check_success(db->CreateStatement(sql, getter_AddRefs(stmt)));
     53 
     54    bool hasResult;
     55    do_check_success(stmt->ExecuteStep(&hasResult));
     56 
     57    Notify(WRITE_LOCK);
     58    WaitFor(TEST_DONE);
     59 
     60    return NS_OK;
     61  }
     62 
     63  void WaitFor(State aState) {
     64    monitor.AssertCurrentThreadIn();
     65    while (mState != aState) {
     66      do_check_success(monitor.Wait());
     67    }
     68  }
     69 
     70  void Notify(State aState) {
     71    monitor.AssertCurrentThreadIn();
     72    mState = aState;
     73    do_check_success(monitor.Notify());
     74  }
     75 
     76  mozilla::ReentrantMonitor monitor MOZ_UNANNOTATED;
     77 
     78 protected:
     79  nsCOMPtr<nsIThread> mThread;
     80  const char* const mSQL;
     81  State mState;
     82  nsCOMPtr<nsIFile> mDBFile;
     83 };
     84 
     85 class DatabaseTester : public DatabaseLocker {
     86 public:
     87  DatabaseTester(mozIStorageConnection* aConnection, const char* aSQL)
     88      : DatabaseLocker(aSQL), mConnection(aConnection) {}
     89 
     90  NS_IMETHOD Run() override {
     91    mozilla::ReentrantMonitorAutoEnter lock(monitor);
     92    WaitFor(READ_LOCK);
     93 
     94    nsCString sql(mSQL);
     95    nsCOMPtr<mozIStorageStatement> stmt;
     96    do_check_success(mConnection->CreateStatement(sql, getter_AddRefs(stmt)));
     97 
     98    bool hasResult;
     99    nsresult rv = stmt->ExecuteStep(&hasResult);
    100    do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
    101 
    102    // Finalize our statement and null out our connection before notifying to
    103    // ensure that we close on the proper thread.
    104    rv = stmt->Finalize();
    105    do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
    106    mConnection = nullptr;
    107 
    108    Notify(TEST_DONE);
    109 
    110    return NS_OK;
    111  }
    112 
    113 private:
    114  nsCOMPtr<mozIStorageConnection> mConnection;
    115 };
    116 
    117 ////////////////////////////////////////////////////////////////////////////////
    118 //// Test Functions
    119 
    120 void setup() {
    121  nsCOMPtr<mozIStorageConnection> db(getDatabase());
    122 
    123  // Create and populate a dummy table.
    124  nsresult rv = db->ExecuteSimpleSQL(nsLiteralCString(
    125      "CREATE TABLE test (id INTEGER PRIMARY KEY, data STRING)"));
    126  do_check_success(rv);
    127  rv = db->ExecuteSimpleSQL("INSERT INTO test (data) VALUES ('foo')"_ns);
    128  do_check_success(rv);
    129  rv = db->ExecuteSimpleSQL("INSERT INTO test (data) VALUES ('bar')"_ns);
    130  do_check_success(rv);
    131  rv =
    132      db->ExecuteSimpleSQL("CREATE UNIQUE INDEX unique_data ON test (data)"_ns);
    133  do_check_success(rv);
    134 }
    135 
    136 void test_step_locked_does_not_block_main_thread() {
    137  nsCOMPtr<mozIStorageConnection> db(getDatabase());
    138 
    139  // Need to prepare our statement ahead of time so we make sure to only test
    140  // step and not prepare.
    141  nsCOMPtr<mozIStorageStatement> stmt;
    142  nsresult rv = db->CreateStatement(
    143      "INSERT INTO test (data) VALUES ('test1')"_ns, getter_AddRefs(stmt));
    144  do_check_success(rv);
    145 
    146  nsCOMPtr<nsIFile> dbFile;
    147  db->GetDatabaseFile(getter_AddRefs(dbFile));
    148  RefPtr<DatabaseLocker> locker(
    149      new DatabaseLocker("SELECT * FROM test", dbFile));
    150  do_check_true(locker);
    151  {
    152    mozilla::ReentrantMonitorAutoEnter lock(locker->monitor);
    153    locker->RunInBackground();
    154 
    155    // Wait for the locker to notify us that it has locked the database
    156    // properly.
    157    locker->WaitFor(WRITE_LOCK);
    158 
    159    bool hasResult;
    160    rv = stmt->ExecuteStep(&hasResult);
    161    do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
    162 
    163    locker->Notify(TEST_DONE);
    164  }
    165  locker->Shutdown();
    166 }
    167 
    168 void test_drop_index_does_not_loop() {
    169  nsCOMPtr<mozIStorageConnection> db(getDatabase());
    170 
    171  // Need to prepare our statement ahead of time so we make sure to only test
    172  // step and not prepare.
    173  nsCOMPtr<mozIStorageStatement> stmt;
    174  nsresult rv =
    175      db->CreateStatement("SELECT * FROM test"_ns, getter_AddRefs(stmt));
    176  do_check_success(rv);
    177 
    178  RefPtr<DatabaseTester> tester =
    179      new DatabaseTester(db, "DROP INDEX unique_data");
    180  do_check_true(tester);
    181  {
    182    mozilla::ReentrantMonitorAutoEnter lock(tester->monitor);
    183    tester->RunInBackground();
    184 
    185    // Hold a read lock on the database, and then let the tester try to execute.
    186    bool hasResult;
    187    rv = stmt->ExecuteStep(&hasResult);
    188    do_check_success(rv);
    189    do_check_true(hasResult);
    190    tester->Notify(READ_LOCK);
    191 
    192    // Make sure the tester finishes its test before we move on.
    193    tester->WaitFor(TEST_DONE);
    194  }
    195  tester->Shutdown();
    196 }
    197 
    198 void test_drop_table_does_not_loop() {
    199  nsCOMPtr<mozIStorageConnection> db(getDatabase());
    200 
    201  // Need to prepare our statement ahead of time so we make sure to only test
    202  // step and not prepare.
    203  nsCOMPtr<mozIStorageStatement> stmt;
    204  nsresult rv =
    205      db->CreateStatement("SELECT * FROM test"_ns, getter_AddRefs(stmt));
    206  do_check_success(rv);
    207 
    208  RefPtr<DatabaseTester> tester(new DatabaseTester(db, "DROP TABLE test"));
    209  do_check_true(tester);
    210  {
    211    mozilla::ReentrantMonitorAutoEnter lock(tester->monitor);
    212    tester->RunInBackground();
    213 
    214    // Hold a read lock on the database, and then let the tester try to execute.
    215    bool hasResult;
    216    rv = stmt->ExecuteStep(&hasResult);
    217    do_check_success(rv);
    218    do_check_true(hasResult);
    219    tester->Notify(READ_LOCK);
    220 
    221    // Make sure the tester finishes its test before we move on.
    222    tester->WaitFor(TEST_DONE);
    223  }
    224  tester->Shutdown();
    225 }
    226 
    227 TEST(storage_unlock_notify, Test)
    228 {
    229  // These must execute in order.
    230  setup();
    231  test_step_locked_does_not_block_main_thread();
    232  test_drop_index_does_not_loop();
    233  test_drop_table_does_not_loop();
    234 }