Bug 460635 - There should be one async execution thread per mozStorageConnection connection; v1.2; r=(bugmail + sdwilsh)
authorAndrew Sutherland <bugmail@asutherland.org>
Wed, 29 Oct 2008 04:53:19 +0100
changeset 21035 ed7f8c9bd8194701e9fe8db8c3bb337204c1d40a
parent 21034 5373680e5d49f189960bf521b629934e6b3b229a
child 21036 0dd2cc785f35a4bca6487ae3fd1a3e5c0288b8d9
push idunknown
push userunknown
push dateunknown
bugs460635
milestone1.9.1b2pre
Bug 460635 - There should be one async execution thread per mozStorageConnection connection; v1.2; r=(bugmail + sdwilsh)
storage/src/mozStorageBackground.cpp
storage/src/mozStorageBackground.h
storage/src/mozStorageConnection.cpp
storage/src/mozStorageConnection.h
storage/src/mozStorageEvents.cpp
storage/src/mozStorageEvents.h
storage/src/mozStorageService.cpp
storage/src/mozStorageService.h
deleted file mode 100644
--- a/storage/src/mozStorageBackground.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/* -*- 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 "nsIThreadPool.h"
-#include "nsXPCOMCIDInternal.h"
-#include "nsIObserver.h"
-#include "nsIObserverService.h"
-#include "nsServiceManagerUtils.h"
-
-#include "mozStorageCID.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;
-}
deleted file mode 100644
--- a/storage/src/mozStorageBackground.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- 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 _mozStorageBackground_h_
-#define _mozStorageBackground_h_
-
-#include "nsClassHashtable.h"
-class mozStorageConnection;
-class nsIThreadPool;
-class nsIEventTarget;
-class nsIObserver;
-
-/**
- * Provides an event target to dispatch background events to for storage.
- *
- * @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_
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -45,16 +45,17 @@
 #include "nsError.h"
 #include "nsIMutableArray.h"
 #include "nsHashSets.h"
 #include "nsAutoPtr.h"
 #include "nsIFile.h"
 #include "nsIVariant.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsThreadUtils.h"
 
 #include "mozIStorageAggregateFunction.h"
 #include "mozIStorageFunction.h"
 
 #include "mozStorageEvents.h"
 #include "mozStorageUnicodeFunctions.h"
 #include "mozStorageConnection.h"
 #include "mozStorageService.h"
@@ -70,16 +71,17 @@ PRLogModuleInfo* gStorageLog = nsnull;
 #endif
 
 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
 
 mozStorageConnection::mozStorageConnection(mozIStorageService* aService) :
     mDBConn(nsnull)
+,   mAsyncExecutionMutex(nsAutoLock::NewLock("AsyncExecutionMutex"))
 ,   mTransactionMutex(nsAutoLock::NewLock("TransactionMutex"))
 ,   mTransactionInProgress(PR_FALSE)
 ,   mFunctionsMutex(nsAutoLock::NewLock("FunctionsMutex"))
 ,   mProgressHandlerMutex(nsAutoLock::NewLock("ProgressHandlerMutex"))
 ,   mProgressHandler(nsnull)
 ,   mStorageService(aService)
 {
     mFunctions.Init();
@@ -221,16 +223,26 @@ mozStorageConnection::Close()
 #ifdef PR_LOGGING
     nsCAutoString leafName(":memory");
     if (mDatabaseFile)
         (void)mDatabaseFile->GetNativeLeafName(leafName);
     PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s'",
                                         leafName.get()));
 #endif
 
+    // The shutdown call runs any pending events to completion, so we want to
+    // do this before closing the connection.
+    {
+        nsAutoLock mutex(mAsyncExecutionMutex);
+        if (mAsyncExecutionThread) {
+            mAsyncExecutionThread->Shutdown();
+            mAsyncExecutionThread = nsnull;
+        }
+    }
+
     {
         nsAutoLock mutex(mProgressHandlerMutex);
         if (mProgressHandler)
             sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
     }
 
     int srv = sqlite3_close(mDBConn);
     if (srv != SQLITE_OK)
@@ -914,16 +926,35 @@ mozStorageConnection::ProgressHandler()
         return res ? 1 : 0;
     }
     return 0;
 }
 
 /**
  ** Other bits
  **/
+
+already_AddRefed<nsIEventTarget>
+mozStorageConnection::getAsyncExecutionTarget()
+{
+    nsAutoLock mutex(mAsyncExecutionMutex);
+
+    if (!mAsyncExecutionThread) {
+        nsresult rv = NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
+        if (NS_FAILED(rv)) {
+            NS_WARNING("Failed to create async thread.");
+            return nsnull;
+        }
+    }
+
+    nsIEventTarget *eventTarget;
+    NS_ADDREF(eventTarget = mAsyncExecutionThread);
+    return eventTarget;
+}
+
 void
 mozStorageConnection::HandleSqliteError(const char *aSqlStatement)
 {
     // an error just occured!
 #ifdef PR_LOGGING
     PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Sqlite error: %d '%s'", sqlite3_errcode(mDBConn), sqlite3_errmsg(mDBConn)));
     if (aSqlStatement)
         PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Statement was: %s", aSqlStatement));
--- a/storage/src/mozStorageConnection.h
+++ b/storage/src/mozStorageConnection.h
@@ -48,16 +48,18 @@
 #include "mozIStorageProgressHandler.h"
 #include "mozIStorageConnection.h"
 
 #include "nsIMutableArray.h"
 
 #include <sqlite3.h>
 
 class nsIFile;
+class nsIEventTarget;
+class nsIThread;
 class mozIStorageService;
 
 class mozStorageConnection : public mozIStorageConnection
 {
 public:
 
     mozStorageConnection(mozIStorageService* aService);
 
@@ -65,16 +67,25 @@ public:
 
     // interfaces
     NS_DECL_ISUPPORTS
     NS_DECL_MOZISTORAGECONNECTION
 
     // fetch the native handle
     sqlite3 *GetNativeConnection() { return mDBConn; }
 
+    /**
+     * Lazily creates and returns a background execution thread.  In the future,
+     * the thread may be re-claimed if left idle, so you should call this
+     * method just before you dispatch and not save the reference.
+     * 
+     * @returns an event target suitable for asynchronous statement execution.
+     */
+    already_AddRefed<nsIEventTarget> getAsyncExecutionTarget();
+
 private:
     ~mozStorageConnection();
 
 protected:
     struct FindFuncEnumArgs {
         nsISupports *mTarget;
         PRBool       mFound;
     };
@@ -88,16 +99,28 @@ protected:
     // Generic progress handler
     // Dispatch call to registered progress handler,
     // if there is one. Do nothing in other cases.
     int ProgressHandler();
 
     sqlite3 *mDBConn;
     nsCOMPtr<nsIFile> mDatabaseFile;
 
+    /**
+     * Protects access to mAsyncExecutionThread.
+     */
+    PRLock *mAsyncExecutionMutex;
+
+    /**
+     * Lazily created thread for asynchronous statement execution.  Consumers
+     * should use getAsyncExecutionTarget rather than directly accessing this
+     * field.
+     */
+    nsCOMPtr<nsIThread> mAsyncExecutionThread;
+
     PRLock *mTransactionMutex;
     PRBool mTransactionInProgress;
 
     PRLock *mFunctionsMutex;
     nsInterfaceHashtable<nsCStringHashKey, nsISupports> mFunctions;
 
     PRLock *mProgressHandlerMutex;
     nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
--- a/storage/src/mozStorageEvents.cpp
+++ b/storage/src/mozStorageEvents.cpp
@@ -44,17 +44,17 @@
 
 #include "sqlite3.h"
 
 #include "mozIStorageStatementCallback.h"
 #include "mozIStoragePendingStatement.h"
 #include "mozStorageHelper.h"
 #include "mozStorageResultSet.h"
 #include "mozStorageRow.h"
-#include "mozStorageBackground.h"
+#include "mozStorageConnection.h"
 #include "mozStorageError.h"
 #include "mozStorageEvents.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Asynchronous Statement Execution
 
 /**
  * Enum used to describe the state of execution.
@@ -527,28 +527,29 @@ private:
 NS_IMPL_THREADSAFE_ISUPPORTS2(
   AsyncExecute,
   nsIRunnable,
   mozIStoragePendingStatement
 )
 
 nsresult
 NS_executeAsync(nsTArray<sqlite3_stmt *> &aStatements,
-                mozIStorageConnection *aConnection,
+                mozStorageConnection *aConnection,
                 mozIStorageStatementCallback *aCallback,
                 mozIStoragePendingStatement **_stmt)
 {
   // Create our event to run in the background
   nsRefPtr<AsyncExecute> event(new AsyncExecute(aStatements, aConnection, 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();
+  nsCOMPtr<nsIEventTarget> target(aConnection->getAsyncExecutionTarget());
+  NS_ENSURE_TRUE(target, NS_ERROR_NOT_AVAILABLE);
   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;
 }
--- a/storage/src/mozStorageEvents.h
+++ b/storage/src/mozStorageEvents.h
@@ -37,18 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _mozStorageEvents_h_
 #define _mozStorageEvents_h_
 
 #include "nscore.h"
 #include "nsTArray.h"
-#include "mozStorageBackground.h"
 struct sqlite3_stmt;
+class mozStorageConnection;
 class mozIStorageStatementCallback;
 class mozIStoragePendingStatement;
 
 /**
  * Executes a statement in the background, and passes results back to the
  * caller.
  *
  * @param aStatements
@@ -57,14 +57,14 @@ class mozIStoragePendingStatement;
  *        The connection that created the statements to execute.
  * @param aCallback
  *        The callback that is notified of results, completion, and errors.
  * @param _stmt
  *        The handle to control the execution of the statements.
  */
 nsresult NS_executeAsync(
   nsTArray<sqlite3_stmt *> &aStatements,
-  mozIStorageConnection *aConnection,
+  mozStorageConnection *aConnection,
   mozIStorageStatementCallback *aCallback,
   mozIStoragePendingStatement **_stmt
 );
 
 #endif // _mozStorageEvents_h_
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -80,19 +80,16 @@ 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,17 +44,16 @@
 
 #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:
@@ -72,20 +71,15 @@ 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_ */