Bug 494828 - Stop using our own mutexes and use SQLite's where possible.
Part 1: Create helper objects to make using sqlite3_mutex safer and easier.
r=cjones
new file mode 100644
--- /dev/null
+++ b/storage/src/SQLiteMutex.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_storage_SQLiteMutex_h_
+#define mozilla_storage_SQLiteMutex_h_
+
+#include "mozilla/BlockingResourceBase.h"
+#include "sqlite3.h"
+
+namespace mozilla {
+namespace storage {
+
+/**
+ * Wrapper class for sqlite3_mutexes. To be used whenever we want to use a
+ * sqlite3_mutex.
+ *
+ * @warning Never EVER wrap the same sqlite3_mutex with a different SQLiteMutex.
+ * If you do this, you void the deadlock detector's warranty!
+ */
+class SQLiteMutex : private BlockingResourceBase
+{
+public:
+ /**
+ * Constructs a wrapper for a sqlite3_mutex that has deadlock detecting.
+ *
+ * @param aName
+ * A name which can be used to reference this mutex.
+ */
+ SQLiteMutex(const char *aName)
+ : BlockingResourceBase(aName, eMutex)
+ , mMutex(NULL)
+ {
+ }
+
+ /**
+ * Sets the mutex that we are wrapping. We generally do not have access to
+ * our mutex at class construction, so we have to set it once we get access to
+ * it.
+ *
+ * @param aMutex
+ * The sqlite3_mutex that we are going to wrap.
+ */
+ void initWithMutex(sqlite3_mutex *aMutex)
+ {
+ NS_ASSERTION(aMutex, "You must pass in a valid mutex!");
+ NS_ASSERTION(!mMutex, "A mutex has already been set for this!");
+ mMutex = aMutex;
+ }
+
+#ifndef DEBUG
+ /**
+ * Acquires the mutex.
+ */
+ void lock()
+ {
+ sqlite3_mutex_enter(mMutex);
+ }
+
+ /**
+ * Releases the mutex.
+ */
+ void unlock()
+ {
+ sqlite3_mutex_leave(mMutex);
+ }
+
+ /**
+ * Asserts that the current thread owns the mutex.
+ */
+ void assertCurrentThreadOwns()
+ {
+ }
+
+ /**
+ * Asserts that the current thread does not own the mutex.
+ */
+ void assertNotCurrentThreadOwns()
+ {
+ }
+
+#else
+ void lock()
+ {
+ NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+
+ // While SQLite Mutexes may be recursive, in our own code we do not want to
+ // treat them as such.
+ CallStack callContext = CallStack();
+
+ CheckAcquire(callContext);
+ sqlite3_mutex_enter(mMutex);
+ Acquire(callContext); // Call is protected by us holding the mutex.
+ }
+
+ void unlock()
+ {
+ NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+
+ // While SQLite Mutexes may be recursive, in our own code we do not want to
+ // treat them as such.
+ Release(); // Call is protected by us holding the mutex.
+ sqlite3_mutex_leave(mMutex);
+ }
+
+ void assertCurrentThreadOwns()
+ {
+ NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+ NS_ASSERTION(sqlite3_mutex_held(mMutex),
+ "Mutex is not held, but we expect it to be!");
+ }
+
+ void assertNotCurrentThreadOwns()
+ {
+ NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+ NS_ASSERTION(sqlite3_mutex_notheld(mMutex),
+ "Mutex is held, but we expect it to not be!");
+ }
+#endif // ifndef DEBUG
+
+private:
+ sqlite3_mutex *mMutex;
+};
+
+/**
+ * Automatically acquires the mutex when it enters scope, and releases it when
+ * it leaves scope.
+ */
+class NS_STACK_CLASS SQLiteMutexAutoLock
+{
+public:
+ SQLiteMutexAutoLock(SQLiteMutex &aMutex)
+ : mMutex(aMutex)
+ {
+ mMutex.lock();
+ }
+
+ ~SQLiteMutexAutoLock()
+ {
+ mMutex.unlock();
+ }
+
+private:
+ SQLiteMutex &mMutex;
+};
+
+/**
+ * Automatically releases the mutex when it enters scope, and acquires it when
+ * it leaves scope.
+ */
+class NS_STACK_CLASS SQLiteMutexAutoUnlock
+{
+public:
+ SQLiteMutexAutoUnlock(SQLiteMutex &aMutex)
+ : mMutex(aMutex)
+ {
+ mMutex.unlock();
+ }
+
+ ~SQLiteMutexAutoUnlock()
+ {
+ mMutex.lock();
+ }
+
+private:
+ SQLiteMutex &mMutex;
+};
+
+} // namespace storage
+} // namespace mozilla
+
+#endif // mozilla_storage_SQLiteMutex_h_
--- a/storage/test/Makefile.in
+++ b/storage/test/Makefile.in
@@ -46,23 +46,41 @@ include $(DEPTH)/config/autoconf.mk
MODULE = test_storage
XPCSHELL_TESTS = unit
CPP_UNIT_TESTS = \
test_transaction_helper.cpp \
test_statement_scoper.cpp \
+ test_mutex.cpp \
$(NULL)
+ifdef MOZ_DEBUG
+# Testing assertion failures is fragile, and test_deadlock_detector doesn't like
+# windows.
+ifneq ($(OS_ARCH), WINNT)
+ifneq ($(OS_ARCH), WINCE)
+CPP_UNIT_TESTS += \
+ test_deadlock_detector.cpp \
+ $(NULL)
+endif
+endif
+endif
+
REQUIRES = \
xpcom \
string \
storage \
$(NULL)
+LOCAL_INCLUDES = \
+ -I$(srcdir)/../src \
+ $(NULL)
+
LIBS = \
$(LIBS_DIR) \
$(XPCOM_GLUE_LDOPTS) \
$(NSPR_LIBS) \
+ $(SQLITE_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk
copy from xpcom/tests/TestDeadlockDetector.cpp
copy to storage/test/test_deadlock_detector.cpp
--- a/xpcom/tests/TestDeadlockDetector.cpp
+++ b/storage/test/test_deadlock_detector.cpp
@@ -11,18 +11,18 @@
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Jones <jones.chris.g@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -32,31 +32,69 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
+/**
+ * Note: This file is a copy of xpcom/tests/TestDeadlockDetector.cpp, but all
+ * mutexes were turned into SQLiteMutexes.
+ */
+
#include "prenv.h"
#include "prerror.h"
#include "prio.h"
#include "prproces.h"
#include "nsMemory.h"
#include "mozilla/CondVar.h"
#include "mozilla/Monitor.h"
-#include "mozilla/Mutex.h"
+#include "SQLiteMutex.h"
#include "TestHarness.h"
using namespace mozilla;
+/**
+ * Helper class to allocate a sqlite3_mutex for our SQLiteMutex. Also makes
+ * keeping the test files in sync easier.
+ */
+class TestMutex : public mozilla::storage::SQLiteMutex
+{
+public:
+ TestMutex(const char* aName)
+ : mozilla::storage::SQLiteMutex(aName)
+ , mInner(sqlite3_mutex_alloc(SQLITE_MUTEX_FAST))
+ {
+ NS_ASSERTION(mInner, "could not allocate a sqlite3_mutex");
+ initWithMutex(mInner);
+ }
+
+ ~TestMutex()
+ {
+ sqlite3_mutex_free(mInner);
+ }
+
+ void Lock()
+ {
+ lock();
+ }
+
+ void Unlock()
+ {
+ unlock();
+ }
+private:
+ sqlite3_mutex *mInner;
+};
+
static PRThread*
spawn(void (*run)(void*), void* arg)
{
return PR_CreateThread(PR_SYSTEM_THREAD,
run,
arg,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
@@ -282,17 +320,17 @@ CheckForDeadlock(const char* test, const
//-----------------------------------------------------------------------------
// Single-threaded sanity tests
// Stupidest possible deadlock.
nsresult
Sanity_Child()
{
- mozilla::Mutex m1("dd.sanity.m1");
+ TestMutex m1("dd.sanity.m1");
m1.Lock();
m1.Lock();
return 0; // not reached
}
nsresult
Sanity()
{
@@ -310,18 +348,18 @@ Sanity()
FAIL("deadlock not detected");
}
}
// Slightly less stupid deadlock.
nsresult
Sanity2_Child()
{
- mozilla::Mutex m1("dd.sanity2.m1");
- mozilla::Mutex m2("dd.sanity2.m2");
+ TestMutex m1("dd.sanity2.m1");
+ TestMutex m2("dd.sanity2.m2");
m1.Lock();
m2.Lock();
m1.Lock();
return 0; // not reached
}
nsresult
Sanity2()
@@ -341,20 +379,20 @@ Sanity2()
FAIL("deadlock not detected");
}
}
nsresult
Sanity3_Child()
{
- mozilla::Mutex m1("dd.sanity3.m1");
- mozilla::Mutex m2("dd.sanity3.m2");
- mozilla::Mutex m3("dd.sanity3.m3");
- mozilla::Mutex m4("dd.sanity3.m4");
+ TestMutex m1("dd.sanity3.m1");
+ TestMutex m2("dd.sanity3.m2");
+ TestMutex m3("dd.sanity3.m3");
+ TestMutex m4("dd.sanity3.m4");
m1.Lock();
m2.Lock();
m3.Lock();
m4.Lock();
m4.Unlock();
m3.Unlock();
m2.Unlock();
@@ -385,17 +423,17 @@ Sanity3()
}
}
nsresult
Sanity4_Child()
{
mozilla::Monitor m1("dd.sanity4.m1");
- mozilla::Mutex m2("dd.sanity4.m2");
+ TestMutex m2("dd.sanity4.m2");
m1.Enter();
m2.Lock();
m1.Enter();
return 0;
}
nsresult
Sanity4()
@@ -414,18 +452,18 @@ Sanity4()
} else {
FAIL("deadlock not detected");
}
}
//-----------------------------------------------------------------------------
// Multithreaded tests
-mozilla::Mutex* ttM1;
-mozilla::Mutex* ttM2;
+TestMutex* ttM1;
+TestMutex* ttM2;
static void
TwoThreads_thread(void* arg)
{
PRInt32 m1First = NS_PTR_TO_INT32(arg);
if (m1First) {
ttM1->Lock();
ttM2->Lock();
@@ -438,18 +476,18 @@ TwoThreads_thread(void* arg)
ttM1->Unlock();
ttM2->Unlock();
}
}
nsresult
TwoThreads_Child()
{
- ttM1 = new mozilla::Mutex("dd.twothreads.m1");
- ttM2 = new mozilla::Mutex("dd.twothreads.m2");
+ ttM1 = new TestMutex("dd.twothreads.m1");
+ ttM2 = new TestMutex("dd.twothreads.m2");
if (!ttM1 || !ttM2)
NS_RUNTIMEABORT("couldn't allocate mutexes");
PRThread* t1 = spawn(TwoThreads_thread, (void*) 0);
PR_JoinThread(t1);
PRThread* t2 = spawn(TwoThreads_thread, (void*) 1);
PR_JoinThread(t2);
@@ -472,17 +510,17 @@ TwoThreads()
if (CheckForDeadlock("TwoThreads", tokens)) {
PASS();
} else {
FAIL("deadlock not detected");
}
}
-mozilla::Mutex* cndMs[4];
+TestMutex* cndMs[4];
const PRUint32 K = 100000;
static void
ContentionNoDeadlock_thread(void* arg)
{
PRInt32 starti = NS_PTR_TO_INT32(arg);
for (PRUint32 k = 0; k < K; ++k) {
@@ -497,17 +535,17 @@ ContentionNoDeadlock_thread(void* arg)
}
nsresult
ContentionNoDeadlock_Child()
{
PRThread* threads[3];
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(cndMs); ++i)
- cndMs[i] = new mozilla::Mutex("dd.cnd.ms");
+ cndMs[i] = new TestMutex("dd.cnd.ms");
for (PRInt32 i = 0; i < (PRInt32) NS_ARRAY_LENGTH(threads); ++i)
threads[i] = spawn(ContentionNoDeadlock_thread, NS_INT32_TO_PTR(i));
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(threads); ++i)
PR_JoinThread(threads[i]);
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(cndMs); ++i)
new file mode 100644
--- /dev/null
+++ b/storage/test/test_mutex.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is storage test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "storage_test_harness.h"
+
+#include "SQLiteMutex.h"
+
+using namespace mozilla::storage;
+
+/**
+ * This file test our sqlite3_mutex wrapper in SQLiteMutex.h.
+ */
+
+void
+test_AutoLock()
+{
+ int lockTypes[] = {
+ SQLITE_MUTEX_FAST,
+ SQLITE_MUTEX_RECURSIVE,
+ };
+ for (size_t i = 0; i < NS_ARRAY_LENGTH(lockTypes); i++) {
+ // Get our test mutex (we have to allocate a SQLite mutex of the right type
+ // too!).
+ SQLiteMutex mutex("TestMutex");
+ sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]);
+ do_check_true(inner);
+ mutex.initWithMutex(inner);
+
+ // And test that our automatic locking wrapper works as expected.
+ mutex.assertNotCurrentThreadOwns();
+ {
+ SQLiteMutexAutoLock lockedScope(mutex);
+ mutex.assertCurrentThreadOwns();
+ }
+ mutex.assertNotCurrentThreadOwns();
+
+ // Free the wrapped mutex - we don't need it anymore.
+ sqlite3_mutex_free(inner);
+ }
+}
+
+void
+test_AutoUnlock()
+{
+ int lockTypes[] = {
+ SQLITE_MUTEX_FAST,
+ SQLITE_MUTEX_RECURSIVE,
+ };
+ for (size_t i = 0; i < NS_ARRAY_LENGTH(lockTypes); i++) {
+ // Get our test mutex (we have to allocate a SQLite mutex of the right type
+ // too!).
+ SQLiteMutex mutex("TestMutex");
+ sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]);
+ do_check_true(inner);
+ mutex.initWithMutex(inner);
+
+ // And test that our automatic unlocking wrapper works as expected.
+ {
+ SQLiteMutexAutoLock lockedScope(mutex);
+
+ {
+ SQLiteMutexAutoUnlock unlockedScope(mutex);
+ mutex.assertNotCurrentThreadOwns();
+ }
+ mutex.assertCurrentThreadOwns();
+ }
+
+ // Free the wrapped mutex - we don't need it anymore.
+ sqlite3_mutex_free(inner);
+ }
+}
+
+void (*gTests[])(void) = {
+ test_AutoLock,
+ test_AutoUnlock,
+};
+
+const char *file = __FILE__;
+#define TEST_NAME "SQLiteMutex"
+#define TEST_FILE file
+#include "storage_test_harness_tail.h"
--- a/xpcom/tests/TestDeadlockDetector.cpp
+++ b/xpcom/tests/TestDeadlockDetector.cpp
@@ -11,18 +11,18 @@
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Jones <jones.chris.g@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),