Make mozIStorageConnection threadsafe - bug 448476 r=robarnold

This commit is contained in:
Shawn Wilsher 2008-08-12 17:50:25 -07:00
Родитель 134dad3910
Коммит bbe06d499c
3 изменённых файлов: 50 добавлений и 17 удалений

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

@ -53,6 +53,8 @@ interface nsIFile;
* primary interface for interacting with a database, including
* creating prepared statements, executing SQL, and examining database
* errors.
*
* @threadsafe
*/
[scriptable, uuid(623f9ddb-434b-4d39-bc2d-1c617da241d0)]
interface mozIStorageConnection : nsISupports {
@ -206,7 +208,9 @@ interface mozIStorageConnection : nsISupports {
*/
/**
* Create a new SQLite function
* Create a new SQL function. If you use your connection on multiple threads,
* your function needs to be threadsafe, or it should only be called on one
* thread.
*
* @param aFunctionName The name of function to create, as seen in SQL.
* @param aNumArguments The number of arguments the function takes. Pass
@ -219,7 +223,9 @@ interface mozIStorageConnection : nsISupports {
in mozIStorageFunction aFunction);
/**
* Create a new SQLite aggregate function
* Create a new SQL aggregate function. If you use your connection on
* multiple threads, your function needs to be threadsafe, or it should only
* be called on one thread.
*
* @param aFunctionName The name of aggregate function to create, as seen
* in SQL.
@ -232,7 +238,7 @@ interface mozIStorageConnection : nsISupports {
in long aNumArguments,
in mozIStorageAggregateFunction aFunction);
/**
* Delete custom SQLite function (simple or aggregate one)
* Delete custom SQL function (simple or aggregate one).
*
* @param aFunctionName The name of function to remove.
*/
@ -240,7 +246,9 @@ interface mozIStorageConnection : nsISupports {
/**
* Sets a progress handler. Only one handler can be registered at a time.
* If you need more than one, you need to chain them yourself.
* If you need more than one, you need to chain them yourself. This progress
* handler should be threadsafe if you use this connection object on more than
* one thread.
*
* @param aGranularity The number of SQL virtual machine steps between
* progress handler callbacks.

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

@ -70,12 +70,16 @@ PRLogModuleInfo* gStorageLog = nsnull;
#define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
NS_IMPL_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
NS_IMPL_THREADSAFE_ISUPPORTS1(mozStorageConnection, mozIStorageConnection)
mozStorageConnection::mozStorageConnection(mozIStorageService* aService)
: mDBConn(nsnull), mTransactionInProgress(PR_FALSE),
mProgressHandler(nsnull),
mStorageService(aService)
mozStorageConnection::mozStorageConnection(mozIStorageService* aService) :
mDBConn(nsnull)
, mTransactionMutex(nsAutoLock::NewLock("TransactionMutex"))
, mTransactionInProgress(PR_FALSE)
, mFunctionsMutex(nsAutoLock::NewLock("FunctionsMutex"))
, mProgressHandlerMutex(nsAutoLock::NewLock("ProgressHandlerMutex"))
, mProgressHandler(nsnull)
, mStorageService(aService)
{
mFunctions.Init();
}
@ -83,6 +87,9 @@ mozStorageConnection::mozStorageConnection(mozIStorageService* aService)
mozStorageConnection::~mozStorageConnection()
{
(void)Close();
nsAutoLock::DestroyLock(mTransactionMutex);
nsAutoLock::DestroyLock(mFunctionsMutex);
nsAutoLock::DestroyLock(mProgressHandlerMutex);
}
#ifdef PR_LOGGING
@ -101,6 +108,9 @@ NS_IMETHODIMP
mozStorageConnection::Initialize(nsIFile *aDatabaseFile)
{
NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
NS_ENSURE_TRUE(mTransactionMutex, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mFunctionsMutex, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mProgressHandlerMutex, NS_ERROR_OUT_OF_MEMORY);
int srv;
nsresult rv;
@ -215,8 +225,12 @@ mozStorageConnection::Close()
leafName.get()));
#endif
if (mProgressHandler)
sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
{
nsAutoLock mutex(mProgressHandlerMutex);
if (mProgressHandler)
sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
}
int srv = sqlite3_close(mDBConn);
if (srv != SQLITE_OK)
NS_WARNING("sqlite3_close failed. There are probably outstanding statements!");
@ -417,6 +431,7 @@ mozStorageConnection::IndexExists(const nsACString& aIndexName, PRBool* _retval)
NS_IMETHODIMP
mozStorageConnection::GetTransactionInProgress(PRBool *_retval)
{
nsAutoLock mutex(mTransactionMutex);
*_retval = mTransactionInProgress;
return NS_OK;
}
@ -425,17 +440,13 @@ mozStorageConnection::GetTransactionInProgress(PRBool *_retval)
NS_IMETHODIMP
mozStorageConnection::BeginTransaction()
{
if (mTransactionInProgress)
return NS_ERROR_FAILURE;
nsresult rv = ExecuteSimpleSQL (NS_LITERAL_CSTRING("BEGIN TRANSACTION"));
if (NS_SUCCEEDED(rv))
mTransactionInProgress = PR_TRUE;
return rv;
return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
}
NS_IMETHODIMP
mozStorageConnection::BeginTransactionAs(PRInt32 aTransactionType)
{
nsAutoLock mutex(mTransactionMutex);
if (mTransactionInProgress)
return NS_ERROR_FAILURE;
nsresult rv;
@ -460,6 +471,7 @@ mozStorageConnection::BeginTransactionAs(PRInt32 aTransactionType)
NS_IMETHODIMP
mozStorageConnection::CommitTransaction()
{
nsAutoLock mutex(mTransactionMutex);
if (!mTransactionInProgress)
return NS_ERROR_FAILURE;
nsresult rv = ExecuteSimpleSQL (NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
@ -471,6 +483,7 @@ mozStorageConnection::CommitTransaction()
NS_IMETHODIMP
mozStorageConnection::RollbackTransaction()
{
nsAutoLock mutex(mTransactionMutex);
if (!mTransactionInProgress)
return NS_ERROR_FAILURE;
nsresult rv = ExecuteSimpleSQL (NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
@ -523,6 +536,7 @@ mozStorageConnection::s_FindFuncEnum(const nsACString &aKey,
PRBool
mozStorageConnection::FindFunctionByInstance(nsISupports *aInstance)
{
// The lock should already be held by calling functions
FindFuncEnumArgs args = { aInstance, PR_FALSE };
mFunctions.EnumerateRead(s_FindFuncEnum, &args);
return args.mFound;
@ -663,6 +677,7 @@ mozStorageConnection::CreateFunction(const nsACString &aFunctionName,
// do we already have this function defined?
// Check for name only because simple function can
// be defined multiple times with different names (aliases).
nsAutoLock mutex(mFunctionsMutex);
NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
int srv = sqlite3_create_function (mDBConn,
@ -738,6 +753,7 @@ mozStorageConnection::CreateAggregateFunction(const nsACString &aFunctionName,
// do we already have this function defined?
// Check for name.
nsAutoLock mutex(mFunctionsMutex);
NS_ENSURE_FALSE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
// Aggregate functions are stateful, so we cannot have
@ -770,6 +786,7 @@ mozStorageConnection::RemoveFunction(const nsACString &aFunctionName)
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
nsAutoLock mutex(mFunctionsMutex);
NS_ENSURE_TRUE(mFunctions.Get (aFunctionName, NULL), NS_ERROR_FAILURE);
int srv = sqlite3_create_function (mDBConn,
@ -805,6 +822,7 @@ mozStorageConnection::SetProgressHandler(PRInt32 aGranularity,
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
// Return previous one
nsAutoLock mutex(mProgressHandlerMutex);
NS_IF_ADDREF(*aOldHandler = mProgressHandler);
if (!aHandler || aGranularity <= 0) {
@ -823,6 +841,7 @@ mozStorageConnection::RemoveProgressHandler(mozIStorageProgressHandler **aOldHan
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
// Return previous one
nsAutoLock mutex(mProgressHandlerMutex);
NS_IF_ADDREF(*aOldHandler = mProgressHandler);
mProgressHandler = nsnull;
@ -834,6 +853,7 @@ mozStorageConnection::RemoveProgressHandler(mozIStorageProgressHandler **aOldHan
int
mozStorageConnection::ProgressHandler()
{
nsAutoLock mutex(mProgressHandlerMutex);
if (mProgressHandler) {
PRBool res;
nsresult rv = mProgressHandler->OnProgress(this, &res);

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

@ -41,6 +41,7 @@
#define _MOZSTORAGECONNECTION_H_
#include "nsCOMPtr.h"
#include "nsAutoLock.h"
#include "nsString.h"
#include "nsInterfaceHashtable.h"
@ -91,10 +92,14 @@ protected:
sqlite3 *mDBConn;
nsCOMPtr<nsIFile> mDatabaseFile;
PRLock *mTransactionMutex;
PRBool mTransactionInProgress;
PRLock *mFunctionsMutex;
nsInterfaceHashtable<nsCStringHashKey, nsISupports> mFunctions;
PRLock *mProgressHandlerMutex;
nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
// This isn't accessed but is used to make sure that the connections do