зеркало из https://github.com/mozilla/gecko-dev.git
Bug 914070 - Part 2 - nullify mDBConn at setClosedState and provide an isClosed helper. r=asuth
This commit is contained in:
Родитель
528ab442ef
Коммит
bf86937455
|
@ -135,6 +135,7 @@ AsyncStatement::initialize(Connection *aDBConnection,
|
||||||
const nsACString &aSQLStatement)
|
const nsACString &aSQLStatement)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aDBConnection, "No database connection given!");
|
MOZ_ASSERT(aDBConnection, "No database connection given!");
|
||||||
|
MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
|
||||||
MOZ_ASSERT(aNativeConnection, "No native connection given!");
|
MOZ_ASSERT(aNativeConnection, "No native connection given!");
|
||||||
|
|
||||||
mDBConnection = aDBConnection;
|
mDBConnection = aDBConnection;
|
||||||
|
@ -301,7 +302,8 @@ AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!mAsyncStatement) {
|
if (!mAsyncStatement) {
|
||||||
int rc = mDBConnection->prepareStatement(mSQLString, &mAsyncStatement);
|
int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
|
||||||
|
&mAsyncStatement);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
||||||
("Sqlite statement prepare error: %d '%s'", rc,
|
("Sqlite statement prepare error: %d '%s'", rc,
|
||||||
|
|
|
@ -337,7 +337,7 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
|
||||||
// lock the sqlite mutex so sqlite3_errmsg cannot change
|
// lock the sqlite mutex so sqlite3_errmsg cannot change
|
||||||
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
SQLiteMutexAutoLock lockedScope(mDBMutex);
|
||||||
|
|
||||||
int rc = mConnection->stepStatement(aStatement);
|
int rc = mConnection->stepStatement(mNativeConnection, aStatement);
|
||||||
// Stop if we have no more results.
|
// Stop if we have no more results.
|
||||||
if (rc == SQLITE_DONE)
|
if (rc == SQLITE_DONE)
|
||||||
{
|
{
|
||||||
|
@ -568,6 +568,8 @@ AsyncExecuteStatements::Cancel()
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
AsyncExecuteStatements::Run()
|
AsyncExecuteStatements::Run()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(!mConnection->isClosed());
|
||||||
|
|
||||||
// Do not run if we have been canceled.
|
// Do not run if we have been canceled.
|
||||||
{
|
{
|
||||||
MutexAutoLock lockedScope(mMutex);
|
MutexAutoLock lockedScope(mMutex);
|
||||||
|
|
|
@ -333,9 +333,11 @@ class AsyncCloseConnection MOZ_FINAL: public nsRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AsyncCloseConnection(Connection *aConnection,
|
AsyncCloseConnection(Connection *aConnection,
|
||||||
|
sqlite3 *aNativeConnection,
|
||||||
nsIRunnable *aCallbackEvent,
|
nsIRunnable *aCallbackEvent,
|
||||||
already_AddRefed<nsIThread> aAsyncExecutionThread)
|
already_AddRefed<nsIThread> aAsyncExecutionThread)
|
||||||
: mConnection(aConnection)
|
: mConnection(aConnection)
|
||||||
|
, mNativeConnection(aNativeConnection)
|
||||||
, mCallbackEvent(aCallbackEvent)
|
, mCallbackEvent(aCallbackEvent)
|
||||||
, mAsyncExecutionThread(aAsyncExecutionThread)
|
, mAsyncExecutionThread(aAsyncExecutionThread)
|
||||||
{
|
{
|
||||||
|
@ -351,7 +353,7 @@ public:
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
// Internal close.
|
// Internal close.
|
||||||
(void)mConnection->internalClose();
|
(void)mConnection->internalClose(mNativeConnection);
|
||||||
|
|
||||||
// Callback
|
// Callback
|
||||||
if (mCallbackEvent) {
|
if (mCallbackEvent) {
|
||||||
|
@ -376,6 +378,7 @@ public:
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
nsRefPtr<Connection> mConnection;
|
nsRefPtr<Connection> mConnection;
|
||||||
|
sqlite3 *mNativeConnection;
|
||||||
nsCOMPtr<nsIRunnable> mCallbackEvent;
|
nsCOMPtr<nsIRunnable> mCallbackEvent;
|
||||||
nsCOMPtr<nsIThread> mAsyncExecutionThread;
|
nsCOMPtr<nsIThread> mAsyncExecutionThread;
|
||||||
};
|
};
|
||||||
|
@ -470,6 +473,7 @@ Connection::Connection(Service *aService,
|
||||||
, threadOpenedOn(do_GetCurrentThread())
|
, threadOpenedOn(do_GetCurrentThread())
|
||||||
, mDBConn(nullptr)
|
, mDBConn(nullptr)
|
||||||
, mAsyncExecutionThreadShuttingDown(false)
|
, mAsyncExecutionThreadShuttingDown(false)
|
||||||
|
, mConnectionClosed(false)
|
||||||
, mTransactionInProgress(false)
|
, mTransactionInProgress(false)
|
||||||
, mProgressHandler(nullptr)
|
, mProgressHandler(nullptr)
|
||||||
, mFlags(aFlags)
|
, mFlags(aFlags)
|
||||||
|
@ -744,11 +748,11 @@ Connection::databaseElementExists(enum DatabaseElementType aElementType,
|
||||||
query.Append("'");
|
query.Append("'");
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int srv = prepareStatement(query, &stmt);
|
int srv = prepareStatement(mDBConn, query, &stmt);
|
||||||
if (srv != SQLITE_OK)
|
if (srv != SQLITE_OK)
|
||||||
return convertResultCode(srv);
|
return convertResultCode(srv);
|
||||||
|
|
||||||
srv = stepStatement(stmt);
|
srv = stepStatement(mDBConn, stmt);
|
||||||
// we just care about the return value from step
|
// we just care about the return value from step
|
||||||
(void)::sqlite3_finalize(stmt);
|
(void)::sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
@ -813,31 +817,50 @@ Connection::setClosedState()
|
||||||
mAsyncExecutionThreadShuttingDown = true;
|
mAsyncExecutionThreadShuttingDown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the property to null before closing the connection, otherwise the other
|
||||||
|
// functions in the module may try to use the connection after it is closed.
|
||||||
|
mDBConn = nullptr;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Connection::isClosing(bool aResultOnClosed) {
|
Connection::connectionReady()
|
||||||
|
{
|
||||||
|
return mDBConn != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Connection::isClosing()
|
||||||
|
{
|
||||||
|
bool shuttingDown = false;
|
||||||
|
{
|
||||||
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
|
shuttingDown = mAsyncExecutionThreadShuttingDown;
|
||||||
|
}
|
||||||
|
return shuttingDown && !isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Connection::isClosed()
|
||||||
|
{
|
||||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
return mAsyncExecutionThreadShuttingDown &&
|
return mConnectionClosed;
|
||||||
(aResultOnClosed || ConnectionReady());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
Connection::internalClose()
|
Connection::internalClose(sqlite3 *aNativeConnection)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
|
||||||
// Sanity checks to make sure we are in the proper state before calling this.
|
// Sanity checks to make sure we are in the proper state before calling this.
|
||||||
NS_ASSERTION(mDBConn, "Database connection is already null!");
|
MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
|
||||||
|
MOZ_ASSERT(!isClosed());
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
{ // Make sure we have marked our async thread as shutting down.
|
{ // Make sure we have marked our async thread as shutting down.
|
||||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
|
NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
|
||||||
"Did not call setClosedState!");
|
"Did not call setClosedState!");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool onOpeningThread = false;
|
|
||||||
(void)threadOpenedOn->IsOnCurrentThread(&onOpeningThread);
|
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
|
@ -848,25 +871,23 @@ Connection::internalClose()
|
||||||
leafName.get()));
|
leafName.get()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set the property to null before closing the connection, otherwise the other
|
|
||||||
// functions in the module may try to use the connection after it is closed.
|
|
||||||
sqlite3 *dbConn = mDBConn;
|
|
||||||
mDBConn = nullptr;
|
|
||||||
|
|
||||||
// At this stage, we may still have statements that need to be
|
// At this stage, we may still have statements that need to be
|
||||||
// finalized. Attempt to close the database connection. This will
|
// finalized. Attempt to close the database connection. This will
|
||||||
// always disconnect any virtual tables and cleanly finalize their
|
// always disconnect any virtual tables and cleanly finalize their
|
||||||
// internal statements. Once this is done, closing may fail due to
|
// internal statements. Once this is done, closing may fail due to
|
||||||
// unfinalized client statements, in which case we need to finalize
|
// unfinalized client statements, in which case we need to finalize
|
||||||
// these statements and close again.
|
// these statements and close again.
|
||||||
|
{
|
||||||
int srv = sqlite3_close(dbConn);
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
|
mConnectionClosed = true;
|
||||||
|
}
|
||||||
|
int srv = sqlite3_close(aNativeConnection);
|
||||||
|
|
||||||
if (srv == SQLITE_BUSY) {
|
if (srv == SQLITE_BUSY) {
|
||||||
// We still have non-finalized statements. Finalize them.
|
// We still have non-finalized statements. Finalize them.
|
||||||
|
|
||||||
sqlite3_stmt *stmt = nullptr;
|
sqlite3_stmt *stmt = nullptr;
|
||||||
while ((stmt = ::sqlite3_next_stmt(dbConn, stmt))) {
|
while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
|
||||||
PR_LOG(gStorageLog, PR_LOG_NOTICE,
|
PR_LOG(gStorageLog, PR_LOG_NOTICE,
|
||||||
("Auto-finalizing SQL statement '%s' (%x)",
|
("Auto-finalizing SQL statement '%s' (%x)",
|
||||||
::sqlite3_sql(stmt),
|
::sqlite3_sql(stmt),
|
||||||
|
@ -901,7 +922,7 @@ Connection::internalClose()
|
||||||
|
|
||||||
// Now that all statements have been finalized, we
|
// Now that all statements have been finalized, we
|
||||||
// should be able to close.
|
// should be able to close.
|
||||||
srv = ::sqlite3_close(dbConn);
|
srv = ::sqlite3_close(aNativeConnection);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -924,23 +945,21 @@ Connection::getFilename()
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Connection::stepStatement(sqlite3_stmt *aStatement)
|
Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aStatement);
|
MOZ_ASSERT(aStatement);
|
||||||
bool checkedMainThread = false;
|
bool checkedMainThread = false;
|
||||||
TimeStamp startTime = TimeStamp::Now();
|
TimeStamp startTime = TimeStamp::Now();
|
||||||
|
|
||||||
// mDBConn may be null if the executing statement has been created and cached
|
// The connection may have been closed if the executing statement has been
|
||||||
// after a call to asyncClose() but before the connection has been nullified
|
// created and cached after a call to asyncClose() but before the actual
|
||||||
// by internalClose(). In such a case closing the connection fails due to
|
// sqlite3_close(). This usually happens when other tasks using cached
|
||||||
// the existence of prepared statements, but mDBConn is set to null
|
// statements are asynchronously scheduled for execution and any of them ends
|
||||||
// regardless. This usually happens when other tasks using cached statements
|
// up after asyncClose. See bug 728653 for details.
|
||||||
// are asynchronously scheduled for execution and any of them ends up after
|
if (isClosed())
|
||||||
// asyncClose. See bug 728653 for details.
|
|
||||||
if (!mDBConn)
|
|
||||||
return SQLITE_MISUSE;
|
return SQLITE_MISUSE;
|
||||||
|
|
||||||
(void)::sqlite3_extended_result_codes(mDBConn, 1);
|
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
|
||||||
|
|
||||||
int srv;
|
int srv;
|
||||||
while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
|
while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
|
||||||
|
@ -952,7 +971,7 @@ Connection::stepStatement(sqlite3_stmt *aStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srv = WaitForUnlockNotify(mDBConn);
|
srv = WaitForUnlockNotify(aNativeConnection);
|
||||||
if (srv != SQLITE_OK) {
|
if (srv != SQLITE_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -971,21 +990,26 @@ Connection::stepStatement(sqlite3_stmt *aStatement)
|
||||||
duration.ToMilliseconds());
|
duration.ToMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)::sqlite3_extended_result_codes(mDBConn, 0);
|
(void)::sqlite3_extended_result_codes(aNativeConnection, 0);
|
||||||
// Drop off the extended result bits of the result code.
|
// Drop off the extended result bits of the result code.
|
||||||
return srv & 0xFF;
|
return srv & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Connection::prepareStatement(const nsCString &aSQL,
|
Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
|
||||||
sqlite3_stmt **_stmt)
|
sqlite3_stmt **_stmt)
|
||||||
{
|
{
|
||||||
|
// We should not even try to prepare statements after the connection has
|
||||||
|
// been closed.
|
||||||
|
if (isClosed())
|
||||||
|
return SQLITE_MISUSE;
|
||||||
|
|
||||||
bool checkedMainThread = false;
|
bool checkedMainThread = false;
|
||||||
|
|
||||||
(void)::sqlite3_extended_result_codes(mDBConn, 1);
|
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
|
||||||
|
|
||||||
int srv;
|
int srv;
|
||||||
while((srv = ::sqlite3_prepare_v2(mDBConn,
|
while((srv = ::sqlite3_prepare_v2(aNativeConnection,
|
||||||
aSQL.get(),
|
aSQL.get(),
|
||||||
-1,
|
-1,
|
||||||
_stmt,
|
_stmt,
|
||||||
|
@ -998,7 +1022,7 @@ Connection::prepareStatement(const nsCString &aSQL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
srv = WaitForUnlockNotify(mDBConn);
|
srv = WaitForUnlockNotify(aNativeConnection);
|
||||||
if (srv != SQLITE_OK) {
|
if (srv != SQLITE_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1009,7 +1033,7 @@ Connection::prepareStatement(const nsCString &aSQL,
|
||||||
warnMsg.AppendLiteral("The SQL statement '");
|
warnMsg.AppendLiteral("The SQL statement '");
|
||||||
warnMsg.Append(aSQL);
|
warnMsg.Append(aSQL);
|
||||||
warnMsg.AppendLiteral("' could not be compiled due to an error: ");
|
warnMsg.AppendLiteral("' could not be compiled due to an error: ");
|
||||||
warnMsg.Append(::sqlite3_errmsg(mDBConn));
|
warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
NS_WARNING(warnMsg.get());
|
NS_WARNING(warnMsg.get());
|
||||||
|
@ -1017,7 +1041,7 @@ Connection::prepareStatement(const nsCString &aSQL,
|
||||||
PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
|
PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)::sqlite3_extended_result_codes(mDBConn, 0);
|
(void)::sqlite3_extended_result_codes(aNativeConnection, 0);
|
||||||
// Drop off the extended result bits of the result code.
|
// Drop off the extended result bits of the result code.
|
||||||
int rc = srv & 0xFF;
|
int rc = srv & 0xFF;
|
||||||
// sqlite will return OK on a comment only string and set _stmt to nullptr.
|
// sqlite will return OK on a comment only string and set _stmt to nullptr.
|
||||||
|
@ -1088,10 +1112,13 @@ Connection::Close()
|
||||||
NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
|
NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setClosedState nullifies our connection pointer, so we take a raw pointer
|
||||||
|
// off it, to pass it through the close procedure.
|
||||||
|
sqlite3 *nativeConn = mDBConn;
|
||||||
nsresult rv = setClosedState();
|
nsresult rv = setClosedState();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return internalClose();
|
return internalClose(nativeConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -1106,6 +1133,9 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
|
||||||
nsIEventTarget *asyncThread = getAsyncExecutionTarget();
|
nsIEventTarget *asyncThread = getAsyncExecutionTarget();
|
||||||
NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
// setClosedState nullifies our connection pointer, so we take a raw pointer
|
||||||
|
// off it, to pass it through the close procedure.
|
||||||
|
sqlite3 *nativeConn = mDBConn;
|
||||||
nsresult rv = setClosedState();
|
nsresult rv = setClosedState();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
@ -1121,6 +1151,7 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
|
||||||
// We need to lock because we're modifying mAsyncExecutionThread
|
// We need to lock because we're modifying mAsyncExecutionThread
|
||||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
closeEvent = new AsyncCloseConnection(this,
|
closeEvent = new AsyncCloseConnection(this,
|
||||||
|
nativeConn,
|
||||||
completeEvent,
|
completeEvent,
|
||||||
mAsyncExecutionThread.forget());
|
mAsyncExecutionThread.forget());
|
||||||
}
|
}
|
||||||
|
@ -1252,7 +1283,7 @@ Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Connection::GetConnectionReady(bool *_ready)
|
Connection::GetConnectionReady(bool *_ready)
|
||||||
{
|
{
|
||||||
*_ready = ConnectionReady();
|
*_ready = connectionReady();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,8 +132,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* Mutex used by asynchronous statements to protect state. The mutex is
|
* Mutex used by asynchronous statements to protect state. The mutex is
|
||||||
* declared on the connection object because there is no contention between
|
* declared on the connection object because there is no contention between
|
||||||
* asynchronous statements (they are serialized on mAsyncExecutionThread). It
|
* asynchronous statements (they are serialized on mAsyncExecutionThread).
|
||||||
* also protects mPendingStatements.
|
* Currently protects:
|
||||||
|
* - Connection.mAsyncExecutionThreadShuttingDown
|
||||||
|
* - Connection.mAsyncExecutionThread
|
||||||
|
* - Connection.mConnectionClosed
|
||||||
|
* - AsyncExecuteStatements.mCancelRequested
|
||||||
*/
|
*/
|
||||||
Mutex sharedAsyncExecutionMutex;
|
Mutex sharedAsyncExecutionMutex;
|
||||||
|
|
||||||
|
@ -154,7 +158,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Closes the SQLite database, and warns about any non-finalized statements.
|
* Closes the SQLite database, and warns about any non-finalized statements.
|
||||||
*/
|
*/
|
||||||
nsresult internalClose();
|
nsresult internalClose(sqlite3 *aDBConn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the filename of the connection. Useful for logging.
|
* Obtains the filename of the connection. Useful for logging.
|
||||||
|
@ -164,39 +168,42 @@ public:
|
||||||
/**
|
/**
|
||||||
* Creates an sqlite3 prepared statement object from an SQL string.
|
* Creates an sqlite3 prepared statement object from an SQL string.
|
||||||
*
|
*
|
||||||
|
* @param aNativeConnection
|
||||||
|
* The underlying Sqlite connection to prepare the statement with.
|
||||||
* @param aSQL
|
* @param aSQL
|
||||||
* The SQL statement string to compile.
|
* The SQL statement string to compile.
|
||||||
* @param _stmt
|
* @param _stmt
|
||||||
* New sqlite3_stmt object.
|
* New sqlite3_stmt object.
|
||||||
* @return the result from sqlite3_prepare_v2.
|
* @return the result from sqlite3_prepare_v2.
|
||||||
*/
|
*/
|
||||||
int prepareStatement(const nsCString &aSQL, sqlite3_stmt **_stmt);
|
int prepareStatement(sqlite3* aNativeConnection,
|
||||||
|
const nsCString &aSQL, sqlite3_stmt **_stmt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
|
* Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
|
||||||
* when not on the main thread by waiting until we are notified.
|
* when not on the main thread by waiting until we are notified.
|
||||||
*
|
*
|
||||||
|
* @param aNativeConnection
|
||||||
|
* The underlying Sqlite connection to step the statement with.
|
||||||
* @param aStatement
|
* @param aStatement
|
||||||
* A pointer to a sqlite3_stmt object.
|
* A pointer to a sqlite3_stmt object.
|
||||||
* @return the result from sqlite3_step.
|
* @return the result from sqlite3_step.
|
||||||
*/
|
*/
|
||||||
int stepStatement(sqlite3_stmt* aStatement);
|
int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement);
|
||||||
|
|
||||||
bool ConnectionReady() {
|
bool connectionReady();
|
||||||
return mDBConn != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this connection is currently shutting down.
|
* True if this connection is shutting down but not yet closed.
|
||||||
*
|
|
||||||
* In particular, if |isClosing(true)| returns |true|, any sqlite3 statement
|
|
||||||
* belonging to this connection must be discarded as its memory has already
|
|
||||||
* been released to sqlite3.
|
|
||||||
*
|
|
||||||
* @param aResultOnceClosed
|
|
||||||
* The value to return if closing has completed.
|
|
||||||
*/
|
*/
|
||||||
bool isClosing(bool aResultOnceClosed = false);
|
bool isClosing();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the underlying connection is closed.
|
||||||
|
* Any sqlite resources may be lost when this returns true, so nothing should
|
||||||
|
* try to use them.
|
||||||
|
*/
|
||||||
|
bool isClosed();
|
||||||
|
|
||||||
nsresult initializeClone(Connection *aClone, bool aReadOnly);
|
nsresult initializeClone(Connection *aClone, bool aReadOnly);
|
||||||
|
|
||||||
|
@ -276,10 +283,19 @@ private:
|
||||||
* returns null.
|
* returns null.
|
||||||
*
|
*
|
||||||
* This variable should be accessed while holding the
|
* This variable should be accessed while holding the
|
||||||
* mAsyncExecutionMutex.
|
* sharedAsyncExecutionMutex.
|
||||||
*/
|
*/
|
||||||
bool mAsyncExecutionThreadShuttingDown;
|
bool mAsyncExecutionThreadShuttingDown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true just prior to calling sqlite3_close on the
|
||||||
|
* connection.
|
||||||
|
*
|
||||||
|
* This variable should be accessed while holding the
|
||||||
|
* sharedAsyncExecutionMutex.
|
||||||
|
*/
|
||||||
|
bool mConnectionClosed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks if we have a transaction in progress or not. Access protected by
|
* Tracks if we have a transaction in progress or not. Access protected by
|
||||||
* sharedDBMutex.
|
* sharedDBMutex.
|
||||||
|
|
|
@ -346,7 +346,7 @@ Service::minimizeMemory()
|
||||||
|
|
||||||
for (uint32_t i = 0; i < connections.Length(); i++) {
|
for (uint32_t i = 0; i < connections.Length(); i++) {
|
||||||
nsRefPtr<Connection> conn = connections[i];
|
nsRefPtr<Connection> conn = connections[i];
|
||||||
if (conn->ConnectionReady()) {
|
if (conn->connectionReady()) {
|
||||||
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
|
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
|
||||||
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
|
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
|
||||||
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
|
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
|
||||||
|
@ -914,9 +914,6 @@ Service::Observe(nsISupports *, const char *aTopic, const char16_t *)
|
||||||
anyOpen = false;
|
anyOpen = false;
|
||||||
for (uint32_t i = 0; i < connections.Length(); i++) {
|
for (uint32_t i = 0; i < connections.Length(); i++) {
|
||||||
nsRefPtr<Connection> &conn = connections[i];
|
nsRefPtr<Connection> &conn = connections[i];
|
||||||
|
|
||||||
// While it would be nice to close all connections, we only
|
|
||||||
// check async ones for now.
|
|
||||||
if (conn->isClosing()) {
|
if (conn->isClosing()) {
|
||||||
anyOpen = true;
|
anyOpen = true;
|
||||||
break;
|
break;
|
||||||
|
@ -932,7 +929,7 @@ Service::Observe(nsISupports *, const char *aTopic, const char16_t *)
|
||||||
nsTArray<nsRefPtr<Connection> > connections;
|
nsTArray<nsRefPtr<Connection> > connections;
|
||||||
getConnections(connections);
|
getConnections(connections);
|
||||||
for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
|
for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
|
||||||
if (connections[i]->ConnectionReady()) {
|
if (!connections[i]->isClosed()) {
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,10 +139,12 @@ Statement::initialize(Connection *aDBConnection,
|
||||||
const nsACString &aSQLStatement)
|
const nsACString &aSQLStatement)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aDBConnection, "No database connection given!");
|
MOZ_ASSERT(aDBConnection, "No database connection given!");
|
||||||
|
MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
|
||||||
MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
|
MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
|
||||||
MOZ_ASSERT(aNativeConnection, "No native connection given!");
|
MOZ_ASSERT(aNativeConnection, "No native connection given!");
|
||||||
|
|
||||||
int srv = aDBConnection->prepareStatement(PromiseFlatCString(aSQLStatement),
|
int srv = aDBConnection->prepareStatement(aNativeConnection,
|
||||||
|
PromiseFlatCString(aSQLStatement),
|
||||||
&mDBStatement);
|
&mDBStatement);
|
||||||
if (srv != SQLITE_OK) {
|
if (srv != SQLITE_OK) {
|
||||||
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
PR_LOG(gStorageLog, PR_LOG_ERROR,
|
||||||
|
@ -284,7 +286,8 @@ Statement::getAsyncStatement(sqlite3_stmt **_stmt)
|
||||||
// If we do not yet have a cached async statement, clone our statement now.
|
// If we do not yet have a cached async statement, clone our statement now.
|
||||||
if (!mAsyncStatement) {
|
if (!mAsyncStatement) {
|
||||||
nsDependentCString sql(::sqlite3_sql(mDBStatement));
|
nsDependentCString sql(::sqlite3_sql(mDBStatement));
|
||||||
int rc = mDBConnection->prepareStatement(sql, &mAsyncStatement);
|
int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
|
||||||
|
&mAsyncStatement);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
*_stmt = nullptr;
|
*_stmt = nullptr;
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -356,7 +359,7 @@ Statement::internalFinalize(bool aDestructing)
|
||||||
|
|
||||||
int srv = SQLITE_OK;
|
int srv = SQLITE_OK;
|
||||||
|
|
||||||
if (!mDBConnection->isClosing(true)) {
|
if (!mDBConnection->isClosed()) {
|
||||||
//
|
//
|
||||||
// The connection is still open. While statement finalization and
|
// The connection is still open. While statement finalization and
|
||||||
// closing may, in some cases, take place in two distinct threads,
|
// closing may, in some cases, take place in two distinct threads,
|
||||||
|
@ -632,7 +635,7 @@ Statement::ExecuteStep(bool *_moreResults)
|
||||||
// We have bound, so now we can clear our array.
|
// We have bound, so now we can clear our array.
|
||||||
mParamsArray = nullptr;
|
mParamsArray = nullptr;
|
||||||
}
|
}
|
||||||
int srv = mDBConnection->stepStatement(mDBStatement);
|
int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
|
if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
|
||||||
|
|
|
@ -215,18 +215,17 @@ SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||||
return JOURNAL_DELETE;
|
return JOURNAL_DELETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlockingConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
|
class ConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
|
||||||
bool mDone;
|
bool mDone;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
|
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
|
||||||
BlockingConnectionCloseCallback();
|
ConnectionCloseCallback();
|
||||||
void Spin();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
BlockingConnectionCloseCallback::Complete(nsresult, nsISupports*)
|
ConnectionCloseCallback::Complete(nsresult, nsISupports*)
|
||||||
{
|
{
|
||||||
mDone = true;
|
mDone = true;
|
||||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
|
@ -240,21 +239,14 @@ BlockingConnectionCloseCallback::Complete(nsresult, nsISupports*)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockingConnectionCloseCallback::BlockingConnectionCloseCallback()
|
ConnectionCloseCallback::ConnectionCloseCallback()
|
||||||
: mDone(false)
|
: mDone(false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockingConnectionCloseCallback::Spin() {
|
|
||||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
||||||
while (!mDone) {
|
|
||||||
NS_ProcessNextEvent(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS1(
|
NS_IMPL_ISUPPORTS1(
|
||||||
BlockingConnectionCloseCallback
|
ConnectionCloseCallback
|
||||||
, mozIStorageCompletionCallback
|
, mozIStorageCompletionCallback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1939,12 +1931,11 @@ Database::Shutdown()
|
||||||
);
|
);
|
||||||
DispatchToAsyncThread(event);
|
DispatchToAsyncThread(event);
|
||||||
|
|
||||||
nsRefPtr<BlockingConnectionCloseCallback> closeListener =
|
|
||||||
new BlockingConnectionCloseCallback();
|
|
||||||
(void)mMainConn->AsyncClose(closeListener);
|
|
||||||
closeListener->Spin();
|
|
||||||
|
|
||||||
mClosed = true;
|
mClosed = true;
|
||||||
|
|
||||||
|
nsRefPtr<ConnectionCloseCallback> closeListener =
|
||||||
|
new ConnectionCloseCallback();
|
||||||
|
(void)mMainConn->AsyncClose(closeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Загрузка…
Ссылка в новой задаче