Use the fixes from bug 571599 instead of what we had.
authorShawn Wilsher <sdwilsh@shawnwilsher.com>
Mon, 21 Jun 2010 14:35:10 -0700
changeset 44112 44f33414d557476ec4aa74dffaf88842d43f2066
parent 44111 afc9375444a282757b927d97deaf7a27b32af553
child 44113 a6a0eeaa7f0f2a0e9b688dbd1cbd130c7d1101a5
push idunknown
push userunknown
push dateunknown
bugs571599
milestone1.9.3a6pre
Use the fixes from bug 571599 instead of what we had.
configure.in
storage/src/Makefile.in
storage/src/SharedCacheUnlockNotify.cpp
storage/src/SharedCacheUnlockNotify.h
storage/src/mozStorageAsyncStatement.cpp
storage/src/mozStorageAsyncStatementExecution.cpp
storage/src/mozStorageConnection.cpp
storage/src/mozStoragePrivateHelpers.cpp
storage/src/mozStoragePrivateHelpers.h
storage/src/mozStorageStatement.cpp
storage/test/Makefile.in
storage/test/storage_test_harness.h
storage/test/test_unlock_notify.cpp
--- a/configure.in
+++ b/configure.in
@@ -6694,17 +6694,17 @@ else
     if test "x$ac_cv_sqlite_threadsafe" = "xno"; then
         AC_MSG_ERROR([System SQLite library is not compiled with SQLITE_THREADSAFE.])
     fi
 
     dnl ================================
     dnl === SQLITE_ENABLE_FTS3 check ===
     dnl ================================
     dnl check to see if the system SQLite package is compiled with
-    dnl SQLITE_THREADSAFE enabled.
+    dnl SQLITE_ENABLE_FTS3 enabled.
     AC_MSG_CHECKING(for SQLITE_ENABLE_FTS3 support in system SQLite)
     _SAVE_CFLAGS="$CFLAGS"
     CFLAGS="$CFLAGS $SQLITE_CFLAGS"
     _SAVE_LIBS="$LIBS"
     LIBS="$LIBS $SQLITE_LIBS"
     AC_CACHE_VAL(ac_cv_sqlite_enable_fts3,[
         AC_TRY_RUN([
             #include "sqlite3.h"
@@ -6719,19 +6719,19 @@ else
     ])
     AC_MSG_RESULT($ac_cv_sqlite_enable_fts3)
     CFLAGS="$_SAVE_CFLAGS"
     LIBS="$_SAVE_LIBS"
     if test "x$ac_cv_sqlite_enable_fts3" = "xno"; then
         AC_MSG_ERROR([System SQLite library is not compiled with SQLITE_ENABLE_FTS3.])
     fi
 
-    dnl ================================
+    dnl =========================================
     dnl === SQLITE_ENABLE_UNLOCK_NOTIFY check ===
-    dnl ================================
+    dnl =========================================
     dnl check to see if the system SQLite package is compiled with
     dnl SQLITE_ENABLE_UNLOCK_NOTIFY enabled.
     AC_MSG_CHECKING(for SQLITE_ENABLE_UNLOCK_NOTIFY support in system SQLite)
     _SAVE_CFLAGS="$CFLAGS"
     CFLAGS="$CFLAGS $SQLITE_CFLAGS"
     _SAVE_LIBS="$LIBS"
     LIBS="$LIBS $SQLITE_LIBS"
     AC_CACHE_VAL(ac_cv_sqlite_enable_unlock_notify,[
--- a/storage/src/Makefile.in
+++ b/storage/src/Makefile.in
@@ -73,17 +73,16 @@ CPPSRCS = \
   mozStorageAsyncStatementExecution.cpp \
   mozStorageStatementJSHelper.cpp \
   mozStoragePrivateHelpers.cpp \
   mozStorageBindingParamsArray.cpp \
   mozStorageBindingParams.cpp \
   mozStorageAsyncStatement.cpp \
   mozStorageAsyncStatementJSHelper.cpp \
   mozStorageAsyncStatementParams.cpp \
-  SharedCacheUnlockNotify.cpp \
   StorageBaseStatementInternal.cpp \
   SQLCollations.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
 	$(SQLITE_CFLAGS)
 
 # This is the default value.  If we ever change it when compiling sqlite, we
deleted file mode 100644
--- a/storage/src/SharedCacheUnlockNotify.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* ***** 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 Indexed Database.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Ben Turner <bent.mozilla@gmail.com>
- *
- * 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 ***** */
-
-/**
- * This code is heavily based on the sample at:
- *   http://www.sqlite.org/unlock_notify.html
- */
-
-#include "SharedCacheUnlockNotify.h"
-
-#include "mozilla/Mutex.h"
-#include "mozilla/CondVar.h"
-#include "nsThreadUtils.h"
-
-namespace {
-
-class UnlockNotification
-{
-public:
-  UnlockNotification()
-  : mMutex("UnlockNotification mMutex"),
-    mCondVar(mMutex, "UnlockNotification condVar"),
-    mSignaled(false)
-  { }
-
-  void Wait()
-  {
-    mozilla::MutexAutoLock lock(mMutex);
-    while (!mSignaled) {
-      mCondVar.Wait();
-    }
-  }
-
-  void Signal()
-  {
-    mozilla::MutexAutoLock lock(mMutex);
-    mSignaled = true;
-    mCondVar.Notify();
-  }
-
-private:
-  mozilla::Mutex mMutex;
-  mozilla::CondVar mCondVar;
-  bool mSignaled;
-};
-
-void
-UnlockNotifyCallback(void** apArg,
-                     int nArg)
-{
-  for (int i = 0; i < nArg; i++) {
-    UnlockNotification* notification =
-      static_cast<UnlockNotification*>(apArg[i]);
-    notification->Signal();
-  }
-}
-
-int
-WaitForUnlockNotify(sqlite3* db)
-{
-  UnlockNotification notification;
-  int srv = ::sqlite3_unlock_notify(db, UnlockNotifyCallback, &notification);
-  NS_ASSERTION(srv == SQLITE_LOCKED || srv == SQLITE_OK, "Bad result!");
-
-  if (srv == SQLITE_OK) {
-    notification.Wait();
-  }
-
-  return srv;
-}
-
-} // anonymous namespace
-
-namespace mozilla {
-namespace storage {
-
-int
-moz_sqlite3_step(sqlite3_stmt* pStmt)
-{
-  bool checkedMainThread = false;
-
-  int srv;
-  while ((srv = ::sqlite3_step(pStmt)) == SQLITE_LOCKED) {
-    if (!checkedMainThread) {
-      checkedMainThread = true;
-      if (NS_IsMainThread()) {
-        NS_WARNING("We won't allow blocking on the main thread!");
-        break;
-      }
-    }
-
-    srv = WaitForUnlockNotify(sqlite3_db_handle(pStmt));
-    if (srv != SQLITE_OK)
-      break;
-
-    sqlite3_reset(pStmt);
-  }
-
-  return srv;
-}
-
-int
-moz_sqlite3_prepare_v2(sqlite3* db,
-                       const char* zSql,
-                       int nByte,
-                       sqlite3_stmt** ppStmt,
-                       const char** pzTail)
-{
-  bool checkedMainThread = false;
-
-  int srv;
-  while((srv = ::sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail)) ==
-        SQLITE_LOCKED) {
-    if (!checkedMainThread) {
-      checkedMainThread = true;
-      if (NS_IsMainThread()) {
-        NS_WARNING("We won't allow blocking on the main thread!");
-        break;
-      }
-    }
-
-    srv = WaitForUnlockNotify(db);
-    if (srv != SQLITE_OK)
-      break;
-  }
-
-  return srv;
-}
-
-} /* namespace storage */
-} /* namespace mozilla */
deleted file mode 100644
--- a/storage/src/SharedCacheUnlockNotify.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* ***** 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 Indexed Database.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Ben Turner <bent.mozilla@gmail.com>
- *
- * 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 _sharedCacheUnlockNotify_h_
-#define _sharedCacheUnlockNotify_h_
-
-#include "sqlite3.h"
-
-namespace mozilla {
-namespace storage {
-
-extern int moz_sqlite3_step(sqlite3_stmt* pStmt);
-
-extern int moz_sqlite3_prepare_v2(sqlite3* db,
-                                  const char* zSql,
-                                  int nByte,
-                                  sqlite3_stmt** ppStmt,
-                                  const char** pzTail);
-
-} /* namespace storage */
-} /* namespace mozilla */
-
-#endif /* _sharedCacheUnlockNotify_h_ */
--- a/storage/src/mozStorageAsyncStatement.cpp
+++ b/storage/src/mozStorageAsyncStatement.cpp
@@ -53,17 +53,16 @@
 
 #include "mozStorageBindingParams.h"
 #include "mozStorageConnection.h"
 #include "mozStorageAsyncStatementJSHelper.h"
 #include "mozStorageAsyncStatementParams.h"
 #include "mozStoragePrivateHelpers.h"
 #include "mozStorageStatementRow.h"
 #include "mozStorageStatement.h"
-#include "SharedCacheUnlockNotify.h"
 
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo *gStorageLog;
 #endif
 
 namespace mozilla {
@@ -328,19 +327,18 @@ AsyncStatement::getAsyncStatement(sqlite
   // Make sure we are never called on the connection's owning thread.
   PRBool onOpenedThread = PR_FALSE;
   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
   NS_ASSERTION(!onOpenedThread,
                "We should only be called on the async thread!");
 #endif
 
   if (!mAsyncStatement) {
-    int rc = moz_sqlite3_prepare_v2(mDBConnection->GetNativeConnection(),
-                                    mSQLString.get(), -1,
-                                    &mAsyncStatement, NULL);
+    int rc = prepareStmt(mDBConnection->GetNativeConnection(), mSQLString,
+                         &mAsyncStatement);
     if (rc != SQLITE_OK) {
 #ifdef PR_LOGGING
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Sqlite statement prepare error: %d '%s'", rc,
               ::sqlite3_errmsg(mDBConnection->GetNativeConnection())));
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Statement was: '%s'", mSQLString.get()));
 #endif
--- a/storage/src/mozStorageAsyncStatementExecution.cpp
+++ b/storage/src/mozStorageAsyncStatementExecution.cpp
@@ -47,17 +47,16 @@
 #include "mozStorageHelper.h"
 #include "mozStorageResultSet.h"
 #include "mozStorageRow.h"
 #include "mozStorageConnection.h"
 #include "mozStorageError.h"
 #include "mozStoragePrivateHelpers.h"
 #include "mozStorageStatementData.h"
 #include "mozStorageAsyncStatementExecution.h"
-#include "SharedCacheUnlockNotify.h"
 
 namespace mozilla {
 namespace storage {
 
 /**
  * The following constants help batch rows into result sets.
  * MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that
  * takes less than 200 milliseconds is considered to feel instantaneous to end
@@ -338,17 +337,17 @@ bool
 AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
 {
   mMutex.AssertNotCurrentThreadOwns();
 
   while (true) {
     // lock the sqlite mutex so sqlite3_errmsg cannot change
     SQLiteMutexAutoLock lockedScope(mDBMutex);
 
-    int rc = moz_sqlite3_step(aStatement);
+    int rc = stepStmt(aStatement);
     // Stop if we have no more results.
     if (rc == SQLITE_DONE)
       return false;
 
     // If we got results, we can return now.
     if (rc == SQLITE_ROW)
       return true;
 
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -61,17 +61,16 @@
 #include "mozStorageSQLFunctions.h"
 #include "mozStorageConnection.h"
 #include "mozStorageService.h"
 #include "mozStorageStatement.h"
 #include "mozStorageAsyncStatement.h"
 #include "mozStorageArgValueArray.h"
 #include "mozStoragePrivateHelpers.h"
 #include "mozStorageStatementData.h"
-#include "SharedCacheUnlockNotify.h"
 #include "StorageBaseStatementInternal.h"
 #include "SQLCollations.h"
 
 #include "prlog.h"
 #include "prprf.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gStorageLog = nsnull;
@@ -409,20 +408,20 @@ Connection::initialize(nsIFile *aDatabas
     ::sqlite3_close(mDBConn);
     mDBConn = nsnull;
     return convertResultCode(srv);
   }
 
   // Execute a dummy statement to force the db open, and to verify if it is
   // valid or not.
   sqlite3_stmt *stmt;
-  srv = moz_sqlite3_prepare_v2(mDBConn, "SELECT * FROM sqlite_master", -1,
-                               &stmt, NULL);
+  srv = prepareStmt(mDBConn, NS_LITERAL_CSTRING("SELECT * FROM sqlite_master"),
+                    &stmt);
   if (srv == SQLITE_OK) {
-    srv = moz_sqlite3_step(stmt);
+    srv = stepStmt(stmt);
 
     if (srv == SQLITE_DONE || srv == SQLITE_ROW)
         srv = SQLITE_OK;
     ::sqlite3_finalize(stmt);
   }
 
   if (srv != SQLITE_OK) {
     ::sqlite3_close(mDBConn);
@@ -472,21 +471,21 @@ Connection::databaseElementExists(enum D
       query.Append("table");
       break;
   }
   query.Append("' AND name ='");
   query.Append(aElementName);
   query.Append("'");
 
   sqlite3_stmt *stmt;
-  int srv = moz_sqlite3_prepare_v2(mDBConn, query.get(), -1, &stmt, NULL);
+  int srv = prepareStmt(mDBConn, query, &stmt);
   if (srv != SQLITE_OK)
     return convertResultCode(srv);
 
-  srv = moz_sqlite3_step(stmt);
+  srv = stepStmt(stmt);
   // we just care about the return value from step
   (void)::sqlite3_finalize(stmt);
 
   if (srv == SQLITE_ROW) {
     *_exists = PR_TRUE;
     return NS_OK;
   }
   if (srv == SQLITE_DONE) {
--- a/storage/src/mozStoragePrivateHelpers.cpp
+++ b/storage/src/mozStoragePrivateHelpers.cpp
@@ -41,31 +41,36 @@
 #include "sqlite3.h"
 
 #include "jsapi.h"
 #include "jsdate.h"
 
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsError.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
 #include "nsThreadUtils.h"
 
 #include "Variant.h"
 #include "mozStoragePrivateHelpers.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageCompletionCallback.h"
 #include "mozIStorageBindingParams.h"
 
 namespace mozilla {
 namespace storage {
 
 nsresult
 convertResultCode(int aSQLiteResultCode)
 {
-  switch (aSQLiteResultCode) {
+  // Drop off the extended result bits of the result code.
+  int rc = aSQLiteResultCode & 0xFF;
+
+  switch (rc) {
     case SQLITE_OK:
     case SQLITE_ROW:
     case SQLITE_DONE:
       return NS_OK;
     case SQLITE_CORRUPT:
     case SQLITE_NOTADB:
       return NS_ERROR_FILE_CORRUPTED;
     case SQLITE_PERM:
@@ -92,17 +97,17 @@ convertResultCode(int aSQLiteResultCode)
     case SQLITE_CONSTRAINT:
       return NS_ERROR_STORAGE_CONSTRAINT;
   }
 
   // generic error
 #ifdef DEBUG
   nsCAutoString message;
   message.AppendLiteral("SQLite returned error code ");
-  message.AppendInt(aSQLiteResultCode);
+  message.AppendInt(rc);
   message.AppendLiteral(" , Storage will convert it to NS_ERROR_FAILURE");
   NS_WARNING(message.get());
 #endif
   return NS_ERROR_FAILURE;
 }
 
 void
 checkAndLogStatementPerformance(sqlite3_stmt *aStatement)
@@ -198,10 +203,133 @@ private:
 already_AddRefed<nsIRunnable>
 newCompletionEvent(mozIStorageCompletionCallback *aCallback)
 {
   NS_ASSERTION(aCallback, "Passing a null callback is a no-no!");
   nsCOMPtr<nsIRunnable> event = new CallbackEvent(aCallback);
   return event.forget();
 }
 
+/**
+ * This code is heavily based on the sample at:
+ *   http://www.sqlite.org/unlock_notify.html
+ */
+namespace {
+
+class UnlockNotification
+{
+public:
+  UnlockNotification()
+  : mMutex("UnlockNotification mMutex")
+  , mCondVar(mMutex, "UnlockNotification condVar")
+  , mSignaled(false)
+  {
+  }
+
+  void Wait()
+  {
+    mozilla::MutexAutoLock lock(mMutex);
+    while (!mSignaled) {
+      (void)mCondVar.Wait();
+    }
+  }
+
+  void Signal()
+  {
+    mozilla::MutexAutoLock lock(mMutex);
+    mSignaled = true;
+    (void)mCondVar.Notify();
+  }
+
+private:
+  mozilla::Mutex mMutex;
+  mozilla::CondVar mCondVar;
+  bool mSignaled;
+};
+
+void
+UnlockNotifyCallback(void **aArgs,
+                     int aArgsSize)
+{
+  for (int i = 0; i < aArgsSize; i++) {
+    UnlockNotification *notification =
+      static_cast<UnlockNotification *>(aArgs[i]);
+    notification->Signal();
+  }
+}
+
+int
+WaitForUnlockNotify(sqlite3* aDatabase)
+{
+  UnlockNotification notification;
+  int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
+                                    &notification);
+  NS_ASSERTION(srv == SQLITE_LOCKED || srv == SQLITE_OK, "Bad result!");
+  if (srv == SQLITE_OK)
+    notification.Wait();
+
+  return srv;
+}
+
+} // anonymous namespace
+
+int
+stepStmt(sqlite3_stmt* aStatement)
+{
+  bool checkedMainThread = false;
+
+  sqlite3* db = ::sqlite3_db_handle(aStatement);
+  (void)::sqlite3_extended_result_codes(db, 1);
+
+  int srv;
+  while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
+    if (!checkedMainThread) {
+      checkedMainThread = true;
+      if (NS_IsMainThread()) {
+        NS_WARNING("We won't allow blocking on the main thread!");
+        break;
+      }
+    }
+
+    srv = WaitForUnlockNotify(sqlite3_db_handle(aStatement));
+    if (srv != SQLITE_OK)
+      break;
+
+    ::sqlite3_reset(aStatement);
+  }
+
+  (void)::sqlite3_extended_result_codes(db, 0);
+  // Drop off the extended result bits of the result code.
+  return srv & 0xFF;
+}
+
+int
+prepareStmt(sqlite3* aDatabase,
+            const nsCString &aSQL,
+            sqlite3_stmt **_stmt)
+{
+  bool checkedMainThread = false;
+
+  (void)::sqlite3_extended_result_codes(aDatabase, 1);
+
+  int srv;
+  while((srv = ::sqlite3_prepare_v2(aDatabase, aSQL.get(), -1, _stmt, NULL)) ==
+        SQLITE_LOCKED_SHAREDCACHE) {
+    if (!checkedMainThread) {
+      checkedMainThread = true;
+      if (NS_IsMainThread()) {
+        NS_WARNING("We won't allow blocking on the main thread!");
+        break;
+      }
+    }
+
+    srv = WaitForUnlockNotify(aDatabase);
+    if (srv != SQLITE_OK)
+      break;
+  }
+
+  (void)::sqlite3_extended_result_codes(aDatabase, 0);
+  // Drop off the extended result bits of the result code.
+  return srv & 0xFF;
+}
+
 } // namespace storage
 } // namespace mozilla
--- a/storage/src/mozStoragePrivateHelpers.h
+++ b/storage/src/mozStoragePrivateHelpers.h
@@ -108,12 +108,35 @@ nsIVariant *convertJSValToVariant(JSCont
  * @param aCallback
  *        The callback to be notified.
  * @return an nsIRunnable that can be dispatched to the calling thread.
  */
 already_AddRefed<nsIRunnable> newCompletionEvent(
   mozIStorageCompletionCallback *aCallback
 );
 
+
+/**
+ * Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
+ * when not on the main thread by waiting until we are notified.
+ *
+ * @param aStatement
+ *        A pointer to a sqlite3_stmt object.
+ * @return the result from sqlite3_step.
+ */
+int stepStmt(sqlite3_stmt *aStatement);
+
+/**
+ * Obtains a prepared sqlite3_stmt object for aDatabase from aSQL.
+ *
+ * @param aDatabase
+ *        The database the statement will execute on.
+ * @param aSQL
+ *        The SQL statement to compile.
+ * @return the result from sqlite3_prepare_v2.
+ */
+int prepareStmt(sqlite3 *aDatabase, const nsCString &aSQL,
+                sqlite3_stmt **_stmt);
+
 } // namespace storage
 } // namespace mozilla
 
 #endif // mozStoragePrivateHelpers_h
--- a/storage/src/mozStorageStatement.cpp
+++ b/storage/src/mozStorageStatement.cpp
@@ -53,17 +53,16 @@
 
 #include "mozStorageBindingParams.h"
 #include "mozStorageConnection.h"
 #include "mozStorageStatementJSHelper.h"
 #include "mozStoragePrivateHelpers.h"
 #include "mozStorageStatementParams.h"
 #include "mozStorageStatementRow.h"
 #include "mozStorageStatement.h"
-#include "SharedCacheUnlockNotify.h"
 
 #include "prlog.h"
 
 #include "mozilla/FunctionTimer.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gStorageLog;
 #endif
@@ -171,18 +170,17 @@ Statement::initialize(Connection *aDBCon
                       const nsACString &aSQLStatement)
 {
   NS_ASSERTION(aDBConnection, "No database connection given!");
   NS_ASSERTION(!mDBStatement, "Statement already initialized!");
 
   sqlite3 *db = aDBConnection->GetNativeConnection();
   NS_ASSERTION(db, "We should never be called with a null sqlite3 database!");
 
-  int srv = moz_sqlite3_prepare_v2(db, PromiseFlatCString(aSQLStatement).get(),
-                                   -1, &mDBStatement, NULL);
+  int srv = prepareStmt(db, PromiseFlatCString(aSQLStatement), &mDBStatement);
   if (srv != SQLITE_OK) {
 #ifdef PR_LOGGING
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Sqlite statement prepare error: %d '%s'", srv,
               ::sqlite3_errmsg(db)));
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
 #endif
@@ -315,19 +313,19 @@ Statement::getOwner()
 int
 Statement::getAsyncStatement(sqlite3_stmt **_stmt)
 {
   // If we have no statement, we shouldn't be calling this method!
   NS_ASSERTION(mDBStatement != NULL, "We have no statement to clone!");
 
   // If we do not yet have a cached async statement, clone our statement now.
   if (!mAsyncStatement) {
-    int rc = moz_sqlite3_prepare_v2(mDBConnection->GetNativeConnection(),
-                                    ::sqlite3_sql(mDBStatement), -1,
-                                    &mAsyncStatement, NULL);
+    nsDependentCString sql(::sqlite3_sql(mDBStatement));
+    int rc = prepareStmt(mDBConnection->GetNativeConnection(), sql,
+                     &mAsyncStatement);
     if (rc != SQLITE_OK) {
       *_stmt = nsnull;
       return rc;
     }
 
 #ifdef PR_LOGGING
     PR_LOG(gStorageLog, PR_LOG_NOTICE,
            ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
@@ -611,17 +609,17 @@ Statement::ExecuteStep(PRBool *_moreResu
       PRInt32 srv;
       (void)error->GetResult(&srv);
       return convertResultCode(srv);
     }
 
     // We have bound, so now we can clear our array.
     mParamsArray = nsnull;
   }
-  int srv = moz_sqlite3_step(mDBStatement);
+  int srv = stepStmt(mDBStatement);
 
 #ifdef PR_LOGGING
   if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
       nsCAutoString errStr;
       (void)mDBConnection->GetLastErrorString(errStr);
       PR_LOG(gStorageLog, PR_LOG_DEBUG,
              ("Statement::ExecuteStep error: %s", errStr.get()));
   }
--- a/storage/test/Makefile.in
+++ b/storage/test/Makefile.in
@@ -49,16 +49,17 @@ MODULE = test_storage
 XPCSHELL_TESTS = unit
 
 CPP_UNIT_TESTS = \
   test_transaction_helper.cpp \
   test_statement_scoper.cpp \
   test_mutex.cpp \
   test_binding_params.cpp \
   test_true_async.cpp \
+  test_unlock_notify.cpp \
   $(NULL)
 
 ifdef MOZ_DEBUG
 # FIXME bug 523392: test_deadlock_detector doesn't like Windows
 # FIXME bug 523378: also fails on OS X
 ifneq (,$(filter-out WINNT WINCE Darwin,$(OS_ARCH)))
 CPP_UNIT_TESTS += \
   test_deadlock_detector.cpp \
--- a/storage/test/storage_test_harness.h
+++ b/storage/test/storage_test_harness.h
@@ -11,17 +11,17 @@
  * 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.
+ * The Mozilla Foundation.
  * 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
@@ -34,41 +34,70 @@
  * 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 "TestHarness.h"
 #include "nsMemory.h"
+#include "nsDirectoryServiceDefs.h"
 #include "mozIStorageService.h"
 #include "mozIStorageConnection.h"
 
 static int gTotalTests = 0;
 static int gPassedTests = 0;
 
 #define do_check_true(aCondition) \
   PR_BEGIN_MACRO \
     gTotalTests++; \
-    if (aCondition) \
+    if (aCondition) { \
       gPassedTests++; \
-    else \
-      fail("Expected true, got false on line %d!", __LINE__); \
+    } else { \
+      fail("Expected true, got false at %s:%d!", __FILE__, __LINE__); \
+    } \
   PR_END_MACRO
 
 #define do_check_false(aCondition) \
   PR_BEGIN_MACRO \
     gTotalTests++; \
-    if (!aCondition) \
+    if (!aCondition) { \
       gPassedTests++; \
-    else \
-      fail("Expected false, got true on line %d!", __LINE__); \
+    } else { \
+      fail("Expected false, got true at %s:%d!", __FILE__, __LINE__); \
+    } \
   PR_END_MACRO
 
+#define do_check_success(aResult) \
+  do_check_true(NS_SUCCEEDED(aResult))
+
+#define do_check_eq(aFirst, aSecond) \
+  do_check_true(aFirst == aSecond)
+
 already_AddRefed<mozIStorageConnection>
 getMemoryDatabase()
 {
   nsCOMPtr<mozIStorageService> ss =
     do_GetService("@mozilla.org/storage/service;1");
   nsCOMPtr<mozIStorageConnection> conn;
-  (void)ss->OpenSpecialDatabase("memory", getter_AddRefs(conn));
+  nsresult rv = ss->OpenSpecialDatabase("memory", getter_AddRefs(conn));
+  do_check_success(rv);
   return conn.forget();
 }
+
+already_AddRefed<mozIStorageConnection>
+getDatabase()
+{
+  nsCOMPtr<nsIFile> dbFile;
+  (void)NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                               getter_AddRefs(dbFile));
+  NS_ASSERTION(dbFile, "The directory doesn't exists?!");
+
+  nsresult rv = dbFile->Append(NS_LITERAL_STRING("storage_test_db.sqlite"));
+  do_check_success(rv);
+
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService("@mozilla.org/storage/service;1");
+  nsCOMPtr<mozIStorageConnection> conn;
+  rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
+  do_check_success(rv);
+  return conn.forget();
+}
new file mode 100644
--- /dev/null
+++ b/storage/test/test_unlock_notify.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim set: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
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "mozilla/Monitor.h"
+#include "nsThreadUtils.h"
+#include "mozIStorageStatement.h"
+
+/**
+ * This file tests that our implementation around sqlite3_unlock_notify works
+ * as expected.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Helpers
+
+enum State {
+  STARTING,
+  WRITE_LOCK,
+  READ_LOCK,
+  TEST_DONE
+};
+
+class DatabaseLocker : public nsRunnable
+{
+public:
+  DatabaseLocker(const char* aSQL)
+  : monitor("DatabaseLocker::monitor")
+  , mSQL(aSQL)
+  , mState(STARTING)
+  {
+  }
+
+  void RunInBackground()
+  {
+    (void)NS_NewThread(getter_AddRefs(mThread));
+    do_check_true(mThread);
+
+    do_check_success(mThread->Dispatch(this, NS_DISPATCH_NORMAL));
+  }
+
+  NS_IMETHOD Run()
+  {
+    mozilla::MonitorAutoEnter lock(monitor);
+
+    nsCOMPtr<mozIStorageConnection> db(getDatabase());
+
+    nsCString sql(mSQL);
+    nsCOMPtr<mozIStorageStatement> stmt;
+    do_check_success(db->CreateStatement(sql, getter_AddRefs(stmt)));
+
+    PRBool hasResult;
+    do_check_success(stmt->ExecuteStep(&hasResult));
+
+    Notify(WRITE_LOCK);
+    WaitFor(TEST_DONE);
+
+    return NS_OK;
+  }
+
+  void WaitFor(State aState)
+  {
+    monitor.AssertCurrentThreadIn();
+    while (mState != aState) {
+      do_check_success(monitor.Wait());
+    }
+  }
+
+  void Notify(State aState)
+  {
+    monitor.AssertCurrentThreadIn();
+    mState = aState;
+    do_check_success(monitor.Notify());
+  }
+
+  mozilla::Monitor monitor;
+
+protected:
+  nsCOMPtr<nsIThread> mThread;
+  const char *const mSQL;
+  State mState;
+};
+
+class DatabaseTester : public DatabaseLocker
+{
+public:
+  DatabaseTester(mozIStorageConnection *aConnection,
+                 const char* aSQL)
+  : DatabaseLocker(aSQL)
+  , mConnection(aConnection)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    mozilla::MonitorAutoEnter lock(monitor);
+    WaitFor(READ_LOCK);
+
+    nsCString sql(mSQL);
+    nsCOMPtr<mozIStorageStatement> stmt;
+    do_check_success(mConnection->CreateStatement(sql, getter_AddRefs(stmt)));
+
+    PRBool hasResult;
+    nsresult rv = stmt->ExecuteStep(&hasResult);
+    do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
+
+    Notify(TEST_DONE);
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<mozIStorageConnection> mConnection;
+  State mState;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Functions
+
+void
+setup()
+{
+  nsCOMPtr<mozIStorageConnection> db(getDatabase());
+
+  // Create and populate a dummy table.
+  nsresult rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE test (id INTEGER PRIMARY KEY, data STRING)"
+  ));
+  do_check_success(rv);
+  rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO test (data) VALUES ('foo')"
+  ));
+  do_check_success(rv);
+  rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO test (data) VALUES ('bar')"
+  ));
+  do_check_success(rv);
+  rv = db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE UNIQUE INDEX unique_data ON test (data)"
+  ));
+  do_check_success(rv);
+}
+
+void
+test_step_locked_does_not_block_main_thread()
+{
+  nsCOMPtr<mozIStorageConnection> db(getDatabase());
+
+  // Need to prepare our statement ahead of time so we make sure to only test
+  // step and not prepare.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO test (data) VALUES ('test1')"
+  ), getter_AddRefs(stmt));
+  do_check_success(rv);
+
+  nsRefPtr<DatabaseLocker> locker(new DatabaseLocker("SELECT * FROM test"));
+  do_check_true(locker);
+  mozilla::MonitorAutoEnter lock(locker->monitor);
+  locker->RunInBackground();
+
+  // Wait for the locker to notify us that it has locked the database properly.
+  locker->WaitFor(WRITE_LOCK);
+
+  PRBool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  do_check_eq(rv, NS_ERROR_FILE_IS_LOCKED);
+
+  locker->Notify(TEST_DONE);
+}
+
+void
+test_drop_index_does_not_loop()
+{
+  nsCOMPtr<mozIStorageConnection> db(getDatabase());
+
+  // Need to prepare our statement ahead of time so we make sure to only test
+  // step and not prepare.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT * FROM test"
+  ), getter_AddRefs(stmt));
+  do_check_success(rv);
+
+  nsRefPtr<DatabaseTester> tester =
+    new DatabaseTester(db, "DROP INDEX unique_data");
+  do_check_true(tester);
+  mozilla::MonitorAutoEnter lock(tester->monitor);
+  tester->RunInBackground();
+
+  // Hold a read lock on the database, and then let the tester try to execute.
+  PRBool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  do_check_success(rv);
+  do_check_true(hasResult);
+  tester->Notify(READ_LOCK);
+
+  // Make sure the tester finishes its test before we move on.
+  tester->WaitFor(TEST_DONE);
+}
+
+void
+test_drop_table_does_not_loop()
+{
+  nsCOMPtr<mozIStorageConnection> db(getDatabase());
+
+  // Need to prepare our statement ahead of time so we make sure to only test
+  // step and not prepare.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  nsresult rv = db->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT * FROM test"
+  ), getter_AddRefs(stmt));
+  do_check_success(rv);
+
+  nsRefPtr<DatabaseTester> tester(new DatabaseTester(db, "DROP TABLE test"));
+  do_check_true(tester);
+  mozilla::MonitorAutoEnter lock(tester->monitor);
+  tester->RunInBackground();
+
+  // Hold a read lock on the database, and then let the tester try to execute.
+  PRBool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  do_check_success(rv);
+  do_check_true(hasResult);
+  tester->Notify(READ_LOCK);
+
+  // Make sure the tester finishes its test before we move on.
+  tester->WaitFor(TEST_DONE);
+}
+
+void (*gTests[])(void) = {
+  setup,
+  test_step_locked_does_not_block_main_thread,
+  test_drop_index_does_not_loop,
+  test_drop_table_does_not_loop,
+};
+
+const char *file = __FILE__;
+#define TEST_NAME "sqlite3_unlock_notify"
+#define TEST_FILE file
+#include "storage_test_harness_tail.h"