Bug 429986 - Provide an option for database access to be asynchronous.
authorShawn Wilsher <sdwilsh@shawnwilsher.com>
Fri, 11 Jul 2008 15:47:33 -0400
changeset 15803 1a1e94b4dca83b4c1c968908cef6eee6ebf40cc2
parent 15802 b8df7471a2e2f6de5c96523f223b36db46ef7500
child 15804 fd493cecad665ada58f560677e4f3f0a3bda40a3
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs429986
milestone1.9.1a1pre
Bug 429986 - Provide an option for database access to be asynchronous. This adds a method to mozIStorageStatement to allow for a statement to execute asynchronously and report to a callback. For writes, this can move fsyncs, which can be painful, off of the main thread. r=vlad sr=shaver
storage/public/Makefile.in
storage/public/mozIStorageError.idl
storage/public/mozIStoragePendingStatement.idl
storage/public/mozIStorageStatement.idl
storage/public/mozIStorageStatementCallback.idl
storage/src/Makefile.in
storage/src/mozStorageBackground.cpp
storage/src/mozStorageBackground.h
storage/src/mozStorageError.cpp
storage/src/mozStorageError.h
storage/src/mozStorageEvents.cpp
storage/src/mozStorageEvents.h
storage/src/mozStorageService.cpp
storage/src/mozStorageService.h
storage/src/mozStorageStatement.cpp
storage/src/mozStorageStatement.h
--- a/storage/public/Makefile.in
+++ b/storage/public/Makefile.in
@@ -55,15 +55,18 @@ XPIDLSRCS = \
 	mozIStorageFunction.idl \
 	mozIStorageProgressHandler.idl \
 	mozIStorageStatement.idl \
 	mozIStorageStatementWrapper.idl \
 	mozIStorageDataSet.idl \
 	mozIStorageValueArray.idl \
 	mozIStorageResultSet.idl \
 	mozIStorageRow.idl \
+  mozIStorageError.idl \
+  mozIStorageStatementCallback.idl \
+  mozIStoragePendingStatement.idl \
 	$(NULL)
 
 EXPORTS = \
 	mozStorageHelper.h \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageError.idl
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab 
+ * ***** 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) 2008
+ * 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 "nsISupports.idl"
+
+[scriptable, uuid(1f350f96-7023-434a-8864-40a1c493aac1)]
+interface mozIStorageError : nsISupports {
+
+  /**
+   * General SQL error or missing database.
+   */
+  const long ERROR = 1;
+
+  /**
+   * Internal logic error.
+   */
+  const long INTERNAL = 2;
+
+  /**
+   * Access permission denied.
+   */
+  const long PERM = 3;
+
+  /**
+   * A callback routine requested an abort.
+   */
+  const long ABORT = 4;
+
+  /**
+   * The database file is locked.
+   */
+  const long BUSY = 5;
+
+  /**
+   * A table in the database is locked.
+   */
+  const long LOCKED = 6;
+
+  /**
+   * An allocation failed.
+   */
+  const long NOMEM = 7;
+
+  /**
+   * Attempt to write to a readonly database.
+   */
+  const long READONLY = 8;
+
+  /**
+   * Operation was terminated by an interrupt.
+   */
+  const long INTERRUPT = 9;
+
+  /**
+   * Some kind of disk I/O error occurred.
+   */
+  const long IOERR = 10;
+
+  /**
+   * The database disk image is malformed.
+   */
+  const long CORRUPT = 11;
+
+  /**
+   * An insertion failed because the database is full.
+   */
+  const long FULL = 13;
+
+  /**
+   * Unable to open the database file.
+   */
+  const long CANTOPEN = 14;
+
+  /**
+   * The database is empty.
+   */
+  const long EMPTY = 16;
+
+  /**
+   * The database scheme changed.
+   */
+  const long SCHEMA = 17;
+
+  /**
+   * A string or blob exceeds the size limit.
+   */
+  const long TOOBIG = 18;
+
+  /**
+   * Abort due to a constraint violation.
+   */
+  const long CONSTRAINT = 19;
+
+  /**
+   * Data type mismatch.
+   */
+  const long MISMATCH = 20;
+
+  /**
+   * Library used incorrectly.
+   */
+  const long MISUSE = 21;
+
+  /**
+   * Uses OS features not supported on the host system.
+   */
+  const long NOLFS = 22;
+
+  /**
+   * Authorization denied.
+   */
+  const long AUTH = 23;
+
+  /**
+   * Auxiliary database format error.
+   */
+  const long FORMAT = 24;
+
+  /**
+   * File opened that is not a database file.
+   */
+  const long NOTADB = 26;
+
+
+  /**
+   * Indicates what type of error occurred.
+   */
+  readonly attribute long result;
+
+  /**
+   * An error string the gives more details, if available.
+   */
+  readonly attribute AUTF8String message;
+};
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStoragePendingStatement.idl
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab 
+ * ***** 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) 2008
+ * 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 "nsISupports.idl"
+
+[scriptable, uuid(5c458b3a-8648-45dd-a9a0-c321949cd864)]
+interface mozIStoragePendingStatement : nsISupports {
+
+  /**
+   * Cancels a pending statement.  This may fail because the statement has
+   * already completed.
+   */
+   void cancel();
+};
--- a/storage/public/mozIStorageStatement.idl
+++ b/storage/public/mozIStorageStatement.idl
@@ -1,10 +1,11 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab 
+ * ***** 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,
@@ -37,20 +38,22 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "mozIStorageValueArray.idl"
 
 interface mozIStorageConnection;
 interface mozIStorageDataSet;
 interface nsISimpleEnumerator;
+interface mozIStorageStatementCallback;
+interface mozIStoragePendingStatement;
 
 [ptr] native sqlite3stmtptr(struct sqlite3_stmt);
 
-[scriptable, uuid(4101bda7-6ec8-4a72-bbf8-6569af0030ea)]
+[scriptable, uuid(4a712295-d076-4007-9c78-8c0e15373b9f)]
 interface mozIStorageStatement : mozIStorageValueArray {
   /**
    * Finalizes a statement so you can successfully close a database connection.
    * This method does not need to be used from native callers since you can just
    * set the statement to null, but is extremely useful to JS callers.
    */
   void finalize();
 
@@ -145,16 +148,29 @@ interface mozIStorageStatement : mozISto
    * @returns a boolean indicating whether there are more rows or not;
    * row data may be accessed using mozIStorageValueArray methods on
    * the statement.
    *
    */
   boolean executeStep();
 
   /**
+   * Execute a query asynchronously using any currently bound parameters.  This
+   * statement can be reused immediately, and reset does not need to be called.
+   *
+   * @param aCallback [optional]
+   *        The callback object that will be notified of progress, errors, and
+   *        completion.
+   * @returns an object that can be used to cancel the statements execution.
+   */
+  mozIStoragePendingStatement executeAsync(
+    [optional] in mozIStorageStatementCallback aCallback
+  );
+
+  /**
    * The current state.  Row getters are only valid while
    * the statement is in the "executing" state.
    */
   const long MOZ_STORAGE_STATEMENT_INVALID = 0;
   const long MOZ_STORAGE_STATEMENT_READY = 1;
   const long MOZ_STORAGE_STATEMENT_EXECUTING = 2;
 
   readonly attribute long state;
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageStatementCallback.idl
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab 
+ * ***** 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) 2008
+ * 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 "nsISupports.idl"
+
+interface mozIStorageResultSet;
+interface mozIStorageError;
+
+[scriptable, uuid(29383d00-d8c4-4ddd-9f8b-c2feb0f2fcfa)]
+interface mozIStorageStatementCallback : nsISupports {
+
+  /**
+   * Called when some result is obtained from the database.  This function can
+   * be called more than once with a different storageIResultSet each time for
+   * any given asynchronous statement.
+   *
+   * @param aResultSet
+   *        The result set containing the data from the database.
+   */
+  void handleResult(in mozIStorageResultSet aResultSet);
+
+  /**
+   * Called when some error occurs while executing the statement.  This function
+   * may be called more than once with a different storageIError each time for
+   * any given asynchronous statement.
+   *
+   * @param aError
+   *        An object containing information about the error.
+   */
+  void handleError(in mozIStorageError aError);
+
+  /**
+   * Called when the statement has finished executing.  This function will only
+   * be called once for any given asynchronous statement.
+   *
+   * @param aReason
+   *        Indicates if the statement is no longer executing because it either
+   *        finished (REASON_FINISHED), was canceled (REASON_CANCELED), or
+   *        a fatal error occurred (REASON_ERROR).
+   */
+  const unsigned short REASON_FINISHED = 0;
+  const unsigned short REASON_CANCELED = 1;
+  const unsigned short REASON_ERROR = 2;
+  void handleCompletion(in unsigned short aReason);
+};
--- a/storage/src/Makefile.in
+++ b/storage/src/Makefile.in
@@ -53,27 +53,31 @@ FORCE_STATIC_LIB = 1
 GRE_MODULE       = 1
 LIBXUL_LIBRARY = 1
 
 REQUIRES = xpcom \
            string \
            sqlite3 \
            js \
            xpconnect \
+           necko \
            $(NULL)
 
 CPPSRCS = \
 	mozStorageService.cpp \
 	mozStorageConnection.cpp \
 	mozStorageStatement.cpp \
 	mozStorageStatementWrapper.cpp \
 	mozStorageValueArray.cpp \
 	mozStorageUnicodeFunctions.cpp \
 	mozStorageRow.cpp \
 	mozStorageResultSet.cpp \
+  mozStorageError.cpp \
+  mozStorageBackground.cpp \
+  mozStorageEvents.cpp \
 	$(NULL)
 
 LOCAL_INCLUDES = \
 	$(SQLITE_CFLAGS)
 
 # This is the default value.  If we ever change it when compiling sqlite, we
 # will need to change it here as well.
 DEFINES += -DSQLITE_MAX_LIKE_PATTERN_LENGTH=50000
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBackground.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * ***** 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) 2008
+ * 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 "nsAutoLock.h"
+#include "nsIFile.h"
+#include "nsIThreadPool.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+
+#include "mozStorageCID.h"
+#include "mozIStorageService.h"
+#include "mozStorageConnection.h"
+#include "mozStorageBackground.h"
+
+namespace {
+  class ThreadShutdownObserver : public nsIObserver
+  {
+  public:
+    NS_DECL_ISUPPORTS
+
+    ThreadShutdownObserver(nsIThreadPool *aThreadPool) :
+      mThreadPool(aThreadPool)
+    {
+    }
+
+    NS_IMETHOD Observe(nsISupports *, const char *aTopic, const PRUnichar *)
+    {
+      if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
+        (void)mThreadPool->Shutdown();
+        mThreadPool = nsnull;
+      }
+      return NS_OK;
+    }
+  private:
+    ThreadShutdownObserver() { }
+    nsCOMPtr<nsIThreadPool> mThreadPool;
+  };
+  NS_IMPL_ISUPPORTS1(ThreadShutdownObserver, nsIObserver)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Public Methods
+
+mozStorageBackground *mozStorageBackground::mSingleton = nsnull;
+
+mozStorageBackground *
+mozStorageBackground::getService()
+{
+  return mozStorageBackground::mSingleton;
+}
+
+mozStorageBackground::mozStorageBackground()
+{
+  mozStorageBackground::mSingleton = this;
+}
+
+mozStorageBackground::~mozStorageBackground()
+{
+  (void)mThreadPool->Shutdown();
+  mozStorageBackground::mSingleton = nsnull;
+}
+
+nsIEventTarget *
+mozStorageBackground::target()
+{
+  return mThreadPool;
+}
+
+nsresult
+mozStorageBackground::initialize()
+{
+  // Create the thread pool
+  mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+  NS_ENSURE_TRUE(mThreadPool, NS_ERROR_OUT_OF_MEMORY);
+
+  // Create the observer, and register it with the observer service
+  mObserver = new ThreadShutdownObserver(mThreadPool);
+  NS_ENSURE_TRUE(mObserver, NS_ERROR_OUT_OF_MEMORY);
+
+  nsCOMPtr<nsIObserverService> os =
+    do_GetService("@mozilla.org/observer-service;1");
+  NS_ENSURE_TRUE(os, NS_ERROR_UNEXPECTED);
+
+  nsresult rv = os->AddObserver(mObserver, "xpcom-shutdown-threads", PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBackground.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * ***** 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) 2008
+ * 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 _mozStorageBackground_h_
+#define _mozStorageBackground_h_
+
+#include "nsClassHashtable.h"
+class mozStorageConnection;
+class nsIThreadPool;
+class nsIEventTarget;
+class nsIObserver;
+
+/**
+ * This class managed the connections used in the background for
+ * asynchronous operations.  There is a one-to-one mapping of calling thread
+ * connections to background ones.  Additionally, it manages the background
+ * thread pool used for asynchronous database calls.
+ *
+ * @note This class is threadsafe.
+ */
+class mozStorageBackground
+{
+public:
+
+  /**
+   * @returns the background event target that all events to be ran on the
+   *          background should be dispatched to.
+   */
+  nsIEventTarget *target();
+
+  /**
+   * Initializes this object.  Creates the background thread pool.
+   */
+  nsresult initialize();
+
+  /**
+   * Obtains a singleton service of this class.
+   *
+   * @returns a mozStorageBackground object.
+   */
+  static mozStorageBackground *getService();
+
+  mozStorageBackground();
+  ~mozStorageBackground();
+
+private:
+
+  nsCOMPtr<nsIThreadPool> mThreadPool;
+
+  static mozStorageBackground *mSingleton;
+
+  nsCOMPtr<nsIObserver> mObserver;
+};
+
+#endif // _mozStorageBackground_h_
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageError.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * ***** 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) 2008
+ * 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 "mozStorageError.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//// mozStorageError
+
+/**
+ * Note:  This object is only ever accessed on one thread at a time.  It it not
+ *        threadsafe, but it does need threadsafe AddRef and Release.
+ */
+NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageError, mozIStorageError)
+
+mozStorageError::mozStorageError(int aResult, const char *aMessage) :
+    mResult(aResult)
+  , mMessage(aMessage)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// mozIStorageError
+
+NS_IMETHODIMP
+mozStorageError::GetResult(PRInt32 *_result)
+{
+  *_result = mResult;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+mozStorageError::GetMessage(nsACString &_message)
+{
+  _message = mMessage;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageError.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * ***** 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) 2008
+ * 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 __mozStorageError_h__
+#define __mozStorageError_h__
+
+#include "mozIStorageError.h"
+#include "nsString.h"
+
+class mozStorageError : public mozIStorageError
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEERROR
+
+  mozStorageError(int aResult, const char *aMessage);
+
+private:
+  int mResult;
+  nsCString mMessage;
+};
+
+#endif // __mozStorageError_h__
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageEvents.cpp
@@ -0,0 +1,545 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * ***** 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) 2008
+ * 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 "nsThreadUtils.h"
+#include "nsAutoPtr.h"
+#include "nsAutoLock.h"
+#include "nsCOMArray.h"
+
+#include "mozIStorageStatementCallback.h"
+#include "mozIStoragePendingStatement.h"
+#include "mozStorageStatement.h"
+#include "mozStorageResultSet.h"
+#include "mozStorageRow.h"
+#include "mozStorageBackground.h"
+#include "mozStorageError.h"
+#include "mozStorageEvents.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//// Asynchronous Statement Execution
+
+/**
+ * Enum used to describe the state of execution.
+ */
+enum ExecutionState {
+    PENDING = -1
+  , COMPLETED = mozIStorageStatementCallback::REASON_FINISHED
+  , CANCELED = mozIStorageStatementCallback::REASON_CANCELED
+  , ERROR = mozIStorageStatementCallback::REASON_ERROR
+};
+
+/**
+ * Interface used to cancel pending events.
+ */
+class iCancelable : public nsISupports
+{
+public:
+  /**
+   * Tells an event to cancel itself.
+   */
+  virtual void cancel() = 0;
+};
+
+/**
+ * Interface used to notify of event completion.
+ */
+class iCompletionNotifier : public nsISupports
+{
+public:
+  /**
+   * Called when an event is completed and no longer needs to be tracked.
+   *
+   * @param aEvent
+   *        The event that has finished.
+   */
+  virtual void completed(iCancelable *aEvent) = 0;
+};
+
+/**
+ * Notifies a callback with a result set.
+ */
+class CallbackResultNotifier : public nsIRunnable
+                             , public iCancelable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  CallbackResultNotifier(mozIStorageStatementCallback *aCallback,
+                         mozIStorageResultSet *aResults,
+                         iCompletionNotifier *aNotifier) :
+      mCallback(aCallback)
+    , mResults(aResults)
+    , mCompletionNotifier(aNotifier)
+    , mCanceled(PR_FALSE)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mCanceled)
+      (void)mCallback->HandleResult(mResults);
+
+    // Notify owner AsyncExecute that we have completed
+    mCompletionNotifier->completed(this);
+    // It is likely that the completion notifier holds a reference to us as
+    // well, so we release our reference to it here to avoid cycles.
+    mCompletionNotifier = nsnull;
+    return NS_OK;
+  }
+
+  virtual void cancel()
+  {
+    // Atomically set our status so we know to not run.
+    PR_AtomicSet(&mCanceled, PR_TRUE);
+  }
+private:
+  CallbackResultNotifier() { }
+
+  mozIStorageStatementCallback *mCallback;
+  nsCOMPtr<mozIStorageResultSet> mResults;
+  nsRefPtr<iCompletionNotifier> mCompletionNotifier;
+  PRInt32 mCanceled;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+  CallbackResultNotifier,
+  nsIRunnable
+)
+
+/**
+ * Notifies the calling thread that an error has occurred.
+ */
+class ErrorNotifier : public nsIRunnable
+                    , public iCancelable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  ErrorNotifier(mozIStorageStatementCallback *aCallback,
+                mozIStorageError *aErrorObj,
+                iCompletionNotifier *aCompletionNotifier) :
+      mCallback(aCallback)
+    , mErrorObj(aErrorObj)
+    , mCanceled(PR_FALSE)
+    , mCompletionNotifier(aCompletionNotifier)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mCanceled)
+      (void)mCallback->HandleError(mErrorObj);
+
+    mCompletionNotifier->completed(this);
+    // It is likely that the completion notifier holds a reference to us as
+    // well, so we release our reference to it here to avoid cycles.
+    mCompletionNotifier = nsnull;
+    return NS_OK;
+  }
+
+  virtual void cancel()
+  {
+    // Atomically set our status so we know to not run.
+    PR_AtomicSet(&mCanceled, PR_TRUE);
+  }
+
+  static inline iCancelable *Dispatch(nsIThread *aCallingThread,
+                                      mozIStorageStatementCallback *aCallback,
+                                      iCompletionNotifier *aCompletionNotifier,
+                                      int aResult,
+                                      const char *aMessage)
+  {
+    nsCOMPtr<mozIStorageError> errorObj(new mozStorageError(aResult, aMessage));
+    if (!errorObj)
+      return nsnull;
+
+    ErrorNotifier *notifier = 
+      new ErrorNotifier(aCallback, errorObj, aCompletionNotifier);
+    (void)aCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
+    return notifier;
+  }
+private:
+  ErrorNotifier() { }
+
+  mozIStorageStatementCallback *mCallback;
+  nsCOMPtr<mozIStorageError> mErrorObj;
+  PRInt32 mCanceled;
+  nsRefPtr<iCompletionNotifier> mCompletionNotifier;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+  ErrorNotifier,
+  nsIRunnable
+)
+
+/**
+ * Notifies the calling thread that the statement has finished executing.
+ */
+class CompletionNotifier : public nsIRunnable
+                         , public iCancelable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  /**
+   * This takes ownership of the callback and statement.  Both are released
+   * on the thread this is dispatched to (which should always be the calling
+   * thread).
+   */
+  CompletionNotifier(mozIStorageStatementCallback *aCallback,
+                     ExecutionState aReason,
+                     mozIStorageStatement *aStatement,
+                     iCompletionNotifier *aCompletionNotifier) :
+      mCallback(aCallback)
+    , mReason(aReason)
+    , mStatement(aStatement)
+    , mCompletionNotifier(aCompletionNotifier)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    NS_RELEASE(mStatement);
+    if (mCallback) {
+      (void)mCallback->HandleCompletion(mReason);
+      NS_RELEASE(mCallback);
+    }
+
+    mCompletionNotifier->completed(this);
+    // It is likely that the completion notifier holds a reference to us as
+    // well, so we release our reference to it here to avoid cycles.
+    mCompletionNotifier = nsnull;
+    return NS_OK;
+  }
+
+  virtual void cancel()
+  {
+    // Update our reason so the completion notifier knows what is up.
+    mReason = CANCELED;
+  }
+
+private:
+  CompletionNotifier() { }
+
+  mozIStorageStatementCallback *mCallback;
+  ExecutionState mReason;
+  mozIStorageStatement *mStatement;
+  nsRefPtr<iCompletionNotifier> mCompletionNotifier;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+  CompletionNotifier,
+  nsIRunnable
+)
+
+/**
+ * Executes a statement asynchronously in the background.
+ */
+class AsyncExecute : public nsIRunnable
+                   , public mozIStoragePendingStatement
+                   , public iCompletionNotifier
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  /**
+   * This takes ownership of both the statement and the callback.
+   */
+  AsyncExecute(mozStorageStatement *aStatement,
+               mozIStorageStatementCallback *aCallback) :
+      mStatement(aStatement)
+    , mCallback(aCallback)
+    , mCallingThread(do_GetCurrentThread())
+    , mState(PENDING)
+    , mStateMutex(nsAutoLock::NewLock("AsyncExecute::mStateMutex"))
+    , mPendingEventsMutex(nsAutoLock::NewLock("AsyncExecute::mPendingEventsMutex"))
+  {
+  }
+
+  nsresult initialize()
+  {
+    NS_ENSURE_TRUE(mStateMutex, NS_ERROR_OUT_OF_MEMORY);
+    NS_ENSURE_TRUE(mPendingEventsMutex, NS_ERROR_OUT_OF_MEMORY);
+    NS_ADDREF(mStatement);
+    NS_IF_ADDREF(mCallback);
+    return NS_OK;
+  }
+
+  NS_IMETHOD Run()
+  {
+    // do not run if we have been canceled
+    {
+      nsAutoLock mutex(mStateMutex);
+      if (mState == CANCELED)
+        return Complete();
+    }
+
+    // Execute the statement, giving the callback results
+    // XXX better chunking of results?
+    nsresult rv;
+    while (PR_TRUE) {
+      PRBool hasResults;
+      rv = mStatement->ExecuteStep(&hasResults);
+      // Break out if we have no more results
+      if (NS_SUCCEEDED(rv) && !hasResults)
+        break;
+
+      // Some errors are not fatal, but we still need to report them
+      if (NS_FAILED(rv)) {
+        // Get the real result code
+        sqlite3 *db = sqlite3_db_handle(mStatement->NativeStatement());
+        int err = sqlite3_errcode(db);
+        if (err == SQLITE_BUSY) {
+          // Yield, and try again
+          PR_Sleep(PR_INTERVAL_NO_WAIT);
+          continue;
+        }
+
+        // Set error state
+        {
+          nsAutoLock mutex(mStateMutex);
+          mState = ERROR;
+        }
+
+        // Notify
+        iCancelable *cancelable = ErrorNotifier::Dispatch(
+          mCallingThread, mCallback, this, err, sqlite3_errmsg(db)
+        );
+        if (cancelable) {
+          nsAutoLock mutex(mPendingEventsMutex);
+          (void)mPendingEvents.AppendObject(cancelable);
+        }
+
+        // And complete
+        return Complete();
+      }
+
+      // Check to see if we have been canceled
+      {
+        nsAutoLock mutex(mStateMutex);
+        if (mState == CANCELED)
+          return Complete();
+      }
+
+      // If we do not have a callback, but are getting results, we should stop
+      // now since all this work isn't going to accomplish anything
+      if (!mCallback) {
+        nsAutoLock mutex(mStateMutex);
+        mState = COMPLETED;
+        return Complete();
+      }
+
+      // Build result object
+      nsRefPtr<mozStorageResultSet> results(new mozStorageResultSet());
+      if (!results)
+        break;
+
+      nsRefPtr<mozStorageRow> row(new mozStorageRow());
+      if (!row)
+        break;
+
+      rv = row->initialize(mStatement->NativeStatement());
+      if (NS_FAILED(rv))
+        break;
+
+      rv = results->add(row);
+      if (NS_FAILED(rv))
+        break;
+
+      // Notify caller
+      nsRefPtr<CallbackResultNotifier> notifier =
+        new CallbackResultNotifier(mCallback, results, this);
+      if (!notifier)
+        break;
+      
+      nsresult status = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
+      if (NS_SUCCEEDED(status)) {
+        nsAutoLock mutex(mPendingEventsMutex);
+        (void)mPendingEvents.AppendObject(notifier);
+      }
+    }
+    if (NS_FAILED(rv)) {
+      // This is a fatal error :(
+
+      // Update state
+      {
+        nsAutoLock mutex(mStateMutex);
+        mState = ERROR;
+      }
+
+      // Notify
+      iCancelable *cancelable = ErrorNotifier::Dispatch(
+        mCallingThread, mCallback, this, mozIStorageError::ERROR, ""
+      );
+      if (cancelable) {
+        nsAutoLock mutex(mPendingEventsMutex);
+        (void)mPendingEvents.AppendObject(cancelable);
+      }
+    }
+
+    // No more results, so update state if needed
+    {
+      nsAutoLock mutex(mStateMutex);
+      if (mState == PENDING)
+        mState = COMPLETED;
+
+      // Notify about completion
+      return Complete();
+    }
+  }
+
+  static PRBool cancelEnumerator(iCancelable *aCancelable, void *)
+  {
+    (void)aCancelable->cancel();
+    return PR_TRUE;
+  }
+
+  NS_IMETHOD Cancel()
+  {
+    // Check and update our state
+    {
+      nsAutoLock mutex(mStateMutex);
+      NS_ENSURE_TRUE(mState == PENDING || mState == COMPLETED,
+                     NS_ERROR_UNEXPECTED);
+      mState = CANCELED;
+    }
+
+    // Cancel all our pending events on the calling thread
+    {
+      nsAutoLock mutex(mPendingEventsMutex);
+      (void)mPendingEvents.EnumerateForwards(&AsyncExecute::cancelEnumerator,
+                                             nsnull);
+      mPendingEvents.Clear();
+    }
+
+    return NS_OK;
+  }
+
+  virtual void completed(iCancelable *aCancelable)
+  {
+    nsAutoLock mutex(mPendingEventsMutex);
+    (void)mPendingEvents.RemoveObject(aCancelable);
+  }
+
+private:
+  AsyncExecute() { }
+
+  ~AsyncExecute()
+  {
+    NS_ASSERTION(mPendingEvents.Count() == 0, "Still pending events!");
+    nsAutoLock::DestroyLock(mStateMutex);
+    nsAutoLock::DestroyLock(mPendingEventsMutex);
+  }
+
+  /**
+   * Notifies callback about completion, and does any necessary cleanup.
+   * @note: When calling this function, mStateMutex must be held.
+   */
+  nsresult Complete()
+  {
+    // Reset the statement
+    (void)mStatement->Reset();
+
+    // Notify about completion
+    NS_ASSERTION(mState != PENDING,
+                 "Still in a pending state when calling Complete!");
+    nsRefPtr<CompletionNotifier> completionEvent =
+      new CompletionNotifier(mCallback, mState, mStatement, this);
+    nsresult rv = mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoLock mutex(mPendingEventsMutex);
+      (void)mPendingEvents.AppendObject(completionEvent);
+    }
+
+    // We no longer own mCallback or mStatement (the CompletionNotifier takes
+    // ownership), so null them out
+    mCallback = nsnull;
+    mStatement = nsnull;
+    return NS_OK;
+  }
+
+  mozStorageStatement *mStatement;
+  mozIStorageStatementCallback *mCallback;
+  nsCOMPtr<nsIThread> mCallingThread;
+
+  /**
+   * Indicates the state the object is currently in.
+   */
+  ExecutionState mState;
+
+  /**
+   * Mutex to protect mState.
+   */
+  PRLock *mStateMutex;
+
+  /**
+   * Stores a list of pending events that have not yet completed on the
+   * calling thread.
+   */
+  nsCOMArray<iCancelable> mPendingEvents;
+
+  /**
+   * Mutex to protect mPendingEvents.
+   */
+  PRLock *mPendingEventsMutex;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS2(
+  AsyncExecute,
+  nsIRunnable,
+  mozIStoragePendingStatement
+)
+
+nsresult
+NS_executeAsync(mozStorageStatement *aStatement,
+                mozIStorageStatementCallback *aCallback,
+                mozIStoragePendingStatement **_stmt)
+{
+  // Create our event to run in the background
+  nsRefPtr<AsyncExecute> event(new AsyncExecute(aStatement, aCallback));
+  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = event->initialize();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Dispatch it to the background
+  nsIEventTarget *target = mozStorageBackground::getService()->target();
+  rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Return it as the pending statement object
+  NS_ADDREF(*_stmt = event);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageEvents.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * ***** 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) 2008
+ * 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 _mozStorageEvents_h_
+#define _mozStorageEvents_h_
+
+#include "nscore.h"
+#include "mozStorageBackground.h"
+class mozStorageStatement;
+class mozIStorageStatementCallback;
+class mozIStoragePendingStatement;
+
+/**
+ * Executes a statement in the background, and passes results back to the
+ * caller.
+ *
+ * @param aStatement
+ *        The statement to execute in the background.
+ * @param aCallback
+ *        The callback that is notified of results, completion, and errors.
+ * @param _stmt
+ *        The handle to control the execution of the statement.
+ */
+nsresult NS_executeAsync(
+  mozStorageStatement *aStatement,
+  mozIStorageStatementCallback *aCallback,
+  mozIStoragePendingStatement **_stmt
+);
+
+#endif // _mozStorageEvents_h_
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -116,16 +116,19 @@ mozStorageService::~mozStorageService()
 
 nsresult
 mozStorageService::Init()
 {
     mLock = PR_NewLock();
     if (!mLock)
         return NS_ERROR_OUT_OF_MEMORY;
 
+    nsresult rv = mBackground.initialize();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // This makes multiple connections to the same database share the same pager
     // cache.  We do not need to lock here with mLock because this function is
     // only ever called from mozStorageService::GetSingleton, which will only
     // call this function once, and will not return until this function returns.
     int rc = sqlite3_enable_shared_cache(1);
     if (rc != SQLITE_OK)
         return ConvertResultCode(rc);
 
--- a/storage/src/mozStorageService.h
+++ b/storage/src/mozStorageService.h
@@ -44,16 +44,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "prlock.h"
 
 #include "mozIStorageService.h"
+#include "mozStorageBackground.h"
 
 class mozStorageConnection;
 
 class mozStorageService : public mozIStorageService
 {
     friend class mozStorageConnection;
 
 public:
@@ -71,15 +72,20 @@ public:
 private:
     virtual ~mozStorageService();
 
     /**
      * Used for locking around calls when initializing connections so that we
      * can ensure that the state of sqlite3_enable_shared_cache is sane.
      */
     PRLock *mLock;
+
+    /**
+     * The background service needs to stay around just as long as this does.
+     */
+    mozStorageBackground mBackground;
 protected:
     nsCOMPtr<nsIFile> mProfileStorageFile;
 
     static mozStorageService *gStorageService;
 };
 
 #endif /* _MOZSTORAGESERVICE_H_ */
--- a/storage/src/mozStorageStatement.cpp
+++ b/storage/src/mozStorageStatement.cpp
@@ -36,24 +36,26 @@
  * 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 <stdio.h>
 
+#include "nsAutoLock.h"
 #include "nsError.h"
 #include "nsISimpleEnumerator.h"
 #include "nsMemory.h"
 
 #include "mozStorageConnection.h"
 #include "mozStorageStatement.h"
 #include "mozStorageValueArray.h"
 #include "mozStorage.h"
+#include "mozStorageEvents.h"
 
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gStorageLog;
 #endif
 
 /**
@@ -451,18 +453,16 @@ mozStorageStatement::Execute()
 
 /* boolean executeStep (); */
 NS_IMETHODIMP
 mozStorageStatement::ExecuteStep(PRBool *_retval)
 {
     if (!mDBConnection || !mDBStatement)
         return NS_ERROR_NOT_INITIALIZED;
 
-    nsresult rv;
-
     int srv = sqlite3_step (mDBStatement);
 
 #ifdef PR_LOGGING
     if (srv != SQLITE_ROW && srv != SQLITE_DONE)
     {
         nsCAutoString errStr;
         mDBConnection->GetLastErrorString(errStr);
         PR_LOG(gStorageLog, PR_LOG_DEBUG, ("mozStorageStatement::ExecuteStep error: %s", errStr.get()));
@@ -488,16 +488,45 @@ mozStorageStatement::ExecuteStep(PRBool 
         PR_LOG(gStorageLog, PR_LOG_ERROR, ("SQLite error after mExecuting was true!"));
 #endif
         mExecuting = PR_FALSE;
     }
 
     return ConvertResultCode(srv);
 }
 
+/* nsICancelable executeAsync([optional] in storageIStatementCallback aCallback); */
+nsresult
+mozStorageStatement::ExecuteAsync(mozIStorageStatementCallback *aCallback,
+                                  mozIStoragePendingStatement **_stmt)
+{
+    // Clone this statement
+    nsRefPtr<mozStorageStatement> stmt(new mozStorageStatement());
+    NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
+
+    nsCAutoString sql(sqlite3_sql(mDBStatement));
+    nsresult rv = stmt->Initialize(mDBConnection, sql);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Transfer the bindings
+    int rc = sqlite3_transfer_bindings(mDBStatement, stmt->mDBStatement);
+    if (rc != SQLITE_OK)
+        return ConvertResultCode(rc);
+
+    // Dispatch to the background.
+    rv = NS_executeAsync(stmt, aCallback, _stmt);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Reset this statement.
+    rv = Reset();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
 /* [noscript,notxpcom] sqlite3stmtptr getNativeStatementPointer(); */
 sqlite3_stmt*
 mozStorageStatement::GetNativeStatementPointer()
 {
     return mDBStatement;
 }
 
 /* readonly attribute long state; */
--- a/storage/src/mozStorageStatement.h
+++ b/storage/src/mozStorageStatement.h
@@ -43,16 +43,18 @@
 #include "nsString.h"
 
 #include "nsVoidArray.h"
 
 #include "mozIStorageStatement.h"
 
 #include <sqlite3.h>
 
+class mozStorageConnection;
+
 class mozStorageStatement : public mozIStorageStatement
 {
 public:
     mozStorageStatement();
 
     // interfaces
     NS_DECL_ISUPPORTS
     NS_DECL_MOZISTORAGESTATEMENT
@@ -65,16 +67,22 @@ public:
      * @param aDBConnection
      *        The mozStorageConnection object this statement is associated with.
      * @param aSQLStatement
      *        The SQL statement to prepare that this object will represent.
      */
     nsresult Initialize(mozStorageConnection *aDBConnection,
                         const nsACString &aSQLStatement);
 
+
+    /**
+     * Obtains the native statement pointer.
+     */
+    inline sqlite3_stmt *NativeStatement() { return mDBStatement; }
+
 private:
     ~mozStorageStatement();
 
 protected:
     nsRefPtr<mozStorageConnection> mDBConnection;
     sqlite3_stmt *mDBStatement;
     PRUint32 mParamCount;
     PRUint32 mResultColumnCount;