diff --git a/storage/public/Makefile.in b/storage/public/Makefile.in index 97b77b37f349..a612fa9842cb 100644 --- a/storage/public/Makefile.in +++ b/storage/public/Makefile.in @@ -60,6 +60,9 @@ XPIDLSRCS = \ mozIStorageValueArray.idl \ mozIStorageResultSet.idl \ mozIStorageRow.idl \ + mozIStorageError.idl \ + mozIStorageStatementCallback.idl \ + mozIStoragePendingStatement.idl \ $(NULL) EXPORTS = \ diff --git a/storage/public/mozIStorageError.idl b/storage/public/mozIStorageError.idl new file mode 100644 index 000000000000..b3bab0bd28fc --- /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 (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; +}; diff --git a/storage/public/mozIStoragePendingStatement.idl b/storage/public/mozIStoragePendingStatement.idl new file mode 100644 index 000000000000..cecf9d238a2a --- /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 (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(); +}; diff --git a/storage/public/mozIStorageStatement.idl b/storage/public/mozIStorageStatement.idl index 199875ec476d..74b567fa6789 100644 --- a/storage/public/mozIStorageStatement.idl +++ b/storage/public/mozIStorageStatement.idl @@ -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. diff --git a/storage/public/mozIStorageStatementCallback.idl b/storage/public/mozIStorageStatementCallback.idl new file mode 100644 index 000000000000..f4e33d269021 --- /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 (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); +}; diff --git a/storage/src/Makefile.in b/storage/src/Makefile.in index a35fda0fa647..64678a45d496 100644 --- a/storage/src/Makefile.in +++ b/storage/src/Makefile.in @@ -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 = \ diff --git a/storage/src/mozStorageBackground.cpp b/storage/src/mozStorageBackground.cpp new file mode 100644 index 000000000000..bb8863238dfc --- /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 (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 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 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; +} diff --git a/storage/src/mozStorageBackground.h b/storage/src/mozStorageBackground.h new file mode 100644 index 000000000000..51da936f0707 --- /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 (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 mThreadPool; + + static mozStorageBackground *mSingleton; + + nsCOMPtr mObserver; +}; + +#endif // _mozStorageBackground_h_ diff --git a/storage/src/mozStorageError.cpp b/storage/src/mozStorageError.cpp new file mode 100644 index 000000000000..80f2462b2300 --- /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 (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; +} diff --git a/storage/src/mozStorageError.h b/storage/src/mozStorageError.h new file mode 100644 index 000000000000..51febbb1e6fd --- /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 (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__ diff --git a/storage/src/mozStorageEvents.cpp b/storage/src/mozStorageEvents.cpp new file mode 100644 index 000000000000..a0a52eb58c48 --- /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 (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 mResults; + nsRefPtr 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 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 mErrorObj; + PRInt32 mCanceled; + nsRefPtr 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 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 results(new mozStorageResultSet()); + if (!results) + break; + + nsRefPtr 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 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 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 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 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 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; +} diff --git a/storage/src/mozStorageEvents.h b/storage/src/mozStorageEvents.h new file mode 100644 index 000000000000..3b4bead966b2 --- /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 (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_ diff --git a/storage/src/mozStorageService.cpp b/storage/src/mozStorageService.cpp index 8624619e44e9..c3ef24e418a7 100644 --- a/storage/src/mozStorageService.cpp +++ b/storage/src/mozStorageService.cpp @@ -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 diff --git a/storage/src/mozStorageService.h b/storage/src/mozStorageService.h index 5865df602eb1..2d0681390454 100644 --- a/storage/src/mozStorageService.h +++ b/storage/src/mozStorageService.h @@ -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 mProfileStorageFile; diff --git a/storage/src/mozStorageStatement.cpp b/storage/src/mozStorageStatement.cpp index 5a006f1f8ea6..c2e795b060a5 100644 --- a/storage/src/mozStorageStatement.cpp +++ b/storage/src/mozStorageStatement.cpp @@ -41,6 +41,7 @@ #include +#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 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() diff --git a/storage/src/mozStorageStatement.h b/storage/src/mozStorageStatement.h index ec5d42e9b308..6313e67afff1 100644 --- a/storage/src/mozStorageStatement.h +++ b/storage/src/mozStorageStatement.h @@ -48,6 +48,8 @@ #include +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();