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
This commit is contained in:
Shawn Wilsher 2008-07-11 15:47:33 -04:00
Родитель 3da654e7c2
Коммит 35ba2ed351
16 изменённых файлов: 1333 добавлений и 5 удалений

Просмотреть файл

@ -60,6 +60,9 @@ XPIDLSRCS = \
mozIStorageValueArray.idl \
mozIStorageResultSet.idl \
mozIStorageRow.idl \
mozIStorageError.idl \
mozIStorageStatementCallback.idl \
mozIStoragePendingStatement.idl \
$(NULL)
EXPORTS = \

Просмотреть файл

@ -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;
};

Просмотреть файл

@ -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();
};

Просмотреть файл

@ -1,5 +1,6 @@
/* -*- 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
@ -42,10 +43,12 @@
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.
@ -149,6 +152,19 @@ interface mozIStorageStatement : mozIStorageValueArray {
*/
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.

Просмотреть файл

@ -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);
};

Просмотреть файл

@ -58,6 +58,7 @@ REQUIRES = xpcom \
sqlite3 \
js \
xpconnect \
necko \
$(NULL)
CPPSRCS = \
@ -69,6 +70,9 @@ CPPSRCS = \
mozStorageUnicodeFunctions.cpp \
mozStorageRow.cpp \
mozStorageResultSet.cpp \
mozStorageError.cpp \
mozStorageBackground.cpp \
mozStorageEvents.cpp \
$(NULL)
LOCAL_INCLUDES = \

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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_

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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__

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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_

Просмотреть файл

@ -121,6 +121,9 @@ mozStorageService::Init()
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

Просмотреть файл

@ -49,6 +49,7 @@
#include "prlock.h"
#include "mozIStorageService.h"
#include "mozStorageBackground.h"
class mozStorageConnection;
@ -76,6 +77,11 @@ private:
* 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;

Просмотреть файл

@ -41,6 +41,7 @@
#include <stdio.h>
#include "nsAutoLock.h"
#include "nsError.h"
#include "nsISimpleEnumerator.h"
#include "nsMemory.h"
@ -49,6 +50,7 @@
#include "mozStorageStatement.h"
#include "mozStorageValueArray.h"
#include "mozStorage.h"
#include "mozStorageEvents.h"
#include "prlog.h"
@ -456,8 +458,6 @@ mozStorageStatement::ExecuteStep(PRBool *_retval)
if (!mDBConnection || !mDBStatement)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv;
int srv = sqlite3_step (mDBStatement);
#ifdef PR_LOGGING
@ -493,6 +493,35 @@ mozStorageStatement::ExecuteStep(PRBool *_retval)
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()

Просмотреть файл

@ -48,6 +48,8 @@
#include <sqlite3.h>
class mozStorageConnection;
class mozStorageStatement : public mozIStorageStatement
{
public:
@ -70,6 +72,12 @@ public:
nsresult Initialize(mozStorageConnection *aDBConnection,
const nsACString &aSQLStatement);
/**
* Obtains the native statement pointer.
*/
inline sqlite3_stmt *NativeStatement() { return mDBStatement; }
private:
~mozStorageStatement();