Hook up threading, storage connection stuff.

This commit is contained in:
Ben Turner 2010-04-30 15:35:06 -07:00
parent 8962ae39bc
commit 2cfe2e3512
9 changed files with 750 additions and 608 deletions

View File

@ -0,0 +1,192 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "AsyncConnectionHelper.h"
#include "nsComponentManagerUtils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "IDBEvents.h"
USING_INDEXEDDB_NAMESPACE
AsyncConnectionHelper::AsyncConnectionHelper(
nsCOMPtr<mozIStorageConnection>& aConnection,
nsIDOMEventTarget* aTarget)
: mConnection(aConnection),
mTarget(aTarget),
mErrorCode(0),
mError(PR_FALSE)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTarget, "Null target!");
}
AsyncConnectionHelper::~AsyncConnectionHelper()
{
if (!NS_IsMainThread()) {
NS_ASSERTION(mErrorCode == NOREPLY,
"This should only happen if NOREPLY was returned!");
nsIDOMEventTarget* target;
mTarget.forget(&target);
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
if (mainThread) {
NS_ProxyRelease(mainThread, target);
}
else {
NS_WARNING("Couldn't get the main thread?! Leaking instead of crashing.");
}
}
NS_ASSERTION(!mTarget, "Should have been released before now!");
NS_ASSERTION(!mDatabaseThread, "Should have been released before now!");
}
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncConnectionHelper, nsIRunnable)
NS_IMETHODIMP
AsyncConnectionHelper::Run()
{
if (NS_IsMainThread()) {
if (mError) {
OnError(mTarget, mErrorCode);
}
else {
OnSuccess(mTarget);
}
mTarget = nsnull;
return NS_OK;
}
#ifdef DEBUG
{
PRBool ok;
NS_ASSERTION(NS_SUCCEEDED(mDatabaseThread->IsOnCurrentThread(&ok)) && ok,
"Wrong thread!");
mDatabaseThread = nsnull;
}
#endif
mErrorCode = DoDatabaseWork();
if (mErrorCode != NOREPLY) {
mError = mErrorCode != OK;
return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
}
return NS_OK;
}
nsresult
AsyncConnectionHelper::Dispatch(nsIThread* aDatabaseThread)
{
#ifdef DEBUG
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
{
PRBool sameThread;
nsresult rv = aDatabaseThread->IsOnCurrentThread(&sameThread);
NS_ASSERTION(NS_SUCCEEDED(rv), "IsOnCurrentThread failed!");
NS_ASSERTION(!sameThread, "Dispatching to main thread not supported!");
}
mDatabaseThread = aDatabaseThread;
#endif
return aDatabaseThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
void
AsyncConnectionHelper::OnSuccess(nsIDOMEventTarget* aTarget)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIWritableVariant> variant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
if (!variant) {
NS_ERROR("Couldn't create variant!");
return;
}
GetSuccessResult(variant);
if (NS_FAILED(variant->SetWritable(PR_FALSE))) {
NS_ERROR("Failed to make variant readonly!");
return;
}
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
if (!event) {
NS_ERROR("Failed to create event!");
return;
}
PRBool dummy;
aTarget->DispatchEvent(event, &dummy);
}
void
AsyncConnectionHelper::OnError(nsIDOMEventTarget* aTarget,
PRUint16 aErrorCode)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Make an error event and fire it at the target.
nsCOMPtr<nsIDOMEvent> event(IDBErrorEvent::Create(aErrorCode));
if (!event) {
NS_ERROR("Failed to create event!");
return;
}
PRBool dummy;
aTarget->DispatchEvent(event, &dummy);
}
void
AsyncConnectionHelper::GetSuccessResult(nsIWritableVariant* /* aResult */)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Leave the variant set to empty.
}

View File

@ -0,0 +1,139 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_indexeddb_asyncconnectionhelper_h__
#define mozilla_dom_indexeddb_asyncconnectionhelper_h__
// Only meant to be included in IndexedDB source files, not exported.
#include "IndexedDatabase.h"
#include "mozIStorageConnection.h"
#include "nsIDOMEventTarget.h"
#include "nsIRunnable.h"
#include "nsIThread.h"
#include "nsIVariant.h"
BEGIN_INDEXEDDB_NAMESPACE
/**
* Must be subclassed. The subclass must implement DoDatabaseWork. It may then
* choose to implement OnSuccess and OnError depending on the needs of the
* subclass. If the default implementation of OnSuccess is desired then the
* subclass can implement GetSuccessResult to properly set the result of the
* success event. Call Dispatch to start the database operation. Must be created
* and Dispatched from the main thread only. Target thread may not be the main
* thread.
*/
class AsyncConnectionHelper : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
/**
* Return this code from DoDatabaseWork to signal the main thread that the
* database operation suceeded. Once the main thread receives this
* notification the OnSuccess callback will be invoked allowing the subclass
* to fire a success event or otherwise note the completed operation.
*/
static const PRUint16 OK = PR_UINT16_MAX;
/**
* Return this code from DoDatabaseWork to prevent the helper from firing a
* response to the main thread. Note that you must hold an additional
* reference to the helper somewhere if further processing is needed or else
* the helper will be deleted after returning from DoDatabaseWork.
*/
static const PRUint16 NOREPLY = OK - 1;
nsresult Dispatch(nsIThread* aDatabaseThread);
protected:
AsyncConnectionHelper(nsCOMPtr<mozIStorageConnection>& aConnection,
nsIDOMEventTarget* aTarget);
virtual ~AsyncConnectionHelper();
/**
* This callback is run on the database thread. It should return a valid error
* code from nsIIDBDatabaseError or one of the two special values above.
*/
virtual PRUint16 DoDatabaseWork() = 0;
/**
* This callback is run on the main thread if the DoDatabaseWork returned OK.
* The default implementation constructs an IDBSuccessEvent with its result
* property set to the value returned by GetSuccessResult. The event is then
* fired at the target.
*/
virtual void OnSuccess(nsIDOMEventTarget* aTarget);
/**
* This callback is run on the main thread if DoDatabaseWork returned an error
* code. The default implementation constructs an IDBErrorEvent for the error
* code and fires it at the target.
*/
virtual void OnError(nsIDOMEventTarget* aTarget,
PRUint16 aErrorCode);
/**
* This function is called from the default implementation of OnSuccess. A
* valid nsIWritableVariant is passed into the function which can be modified
* by the subclass. The default implementation returns an nsIVariant that is
* set to nsIDataType::VTYPE_EMPTY.
*/
virtual void GetSuccessResult(nsIWritableVariant* aVariant);
protected:
nsCOMPtr<mozIStorageConnection>& mConnection;
private:
nsCOMPtr<nsIDOMEventTarget> mTarget;
#ifdef DEBUG
nsCOMPtr<nsIThread> mDatabaseThread;
#endif
PRUint16 mErrorCode;
PRPackedBool mError;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_asyncconnectionhelper_h__

View File

@ -1,527 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "AsyncDatabaseConnection.h"
#include "nsIIDBObjectStore.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIVariant.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsXPCOMCID.h"
#include "mozilla/Storage.h"
#include "IDBEvents.h"
#define DB_SCHEMA_VERSION 1
USING_INDEXEDDB_NAMESPACE
namespace {
PRUint32 gConnectionCount = 0;
nsIThread* gDatabaseThread = nsnull;
PRBool gXPCOMShutdown = PR_FALSE;
struct ObjectStore
{
ObjectStore() : autoIncrement(PR_FALSE), readerCount(0), writing(PR_FALSE) { }
nsString name;
nsString keyPath;
PRBool autoIncrement;
nsTArray<nsString> mIndexes;
PRUint32 readerCount;
PRBool writing;
};
nsTArray<ObjectStore>* gObjectStores = nsnull;
class ShutdownObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD
Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gXPCOMShutdown, "Already shutdown?!");
if (gDatabaseThread) {
nsresult rv = gDatabaseThread->Shutdown();
NS_RELEASE(gDatabaseThread);
NS_ENSURE_SUCCESS(rv, rv);
}
delete gObjectStores;
gObjectStores = nsnull;
gXPCOMShutdown = PR_TRUE;
return NS_OK;
}
};
already_AddRefed<nsIDOMEvent>
MakeSuccessEventBool(PRBool aResult)
{
nsCOMPtr<nsIWritableVariant> variant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
NS_ENSURE_TRUE(variant, nsnull);
nsresult rv = variant->SetAsBool(aResult);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = variant->SetWritable(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
return event.forget();
}
already_AddRefed<nsIDOMEvent>
MakeSuccessEvent(nsISupports* aResult)
{
nsCOMPtr<nsIWritableVariant> variant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
NS_ENSURE_TRUE(variant, nsnull);
nsresult rv = variant->SetAsISupports(aResult);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = variant->SetWritable(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<nsIDOMEvent> event(IDBSuccessEvent::Create(variant));
return event.forget();
}
class DatabaseWorkHelper : public nsRunnable
{
public:
static const PRUint16 OK = PR_UINT16_MAX;
DatabaseWorkHelper(nsIDOMEventTarget* aTarget)
: mTarget(aTarget), mError(PR_FALSE), mErrorCode(0)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
virtual ~DatabaseWorkHelper()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mTarget, "Badness!");
}
NS_IMETHOD
Run()
{
if (NS_IsMainThread()) {
if (mError) {
// Automatically fire error events.
nsCOMPtr<nsIDOMEvent> event(IDBErrorEvent::Create(mErrorCode));
PRBool dummy;
mTarget->DispatchEvent(event, &dummy);
}
else {
OnSuccess();
}
mTarget = nsnull;
return NS_OK;
}
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
// Don't let the function mess with mTarget on the wrong thread!
{
nsCOMPtr<nsIDOMEventTarget> target;
target.swap(mTarget);
mErrorCode = DoDatabaseWork();
target.swap(mTarget);
}
mError = mErrorCode != OK;
NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dispatch()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!gDatabaseThread) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
return gDatabaseThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
protected:
virtual PRUint16 DoDatabaseWork() = 0;
virtual void OnSuccess() = 0;
nsCOMPtr<nsIDOMEventTarget> mTarget;
private:
PRBool mError;
PRUint16 mErrorCode;
};
class CreateObjectStoreHelper : public DatabaseWorkHelper
{
public:
CreateObjectStoreHelper(const nsAString& aName,
const nsAString& aKeyPath,
PRBool aAutoIncrement,
nsIDOMEventTarget* aTarget)
: DatabaseWorkHelper(aTarget), mName(aName), mKeyPath(aKeyPath),
mAutoIncrement(aAutoIncrement)
{ }
PRUint16
DoDatabaseWork()
{
PRUint32 count = gObjectStores->Length();
for (PRUint32 index = 0; index < count; index++) {
if (gObjectStores->ElementAt(index).name.Equals(mName)) {
return nsIIDBDatabaseError::CONSTRAINT_ERR;
}
}
ObjectStore* newStore = gObjectStores->AppendElement();
newStore->name.Assign(mName);
newStore->keyPath.Assign(mKeyPath);
newStore->autoIncrement = mAutoIncrement;
return OK;
}
void
OnSuccess()
{
nsCOMPtr<nsIDOMEvent> event(MakeSuccessEventBool(PR_TRUE));
PRBool dummy;
mTarget->DispatchEvent(event, &dummy);
}
protected:
nsString mName;
nsString mKeyPath;
PRBool mAutoIncrement;
};
class OpenObjectStoreHelper : public DatabaseWorkHelper
{
public:
OpenObjectStoreHelper(const nsAString& aName,
PRUint16 aMode,
nsIDOMEventTarget* aTarget)
: DatabaseWorkHelper(aTarget), mName(aName), mMode(aMode)
{ }
PRUint16
DoDatabaseWork()
{
PRUint32 count = gObjectStores->Length();
for (PRUint32 index = 0; index < count; index++) {
ObjectStore& store = gObjectStores->ElementAt(index);
if (store.name.Equals(mName)) {
if (mMode == nsIIDBObjectStore::READ_WRITE) {
store.writing = PR_TRUE;
}
store.readerCount++;
return OK;
}
}
return nsIIDBDatabaseError::NOT_FOUND_ERR;
}
void
OnSuccess()
{
nsCOMPtr<nsIDOMEvent> event(MakeSuccessEventBool(PR_TRUE));
PRBool dummy;
mTarget->DispatchEvent(event, &dummy);
}
protected:
nsString mName;
PRUint16 mMode;
};
/**
* Creates the needed tables and their indexes.
*
* @param aDBConn
* The database connection to create the tables on.
*/
nsresult
CreateTables(mozIStorageConnection* aDBConn)
{
NS_PRECONDITION(!NS_IsMainThread(),
"Creating tables on the main thread!");
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
// Table `database`
nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE database ("
"name TEXT NOT NULL, "
"description TEXT NOT NULL, "
"version TEXT DEFAULT NULL, "
"UNIQUE (name)"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `object_store`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE object_store ("
"id INTEGER DEFAULT NULL, "
"name TEXT NOT NULL, "
"key_path TEXT DEFAULT NULL, "
"autoincrement INTEGER NOT NULL DEFAULT 0, "
"readers INTEGER NOT NULL DEFAULT 0, "
"isWriting INTEGER NOT NULL DEFAULT 0, "
"PRIMARY KEY (id), "
"UNIQUE (name)"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `object_data`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE object_data ("
"id INTEGER NOT NULL AUTOINCREMENT, "
"object_store_id INTEGER NOT NULL, "
"data TEXT NOT NULL, "
"key_value TEXT DEFAULT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX key_index "
"ON object_data (id, object_store_id);"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `ai_object_data`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE ai_object_data ("
"id INTEGER NOT NULL AUTOINCREMENT, "
"object_store_id INTEGER NOT NULL, "
"data TEXT NOT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX key_index "
"ON ai_object_data (id, object_store_id);"
));
NS_ENSURE_SUCCESS(rv, rv);
return aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION);
}
already_AddRefed<mozIStorageConnection>
NewConnection(const nsCString& aOrigin, bool aDeleteIfExists = false);
bool
ConnectionReady(nsCOMPtr<mozIStorageConnection>& aDBConn,
const nsCString& aOrigin)
{
NS_PRECONDITION(!NS_IsMainThread(),
"Checking a database on the main thread!");
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
// Check to make sure that the database schema is correct.
PRInt32 schemaVersion;
nsresult rv = aDBConn->GetSchemaVersion(&schemaVersion);
NS_ENSURE_SUCCESS(rv, false);
// If the connection is not at the right schema version, nuke it.
if (schemaVersion != DB_SCHEMA_VERSION) {
aDBConn = NewConnection(aOrigin, true);
NS_ENSURE_TRUE(aDBConn, false);
rv = CreateTables(aDBConn);
NS_ENSURE_SUCCESS(rv, false);
}
#ifdef DEBUG
// Turn on foreign key constraints in debug builds to catch bugs!
(void)aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA foreign_keys = ON;"
));
#endif
return true;
}
already_AddRefed<mozIStorageConnection>
NewConnection(const nsCString& aOrigin,
bool aDeleteIfExists)
{
NS_PRECONDITION(!NS_IsMainThread(),
"Opening a database on the main thread!");
nsCOMPtr<nsIFile> dbFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, nsnull);
rv = dbFile->Append(NS_LITERAL_STRING("indexedDB_files"));
NS_ENSURE_SUCCESS(rv, nsnull);
// Replace illegal filename characters.
char illegalChars[] = {':', '/', '\\' };
nsCString fname(aOrigin);
for (size_t i = 0; i < NS_ARRAY_LENGTH(illegalChars); i++) {
fname.ReplaceChar(illegalChars[i], '_');
}
fname.AppendLiteral(".sqlite");
rv = dbFile->Append(NS_ConvertUTF8toUTF16(fname));
NS_ENSURE_SUCCESS(rv, nsnull);
if (aDeleteIfExists) {
rv = dbFile->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
}
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
nsCOMPtr<mozIStorageConnection> conn;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
if (rv == NS_ERROR_FILE_CORRUPTED) {
// Nuke the database file. The web services can recreate their data.
rv = dbFile->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
}
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ENSURE_TRUE(ConnectionReady(conn, aOrigin), false);
return conn.forget();
}
} // anonymous namespace
NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
// static
AsyncDatabaseConnection*
AsyncDatabaseConnection::OpenConnection(const nsAString& aName,
const nsAString& aDescription,
PRBool aReadOnly)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAutoPtr<AsyncDatabaseConnection> connection(new AsyncDatabaseConnection());
if (!gConnectionCount++) {
if (gXPCOMShutdown) {
NS_ERROR("Creating a new connection after shutdown!");
return nsnull;
}
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
NS_ENSURE_TRUE(obs, nsnull);
nsCOMPtr<nsIObserver> observer(new ShutdownObserver());
nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads",
PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ASSERTION(!gObjectStores, "Already got a store?!");
gObjectStores = new nsAutoTArray<ObjectStore, 10>;
rv = NS_NewThread(&gDatabaseThread);
NS_ENSURE_SUCCESS(rv, nsnull);
}
NS_ASSERTION(gDatabaseThread, "No thread?!");
return connection.forget();
}
AsyncDatabaseConnection::AsyncDatabaseConnection()
: mReadOnly(PR_FALSE)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
AsyncDatabaseConnection::~AsyncDatabaseConnection()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
nsresult
AsyncDatabaseConnection::CreateObjectStore(const nsAString& aName,
const nsAString& aKeyPath,
PRBool aAutoIncrement,
nsIDOMEventTarget* aTarget)
{
nsRefPtr<CreateObjectStoreHelper> helper =
new CreateObjectStoreHelper(aName, aKeyPath, aAutoIncrement, aTarget);
return helper->Dispatch();
}
nsresult
AsyncDatabaseConnection::OpenObjectStore(const nsAString& aName,
PRUint16 aMode,
nsIDOMEventTarget* aTarget)
{
nsRefPtr<OpenObjectStoreHelper> helper =
new OpenObjectStoreHelper(aName, aMode, aTarget);
return helper->Dispatch();
}

View File

@ -39,17 +39,27 @@
#include "IDBDatabaseRequest.h"
#include "mozilla/Storage.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDOMClassInfo.h"
#include "nsDOMLists.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "AsyncConnectionHelper.h"
#include "IDBEvents.h"
#include "IDBRequest.h"
#include "LazyIdleThread.h"
#define DB_SCHEMA_VERSION 1
USING_INDEXEDDB_NAMESPACE
namespace {
const PRUint32 kDefaultTimeoutMS = 5000;
const PRUint32 kDefaultDatabaseTimeoutMS = 5000;
const PRUint32 kDefaultThreadTimeoutMS = 30000;
inline
nsISupports*
@ -59,6 +69,200 @@ isupports_cast(IDBDatabaseRequest* aClassPtr)
static_cast<IDBRequest::Generator*>(aClassPtr));
}
class CreateObjectStoreHelper : public AsyncConnectionHelper
{
public:
CreateObjectStoreHelper(nsCOMPtr<mozIStorageConnection>& aConnection,
nsIDOMEventTarget* aTarget,
const nsAString& aName,
const nsAString& aKeyPath,
PRBool aAutoIncrement)
: AsyncConnectionHelper(aConnection, aTarget), mName(aName),
mKeyPath(aKeyPath), mAutoIncrement(aAutoIncrement)
{ }
PRUint16 DoDatabaseWork();
void GetSuccessResult(nsIWritableVariant* aResult);
protected:
nsString mName;
nsString mKeyPath;
PRBool mAutoIncrement;
};
/**
* Creates the needed tables and their indexes.
*
* @param aDBConn
* The database connection to create the tables on.
*/
nsresult
CreateTables(mozIStorageConnection* aDBConn)
{
NS_PRECONDITION(!NS_IsMainThread(),
"Creating tables on the main thread!");
NS_PRECONDITION(aDBConn, "Passing a null database connection!");
// Table `database`
nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE database ("
"name TEXT NOT NULL, "
"description TEXT NOT NULL, "
"version TEXT DEFAULT NULL, "
"UNIQUE (name)"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `object_store`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE object_store ("
"id INTEGER NOT NULL, "
"name TEXT NOT NULL, "
"keyPath TEXT DEFAULT NULL, "
"auto_increment INTEGER NOT NULL DEFAULT 0, "
"readers INTEGER NOT NULL DEFAULT 0, "
"is_writing INTEGER NOT NULL DEFAULT 0, "
"PRIMARY KEY (id), "
"UNIQUE (name)"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `object_data`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE object_data ("
"id INTEGER NOT NULL, "
"object_store_id INTEGER NOT NULL, "
"data TEXT NOT NULL, "
"key_value TEXT DEFAULT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX key_index "
"ON object_data (id, object_store_id);"
));
NS_ENSURE_SUCCESS(rv, rv);
// Table `ai_object_data`
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE ai_object_data ("
"id INTEGER NOT NULL, "
"object_store_id INTEGER NOT NULL, "
"data TEXT NOT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE CASCADE"
");"
));
NS_ENSURE_SUCCESS(rv, rv);
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX ai_key_index "
"ON ai_object_data (id, object_store_id);"
));
NS_ENSURE_SUCCESS(rv, rv);
return aDBConn->SetSchemaVersion(DB_SCHEMA_VERSION);
}
already_AddRefed<mozIStorageConnection>
NewConnection(const nsCString& aOrigin)
{
NS_PRECONDITION(!NS_IsMainThread(),
"Opening a database on the main thread!");
nsCOMPtr<nsIFile> dbFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, nsnull);
rv = dbFile->Append(NS_LITERAL_STRING("indexedDB_files"));
NS_ENSURE_SUCCESS(rv, nsnull);
PRBool exists;
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, nsnull);
if (exists) {
PRBool isDirectory;
rv = dbFile->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ENSURE_TRUE(isDirectory, nsnull);
}
else {
rv = dbFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
NS_ENSURE_SUCCESS(rv, nsnull);
}
// Replace illegal filename characters.
static const char illegalChars[] = {':', '/', '\\' };
nsCString fname(aOrigin);
for (size_t i = 0; i < NS_ARRAY_LENGTH(illegalChars); i++) {
fname.ReplaceChar(illegalChars[i], '_');
}
fname.AppendLiteral(".sqlite");
rv = dbFile->Append(NS_ConvertUTF8toUTF16(fname));
NS_ENSURE_SUCCESS(rv, nsnull);
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
nsCOMPtr<mozIStorageConnection> conn;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
if (rv == NS_ERROR_FILE_CORRUPTED) {
// Nuke the database file. The web services can recreate their data.
rv = dbFile->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
exists = PR_FALSE;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
}
NS_ENSURE_SUCCESS(rv, nsnull);
// Check to make sure that the database schema is correct.
PRInt32 schemaVersion;
rv = conn->GetSchemaVersion(&schemaVersion);
NS_ENSURE_SUCCESS(rv, nsnull);
if (schemaVersion != DB_SCHEMA_VERSION) {
if (exists) {
// If the connection is not at the right schema version, nuke it.
rv = dbFile->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = ss->OpenDatabase(dbFile, getter_AddRefs(conn));
NS_ENSURE_SUCCESS(rv, nsnull);
}
rv = CreateTables(conn);
NS_ENSURE_SUCCESS(rv, nsnull);
}
#ifdef DEBUG
// Check to make sure that the database schema is correct.
NS_ASSERTION(NS_SUCCEEDED(conn->GetSchemaVersion(&schemaVersion)) &&
schemaVersion == DB_SCHEMA_VERSION,
"CreateTables failed!");
// Turn on foreign key constraints in debug builds to catch bugs!
(void)conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA foreign_keys = ON;"
));
#endif
return conn.forget();
}
} // anonymous namespace
// static
@ -68,9 +272,15 @@ IDBDatabaseRequest::Create(const nsAString& aName,
PRBool aReadOnly)
{
nsRefPtr<IDBDatabaseRequest> db(new IDBDatabaseRequest());
db->mDatabase = AsyncDatabaseConnection::OpenConnection(aName, aDescription,
aReadOnly);
NS_ENSURE_TRUE(db->mDatabase, nsnull);
db->mStorageThread = new LazyIdleThread(kDefaultThreadTimeoutMS);
db->mName.Assign(aName);
db->mDescription.Assign(aDescription);
db->mReadOnly = aReadOnly;
db->mObjectStores = new nsDOMStringList();
db->mIndexes = new nsDOMStringList();
nsIIDBDatabaseRequest* result;
db.forget(&result);
@ -84,7 +294,9 @@ IDBDatabaseRequest::IDBDatabaseRequest()
IDBDatabaseRequest::~IDBDatabaseRequest()
{
if (mStorageThread) {
mStorageThread->Shutdown();
}
}
NS_IMPL_ADDREF(IDBDatabaseRequest)
@ -102,43 +314,46 @@ DOMCI_DATA(IDBDatabaseRequest, IDBDatabaseRequest)
NS_IMETHODIMP
IDBDatabaseRequest::GetName(nsAString& aName)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabaseRequest::GetDescription(nsAString& aDescription)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
aDescription.Assign(mDescription);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabaseRequest::GetVersion(nsAString& aVersion)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
aVersion.Assign(mVersion);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabaseRequest::GetObjectStores(nsIDOMDOMStringList** aObjectStores)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsIDOMDOMStringList> objectStores(mObjectStores);
objectStores.forget(aObjectStores);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabaseRequest::GetIndexes(nsIDOMDOMStringList** aIndexes)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsIDOMDOMStringList> indexes(mIndexes);
indexes.forget(aIndexes);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabaseRequest::GetCurrentTransaction(nsIIDBTransaction** aTransaction)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsIIDBTransaction> transaction(mCurrentTransaction);
transaction.forget(aTransaction);
return NS_OK;
}
NS_IMETHODIMP
@ -148,8 +363,12 @@ IDBDatabaseRequest::CreateObjectStore(const nsAString& aName,
nsIIDBRequest** _retval)
{
nsRefPtr<IDBRequest> request = GenerateRequest();
nsresult rv = mDatabase->CreateObjectStore(aName, aKeyPath, aAutoIncrement,
request);
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<CreateObjectStoreHelper> helper =
new CreateObjectStoreHelper(mStorage, request, aName, aKeyPath,
aAutoIncrement);
nsresult rv = helper->Dispatch(mStorageThread);
NS_ENSURE_SUCCESS(rv, rv);
IDBRequest* retval;
@ -164,9 +383,10 @@ IDBDatabaseRequest::OpenObjectStore(const nsAString& aName,
nsIIDBRequest** _retval)
{
nsRefPtr<IDBRequest> request = GenerateRequest();
/*
nsresult rv = mDatabase->OpenObjectStore(aName, aMode, request);
NS_ENSURE_SUCCESS(rv, rv);
*/
IDBRequest* retval;
request.forget(&retval);
*_retval = retval;
@ -245,7 +465,7 @@ IDBDatabaseRequest::OpenTransaction(nsIDOMDOMStringList* aStoreNames,
NS_NOTYETIMPLEMENTED("Implement me!");
if (aArgCount < 2) {
aTimeout = kDefaultTimeoutMS;
aTimeout = kDefaultDatabaseTimeoutMS;
}
nsCOMPtr<nsIIDBRequest> request(GenerateRequest());
@ -253,3 +473,16 @@ IDBDatabaseRequest::OpenTransaction(nsIDOMDOMStringList* aStoreNames,
return NS_OK;
}
PRUint16
CreateObjectStoreHelper::DoDatabaseWork()
{
mConnection = NewConnection(NS_LITERAL_CSTRING("http://foo.com/bar.html"));
return mConnection ? OK : nsIIDBDatabaseError::UNKNOWN_ERR;
}
void
CreateObjectStoreHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
aResult->SetAsBool(PR_TRUE);
}

View File

@ -41,9 +41,12 @@
#define mozilla_dom_indexeddb_idbdatabaserequest_h__
#include "mozilla/dom/indexedDB/IDBRequest.h"
#include "mozilla/dom/indexedDB/AsyncDatabaseConnection.h"
#include "mozilla/dom/indexedDB/LazyIdleThread.h"
#include "mozIStorageConnection.h"
#include "nsIDOMDOMStringList.h"
#include "nsIIDBDatabaseRequest.h"
#include "nsIIDBTransaction.h"
BEGIN_INDEXEDDB_NAMESPACE
@ -65,7 +68,18 @@ protected:
~IDBDatabaseRequest();
private:
nsAutoPtr<AsyncDatabaseConnection> mDatabase;
nsString mName;
nsString mDescription;
PRBool mReadOnly;
nsString mVersion;
nsCOMPtr<nsIDOMDOMStringList> mObjectStores;
nsCOMPtr<nsIDOMDOMStringList> mIndexes;
nsCOMPtr<nsIIDBTransaction> mCurrentTransaction;
nsRefPtr<LazyIdleThread> mStorageThread;
// Only touched on mStorageThread!
nsCOMPtr<mozIStorageConnection> mStorage;
};
END_INDEXEDDB_NAMESPACE

View File

@ -37,47 +37,37 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_indexeddb_asyncdatabaseconnection_h__
#define mozilla_dom_indexeddb_asyncdatabaseconnection_h__
#ifndef mozilla_dom_indexeddb_idbdatabaserequest_h__
#define mozilla_dom_indexeddb_idbdatabaserequest_h__
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
class nsIDOMEventTarget;
#include "nsIIDBObjectStoreRequest.h"
BEGIN_INDEXEDDB_NAMESPACE
/**
* This is mostly a fake backend until we have a real one
*/
class AsyncDatabaseConnection
class IDBObjectStoreRequest : public IDBRequest::Generator,
public nsIIDBObjectStoreRequest
{
public:
static AsyncDatabaseConnection*
OpenConnection(const nsAString& aName,
const nsAString& aDescription,
PRBool aReadOnly);
~AsyncDatabaseConnection();
NS_DECL_ISUPPORTS
NS_DECL_NSIIDBOBJECTSTORE
NS_DECL_NSIIDBOBJECTSTOREREQUEST
nsresult
CreateObjectStore(const nsAString& aName,
const nsAString& aKeyPath,
PRBool aAutoIncrement,
nsIDOMEventTarget* aTarget);
static already_AddRefed<nsIIDBObjectStoreRequest>
Create(const nsAString& aName,
const nsAString& aKeyPath,
PRBool aAutoIncrement,
PRUint16 aMode);
nsresult
OpenObjectStore(const nsAString& aName,
PRUint16 aMode,
nsIDOMEventTarget* aTarget);
protected:
IDBObjectStoreRequest();
~IDBObjectStoreRequest();
private:
AsyncDatabaseConnection();
nsString mName;
nsString mDescription;
PRBool mReadOnly;
nsAutoPtr<AsyncDatabaseConnection> mDatabase;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_asyncdatabaseconnection_h__
#endif // mozilla_dom_indexeddb_idbdatabaserequest_h__

View File

@ -76,16 +76,43 @@ LazyIdleThread::LazyIdleThread(PRUint32 aIdleTimeoutMS)
mOwningThread(NS_GetCurrentThread()),
mIdleTimeoutMS(aIdleTimeoutMS),
mShutdown(PR_FALSE),
mThreadHasTimedOut(PR_FALSE)
mThreadHasTimedOut(PR_FALSE),
mTimeoutDisabledCount(0)
{
NS_ASSERTION(mOwningThread, "This should never fail!");
}
LazyIdleThread::~LazyIdleThread()
{
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
if (!mShutdown) {
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
Shutdown();
}
}
Shutdown();
void
LazyIdleThread::EnableIdleTimeout(PRBool aEnable)
{
if (aEnable) {
MutexAutoLock lock(mMutex);
if (mTimeoutDisabledCount) {
mTimeoutDisabledCount--;
}
return;
}
nsCOMPtr<nsITimer> timer;
{
MutexAutoLock lock(mMutex);
NS_ASSERTION(mTimeoutDisabledCount < PR_UINT32_MAX, "Too many calls!");
if (mTimeoutDisabledCount < PR_UINT32_MAX) {
mTimeoutDisabledCount++;
}
timer.swap(mIdleTimer);
}
if (timer) {
CancelTimer(timer);
}
}
/**
@ -177,35 +204,108 @@ LazyIdleThread::ShutdownThread()
}
}
NS_IMPL_THREADSAFE_ISUPPORTS3(LazyIdleThread, nsITimerCallback,
/**
* Cancel any pending timer. May be called from any thread.
*/
void
LazyIdleThread::CancelTimer(nsITimer* aTimer)
{
NS_ASSERTION(aTimer, "Null timer!");
if (NS_FAILED(mOwningThread->Dispatch(new CancelTimerRunnable(aTimer),
NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch CancelTimerRunnable!");
}
}
NS_IMPL_THREADSAFE_ISUPPORTS5(LazyIdleThread, nsIThread,
nsIEventTarget,
nsITimerCallback,
nsIThreadObserver,
nsIObserver)
/**
* Dispatch an event to the thread.
*/
nsresult
LazyIdleThread::Dispatch(nsIRunnable* aEvent)
NS_IMETHODIMP
LazyIdleThread::Dispatch(nsIRunnable* aEvent,
PRUint32 aFlags)
{
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
nsresult rv = EnsureThread();
NS_ENSURE_SUCCESS(rv, rv);
return mThread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
return mThread->Dispatch(aEvent, aFlags);
}
NS_IMETHODIMP
LazyIdleThread::IsOnCurrentThread(PRBool* aIsOnCurrentThread)
{
if (mThread) {
return mThread->IsOnCurrentThread(aIsOnCurrentThread);
}
*aIsOnCurrentThread = PR_FALSE;
return NS_OK;
}
/**
* Get the PRThread for our thread (if it has been created).
*/
NS_IMETHODIMP
LazyIdleThread::GetPRThread(PRThread** aPRThread)
{
if (mThread) {
return mThread->GetPRThread(aPRThread);
}
*aPRThread = nsnull;
return NS_ERROR_NOT_AVAILABLE;
}
/**
* Shut down the thread (if it has been created) and prevent future dispatch.
*/
void
NS_IMETHODIMP
LazyIdleThread::Shutdown()
{
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
if (!mShutdown) {
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
mShutdown = PR_TRUE;
ShutdownThread();
ShutdownThread();
mShutdown = PR_TRUE;
mOwningThread = nsnull;
}
return NS_OK;
}
/**
* See if there are more events to be processed. This is only supposed to be
* called from the thread itself.
*/
NS_IMETHODIMP
LazyIdleThread::HasPendingEvents(PRBool* aHasPendingEvents)
{
nsCOMPtr<nsIThread> thisThread(NS_GetCurrentThread());
NS_ASSERTION(thisThread, "This should never be null!");
return thisThread->HasPendingEvents(aHasPendingEvents);
}
/**
* Run another event on the thread. Should only be called from the thread
* itself.
*/
NS_IMETHODIMP
LazyIdleThread::ProcessNextEvent(PRBool aMayWait,
PRBool* aEventWasProcessed)
{
nsCOMPtr<nsIThread> thisThread(NS_GetCurrentThread());
NS_ASSERTION(thisThread, "This should never be null!");
return thisThread->ProcessNextEvent(aMayWait, aEventWasProcessed);
}
/**
@ -247,7 +347,6 @@ NS_IMETHODIMP
LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
{
// This can happen on *any* thread...
nsCOMPtr<nsITimer> timer;
{
MutexAutoLock lock(mMutex);
@ -260,9 +359,7 @@ LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
}
if (timer) {
nsresult rv = mOwningThread->Dispatch(new CancelTimerRunnable(timer),
NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
CancelTimer(timer);
}
return NS_OK;
}
@ -296,7 +393,7 @@ LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* aThread,
MutexAutoLock lock(mMutex);
if (mThreadHasTimedOut) {
if (mThreadHasTimedOut || mTimeoutDisabledCount) {
return NS_OK;
}
@ -323,8 +420,7 @@ LazyIdleThread::Observe(nsISupports* /* aSubject */,
const char* /* aTopic */,
const PRUnichar* /* aData */)
{
NS_ASSERTION(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
ShutdownThread();
Shutdown();
return NS_OK;
}

View File

@ -57,12 +57,15 @@ BEGIN_INDEXEDDB_NAMESPACE
* is created on the main thread then it will automatically join its thread on
* XPCOM shutdown using the Observer Service.
*/
class LazyIdleThread : public nsITimerCallback,
class LazyIdleThread : public nsIThread,
public nsITimerCallback,
public nsIThreadObserver,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTTARGET
NS_DECL_NSITHREAD
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIOBSERVER
@ -74,16 +77,9 @@ public:
LazyIdleThread(PRUint32 aIdleTimeoutMS);
/**
* Dispatch an event to the thread, creating it if necessary.
* Enables or disables the idle timeout.
*/
nsresult Dispatch(nsIRunnable* aEvent);
/**
* Join the thread and prevent further event dispatch. It is not possible to
* reuse the LazyIdleThread after calling this. Shutdown() will also be called
* automatically when the reference count drops to 0.
*/
void Shutdown();
void EnableIdleTimeout(PRBool aEnable);
private:
/**
@ -106,6 +102,11 @@ private:
*/
void ShutdownThread();
/**
* Called to cancel any pending timer.
*/
void CancelTimer(nsITimer* aTimer);
/**
* Protects mIdleTimer and mThreadHasTimedOut.
*/
@ -145,6 +146,11 @@ private:
* when we're in the process of shutting down mThread.
*/
PRBool mThreadHasTimedOut;
/**
* Protected by mMutex. Whether or not the timeout is enabled.
*/
PRUint32 mTimeoutDisabledCount;
};
END_INDEXEDDB_NAMESPACE

View File

@ -52,7 +52,7 @@ FORCE_STATIC_LIB = 1
EXPORTS_NAMESPACES = mozilla/dom/indexedDB
CPPSRCS = \
AsyncDatabaseConnection.cpp \
AsyncConnectionHelper.cpp \
IDBDatabaseError.cpp \
IDBDatabaseRequest.cpp \
IDBEvents.cpp \
@ -62,7 +62,6 @@ CPPSRCS = \
$(NULL)
EXPORTS_mozilla/dom/indexedDB = \
AsyncDatabaseConnection.h \
IDBDatabaseError.h \
IDBDatabaseRequest.h \
IDBEvents.h \