1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/test/test_unlock_notify.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,250 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim set:sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "storage_test_harness.h" 1.11 + 1.12 +#include "mozilla/ReentrantMonitor.h" 1.13 +#include "nsThreadUtils.h" 1.14 +#include "mozIStorageStatement.h" 1.15 + 1.16 +/** 1.17 + * This file tests that our implementation around sqlite3_unlock_notify works 1.18 + * as expected. 1.19 + */ 1.20 + 1.21 +//////////////////////////////////////////////////////////////////////////////// 1.22 +//// Helpers 1.23 + 1.24 +enum State { 1.25 + STARTING, 1.26 + WRITE_LOCK, 1.27 + READ_LOCK, 1.28 + TEST_DONE 1.29 +}; 1.30 + 1.31 +class DatabaseLocker : public nsRunnable 1.32 +{ 1.33 +public: 1.34 + DatabaseLocker(const char* aSQL) 1.35 + : monitor("DatabaseLocker::monitor") 1.36 + , mSQL(aSQL) 1.37 + , mState(STARTING) 1.38 + { 1.39 + } 1.40 + 1.41 + void RunInBackground() 1.42 + { 1.43 + (void)NS_NewThread(getter_AddRefs(mThread)); 1.44 + do_check_true(mThread); 1.45 + 1.46 + do_check_success(mThread->Dispatch(this, NS_DISPATCH_NORMAL)); 1.47 + } 1.48 + 1.49 + NS_IMETHOD Run() 1.50 + { 1.51 + mozilla::ReentrantMonitorAutoEnter lock(monitor); 1.52 + 1.53 + nsCOMPtr<mozIStorageConnection> db(getDatabase()); 1.54 + 1.55 + nsCString sql(mSQL); 1.56 + nsCOMPtr<mozIStorageStatement> stmt; 1.57 + do_check_success(db->CreateStatement(sql, getter_AddRefs(stmt))); 1.58 + 1.59 + bool hasResult; 1.60 + do_check_success(stmt->ExecuteStep(&hasResult)); 1.61 + 1.62 + Notify(WRITE_LOCK); 1.63 + WaitFor(TEST_DONE); 1.64 + 1.65 + return NS_OK; 1.66 + } 1.67 + 1.68 + void WaitFor(State aState) 1.69 + { 1.70 + monitor.AssertCurrentThreadIn(); 1.71 + while (mState != aState) { 1.72 + do_check_success(monitor.Wait()); 1.73 + } 1.74 + } 1.75 + 1.76 + void Notify(State aState) 1.77 + { 1.78 + monitor.AssertCurrentThreadIn(); 1.79 + mState = aState; 1.80 + do_check_success(monitor.Notify()); 1.81 + } 1.82 + 1.83 + mozilla::ReentrantMonitor monitor; 1.84 + 1.85 +protected: 1.86 + nsCOMPtr<nsIThread> mThread; 1.87 + const char *const mSQL; 1.88 + State mState; 1.89 +}; 1.90 + 1.91 +class DatabaseTester : public DatabaseLocker 1.92 +{ 1.93 +public: 1.94 + DatabaseTester(mozIStorageConnection *aConnection, 1.95 + const char* aSQL) 1.96 + : DatabaseLocker(aSQL) 1.97 + , mConnection(aConnection) 1.98 + { 1.99 + } 1.100 + 1.101 + NS_IMETHOD Run() 1.102 + { 1.103 + mozilla::ReentrantMonitorAutoEnter lock(monitor); 1.104 + WaitFor(READ_LOCK); 1.105 + 1.106 + nsCString sql(mSQL); 1.107 + nsCOMPtr<mozIStorageStatement> stmt; 1.108 + do_check_success(mConnection->CreateStatement(sql, getter_AddRefs(stmt))); 1.109 + 1.110 + bool hasResult; 1.111 + nsresult rv = stmt->ExecuteStep(&hasResult); 1.112 + do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED); 1.113 + 1.114 + // Finalize our statement and null out our connection before notifying to 1.115 + // ensure that we close on the proper thread. 1.116 + rv = stmt->Finalize(); 1.117 + do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED); 1.118 + mConnection = nullptr; 1.119 + 1.120 + Notify(TEST_DONE); 1.121 + 1.122 + return NS_OK; 1.123 + } 1.124 + 1.125 +private: 1.126 + nsCOMPtr<mozIStorageConnection> mConnection; 1.127 +}; 1.128 + 1.129 +//////////////////////////////////////////////////////////////////////////////// 1.130 +//// Test Functions 1.131 + 1.132 +void 1.133 +setup() 1.134 +{ 1.135 + nsCOMPtr<mozIStorageConnection> db(getDatabase()); 1.136 + 1.137 + // Create and populate a dummy table. 1.138 + nsresult rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.139 + "CREATE TABLE test (id INTEGER PRIMARY KEY, data STRING)" 1.140 + )); 1.141 + do_check_success(rv); 1.142 + rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.143 + "INSERT INTO test (data) VALUES ('foo')" 1.144 + )); 1.145 + do_check_success(rv); 1.146 + rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.147 + "INSERT INTO test (data) VALUES ('bar')" 1.148 + )); 1.149 + do_check_success(rv); 1.150 + rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.151 + "CREATE UNIQUE INDEX unique_data ON test (data)" 1.152 + )); 1.153 + do_check_success(rv); 1.154 +} 1.155 + 1.156 +void 1.157 +test_step_locked_does_not_block_main_thread() 1.158 +{ 1.159 + nsCOMPtr<mozIStorageConnection> db(getDatabase()); 1.160 + 1.161 + // Need to prepare our statement ahead of time so we make sure to only test 1.162 + // step and not prepare. 1.163 + nsCOMPtr<mozIStorageStatement> stmt; 1.164 + nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING( 1.165 + "INSERT INTO test (data) VALUES ('test1')" 1.166 + ), getter_AddRefs(stmt)); 1.167 + do_check_success(rv); 1.168 + 1.169 + nsRefPtr<DatabaseLocker> locker(new DatabaseLocker("SELECT * FROM test")); 1.170 + do_check_true(locker); 1.171 + mozilla::ReentrantMonitorAutoEnter lock(locker->monitor); 1.172 + locker->RunInBackground(); 1.173 + 1.174 + // Wait for the locker to notify us that it has locked the database properly. 1.175 + locker->WaitFor(WRITE_LOCK); 1.176 + 1.177 + bool hasResult; 1.178 + rv = stmt->ExecuteStep(&hasResult); 1.179 + do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED); 1.180 + 1.181 + locker->Notify(TEST_DONE); 1.182 +} 1.183 + 1.184 +void 1.185 +test_drop_index_does_not_loop() 1.186 +{ 1.187 + nsCOMPtr<mozIStorageConnection> db(getDatabase()); 1.188 + 1.189 + // Need to prepare our statement ahead of time so we make sure to only test 1.190 + // step and not prepare. 1.191 + nsCOMPtr<mozIStorageStatement> stmt; 1.192 + nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING( 1.193 + "SELECT * FROM test" 1.194 + ), getter_AddRefs(stmt)); 1.195 + do_check_success(rv); 1.196 + 1.197 + nsRefPtr<DatabaseTester> tester = 1.198 + new DatabaseTester(db, "DROP INDEX unique_data"); 1.199 + do_check_true(tester); 1.200 + mozilla::ReentrantMonitorAutoEnter lock(tester->monitor); 1.201 + tester->RunInBackground(); 1.202 + 1.203 + // Hold a read lock on the database, and then let the tester try to execute. 1.204 + bool hasResult; 1.205 + rv = stmt->ExecuteStep(&hasResult); 1.206 + do_check_success(rv); 1.207 + do_check_true(hasResult); 1.208 + tester->Notify(READ_LOCK); 1.209 + 1.210 + // Make sure the tester finishes its test before we move on. 1.211 + tester->WaitFor(TEST_DONE); 1.212 +} 1.213 + 1.214 +void 1.215 +test_drop_table_does_not_loop() 1.216 +{ 1.217 + nsCOMPtr<mozIStorageConnection> db(getDatabase()); 1.218 + 1.219 + // Need to prepare our statement ahead of time so we make sure to only test 1.220 + // step and not prepare. 1.221 + nsCOMPtr<mozIStorageStatement> stmt; 1.222 + nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING( 1.223 + "SELECT * FROM test" 1.224 + ), getter_AddRefs(stmt)); 1.225 + do_check_success(rv); 1.226 + 1.227 + nsRefPtr<DatabaseTester> tester(new DatabaseTester(db, "DROP TABLE test")); 1.228 + do_check_true(tester); 1.229 + mozilla::ReentrantMonitorAutoEnter lock(tester->monitor); 1.230 + tester->RunInBackground(); 1.231 + 1.232 + // Hold a read lock on the database, and then let the tester try to execute. 1.233 + bool hasResult; 1.234 + rv = stmt->ExecuteStep(&hasResult); 1.235 + do_check_success(rv); 1.236 + do_check_true(hasResult); 1.237 + tester->Notify(READ_LOCK); 1.238 + 1.239 + // Make sure the tester finishes its test before we move on. 1.240 + tester->WaitFor(TEST_DONE); 1.241 +} 1.242 + 1.243 +void (*gTests[])(void) = { 1.244 + setup, 1.245 + test_step_locked_does_not_block_main_thread, 1.246 + test_drop_index_does_not_loop, 1.247 + test_drop_table_does_not_loop, 1.248 +}; 1.249 + 1.250 +const char *file = __FILE__; 1.251 +#define TEST_NAME "sqlite3_unlock_notify" 1.252 +#define TEST_FILE file 1.253 +#include "storage_test_harness_tail.h"