1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/src/mozStorageAsyncStatementExecution.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,646 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsAutoPtr.h" 1.11 + 1.12 +#include "sqlite3.h" 1.13 + 1.14 +#include "mozIStorageStatementCallback.h" 1.15 +#include "mozStorageBindingParams.h" 1.16 +#include "mozStorageHelper.h" 1.17 +#include "mozStorageResultSet.h" 1.18 +#include "mozStorageRow.h" 1.19 +#include "mozStorageConnection.h" 1.20 +#include "mozStorageError.h" 1.21 +#include "mozStoragePrivateHelpers.h" 1.22 +#include "mozStorageStatementData.h" 1.23 +#include "mozStorageAsyncStatementExecution.h" 1.24 + 1.25 +#include "mozilla/Telemetry.h" 1.26 + 1.27 +namespace mozilla { 1.28 +namespace storage { 1.29 + 1.30 +/** 1.31 + * The following constants help batch rows into result sets. 1.32 + * MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that 1.33 + * takes less than 200 milliseconds is considered to feel instantaneous to end 1.34 + * users. MAX_ROWS_PER_RESULT was arbitrarily chosen to reduce the number of 1.35 + * dispatches to calling thread, while also providing reasonably-sized sets of 1.36 + * data for consumers. Both of these constants are used because we assume that 1.37 + * consumers are trying to avoid blocking their execution thread for long 1.38 + * periods of time, and dispatching many small events to the calling thread will 1.39 + * end up blocking it. 1.40 + */ 1.41 +#define MAX_MILLISECONDS_BETWEEN_RESULTS 75 1.42 +#define MAX_ROWS_PER_RESULT 15 1.43 + 1.44 +//////////////////////////////////////////////////////////////////////////////// 1.45 +//// Local Classes 1.46 + 1.47 +namespace { 1.48 + 1.49 +typedef AsyncExecuteStatements::ExecutionState ExecutionState; 1.50 +typedef AsyncExecuteStatements::StatementDataArray StatementDataArray; 1.51 + 1.52 +/** 1.53 + * Notifies a callback with a result set. 1.54 + */ 1.55 +class CallbackResultNotifier : public nsRunnable 1.56 +{ 1.57 +public: 1.58 + CallbackResultNotifier(mozIStorageStatementCallback *aCallback, 1.59 + mozIStorageResultSet *aResults, 1.60 + AsyncExecuteStatements *aEventStatus) : 1.61 + mCallback(aCallback) 1.62 + , mResults(aResults) 1.63 + , mEventStatus(aEventStatus) 1.64 + { 1.65 + } 1.66 + 1.67 + NS_IMETHOD Run() 1.68 + { 1.69 + NS_ASSERTION(mCallback, "Trying to notify about results without a callback!"); 1.70 + 1.71 + if (mEventStatus->shouldNotify()) { 1.72 + // Hold a strong reference to the callback while notifying it, so that if 1.73 + // it spins the event loop, the callback won't be released and freed out 1.74 + // from under us. 1.75 + nsCOMPtr<mozIStorageStatementCallback> callback = 1.76 + do_QueryInterface(mCallback); 1.77 + 1.78 + (void)mCallback->HandleResult(mResults); 1.79 + } 1.80 + 1.81 + return NS_OK; 1.82 + } 1.83 + 1.84 +private: 1.85 + mozIStorageStatementCallback *mCallback; 1.86 + nsCOMPtr<mozIStorageResultSet> mResults; 1.87 + nsRefPtr<AsyncExecuteStatements> mEventStatus; 1.88 +}; 1.89 + 1.90 +/** 1.91 + * Notifies the calling thread that an error has occurred. 1.92 + */ 1.93 +class ErrorNotifier : public nsRunnable 1.94 +{ 1.95 +public: 1.96 + ErrorNotifier(mozIStorageStatementCallback *aCallback, 1.97 + mozIStorageError *aErrorObj, 1.98 + AsyncExecuteStatements *aEventStatus) : 1.99 + mCallback(aCallback) 1.100 + , mErrorObj(aErrorObj) 1.101 + , mEventStatus(aEventStatus) 1.102 + { 1.103 + } 1.104 + 1.105 + NS_IMETHOD Run() 1.106 + { 1.107 + if (mEventStatus->shouldNotify() && mCallback) { 1.108 + // Hold a strong reference to the callback while notifying it, so that if 1.109 + // it spins the event loop, the callback won't be released and freed out 1.110 + // from under us. 1.111 + nsCOMPtr<mozIStorageStatementCallback> callback = 1.112 + do_QueryInterface(mCallback); 1.113 + 1.114 + (void)mCallback->HandleError(mErrorObj); 1.115 + } 1.116 + 1.117 + return NS_OK; 1.118 + } 1.119 + 1.120 +private: 1.121 + mozIStorageStatementCallback *mCallback; 1.122 + nsCOMPtr<mozIStorageError> mErrorObj; 1.123 + nsRefPtr<AsyncExecuteStatements> mEventStatus; 1.124 +}; 1.125 + 1.126 +/** 1.127 + * Notifies the calling thread that the statement has finished executing. Takes 1.128 + * ownership of the StatementData so it is released on the proper thread. 1.129 + */ 1.130 +class CompletionNotifier : public nsRunnable 1.131 +{ 1.132 +public: 1.133 + /** 1.134 + * This takes ownership of the callback and the StatementData. They are 1.135 + * released on the thread this is dispatched to (which should always be the 1.136 + * calling thread). 1.137 + */ 1.138 + CompletionNotifier(mozIStorageStatementCallback *aCallback, 1.139 + ExecutionState aReason) 1.140 + : mCallback(aCallback) 1.141 + , mReason(aReason) 1.142 + { 1.143 + } 1.144 + 1.145 + NS_IMETHOD Run() 1.146 + { 1.147 + if (mCallback) { 1.148 + (void)mCallback->HandleCompletion(mReason); 1.149 + NS_RELEASE(mCallback); 1.150 + } 1.151 + 1.152 + return NS_OK; 1.153 + } 1.154 + 1.155 +private: 1.156 + mozIStorageStatementCallback *mCallback; 1.157 + ExecutionState mReason; 1.158 +}; 1.159 + 1.160 +} // anonymous namespace 1.161 + 1.162 +//////////////////////////////////////////////////////////////////////////////// 1.163 +//// AsyncExecuteStatements 1.164 + 1.165 +/* static */ 1.166 +nsresult 1.167 +AsyncExecuteStatements::execute(StatementDataArray &aStatements, 1.168 + Connection *aConnection, 1.169 + sqlite3 *aNativeConnection, 1.170 + mozIStorageStatementCallback *aCallback, 1.171 + mozIStoragePendingStatement **_stmt) 1.172 +{ 1.173 + // Create our event to run in the background 1.174 + nsRefPtr<AsyncExecuteStatements> event = 1.175 + new AsyncExecuteStatements(aStatements, aConnection, aNativeConnection, 1.176 + aCallback); 1.177 + NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); 1.178 + 1.179 + // Dispatch it to the background 1.180 + nsIEventTarget *target = aConnection->getAsyncExecutionTarget(); 1.181 + 1.182 + // If we don't have a valid target, this is a bug somewhere else. In the past, 1.183 + // this assert found cases where a Run method would schedule a new statement 1.184 + // without checking if asyncClose had been called. The caller must prevent 1.185 + // that from happening or, if the work is not critical, just avoid creating 1.186 + // the new statement during shutdown. See bug 718449 for an example. 1.187 + MOZ_ASSERT(target); 1.188 + if (!target) { 1.189 + return NS_ERROR_NOT_AVAILABLE; 1.190 + } 1.191 + 1.192 + nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL); 1.193 + NS_ENSURE_SUCCESS(rv, rv); 1.194 + 1.195 + // Return it as the pending statement object and track it. 1.196 + NS_ADDREF(*_stmt = event); 1.197 + return NS_OK; 1.198 +} 1.199 + 1.200 +AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements, 1.201 + Connection *aConnection, 1.202 + sqlite3 *aNativeConnection, 1.203 + mozIStorageStatementCallback *aCallback) 1.204 +: mConnection(aConnection) 1.205 +, mNativeConnection(aNativeConnection) 1.206 +, mHasTransaction(false) 1.207 +, mCallback(aCallback) 1.208 +, mCallingThread(::do_GetCurrentThread()) 1.209 +, mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS)) 1.210 +, mIntervalStart(TimeStamp::Now()) 1.211 +, mState(PENDING) 1.212 +, mCancelRequested(false) 1.213 +, mMutex(aConnection->sharedAsyncExecutionMutex) 1.214 +, mDBMutex(aConnection->sharedDBMutex) 1.215 + , mRequestStartDate(TimeStamp::Now()) 1.216 +{ 1.217 + (void)mStatements.SwapElements(aStatements); 1.218 + NS_ASSERTION(mStatements.Length(), "We weren't given any statements!"); 1.219 + NS_IF_ADDREF(mCallback); 1.220 +} 1.221 + 1.222 +AsyncExecuteStatements::~AsyncExecuteStatements() 1.223 +{ 1.224 + MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point"); 1.225 +} 1.226 + 1.227 +bool 1.228 +AsyncExecuteStatements::shouldNotify() 1.229 +{ 1.230 +#ifdef DEBUG 1.231 + mMutex.AssertNotCurrentThreadOwns(); 1.232 + 1.233 + bool onCallingThread = false; 1.234 + (void)mCallingThread->IsOnCurrentThread(&onCallingThread); 1.235 + NS_ASSERTION(onCallingThread, "runEvent not running on the calling thread!"); 1.236 +#endif 1.237 + 1.238 + // We do not need to acquire mMutex here because it can only ever be written 1.239 + // to on the calling thread, and the only thread that can call us is the 1.240 + // calling thread, so we know that our access is serialized. 1.241 + return !mCancelRequested; 1.242 +} 1.243 + 1.244 +bool 1.245 +AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData, 1.246 + bool aLastStatement) 1.247 +{ 1.248 + mMutex.AssertNotCurrentThreadOwns(); 1.249 + 1.250 + sqlite3_stmt *aStatement = nullptr; 1.251 + // This cannot fail; we are only called if it's available. 1.252 + (void)aData.getSqliteStatement(&aStatement); 1.253 + NS_ASSERTION(aStatement, "You broke the code; do not call here like that!"); 1.254 + BindingParamsArray *paramsArray(aData); 1.255 + 1.256 + // Iterate through all of our parameters, bind them, and execute. 1.257 + bool continueProcessing = true; 1.258 + BindingParamsArray::iterator itr = paramsArray->begin(); 1.259 + BindingParamsArray::iterator end = paramsArray->end(); 1.260 + while (itr != end && continueProcessing) { 1.261 + // Bind the data to our statement. 1.262 + nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = 1.263 + do_QueryInterface(*itr); 1.264 + nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement); 1.265 + if (error) { 1.266 + // Set our error state. 1.267 + mState = ERROR; 1.268 + 1.269 + // And notify. 1.270 + (void)notifyError(error); 1.271 + return false; 1.272 + } 1.273 + 1.274 + // Advance our iterator, execute, and then process the statement. 1.275 + itr++; 1.276 + bool lastStatement = aLastStatement && itr == end; 1.277 + continueProcessing = executeAndProcessStatement(aStatement, lastStatement); 1.278 + 1.279 + // Always reset our statement. 1.280 + (void)::sqlite3_reset(aStatement); 1.281 + } 1.282 + 1.283 + return continueProcessing; 1.284 +} 1.285 + 1.286 +bool 1.287 +AsyncExecuteStatements::executeAndProcessStatement(sqlite3_stmt *aStatement, 1.288 + bool aLastStatement) 1.289 +{ 1.290 + mMutex.AssertNotCurrentThreadOwns(); 1.291 + 1.292 + // Execute our statement 1.293 + bool hasResults; 1.294 + do { 1.295 + hasResults = executeStatement(aStatement); 1.296 + 1.297 + // If we had an error, bail. 1.298 + if (mState == ERROR) 1.299 + return false; 1.300 + 1.301 + // If we have been canceled, there is no point in going on... 1.302 + { 1.303 + MutexAutoLock lockedScope(mMutex); 1.304 + if (mCancelRequested) { 1.305 + mState = CANCELED; 1.306 + return false; 1.307 + } 1.308 + } 1.309 + 1.310 + // Build our result set and notify if we got anything back and have a 1.311 + // callback to notify. 1.312 + if (mCallback && hasResults && 1.313 + NS_FAILED(buildAndNotifyResults(aStatement))) { 1.314 + // We had an error notifying, so we notify on error and stop processing. 1.315 + mState = ERROR; 1.316 + 1.317 + // Notify, and stop processing statements. 1.318 + (void)notifyError(mozIStorageError::ERROR, 1.319 + "An error occurred while notifying about results"); 1.320 + 1.321 + return false; 1.322 + } 1.323 + } while (hasResults); 1.324 + 1.325 +#ifdef DEBUG 1.326 + // Check to make sure that this statement was smart about what it did. 1.327 + checkAndLogStatementPerformance(aStatement); 1.328 +#endif 1.329 + 1.330 + // If we are done, we need to set our state accordingly while we still hold 1.331 + // our mutex. We would have already returned if we were canceled or had 1.332 + // an error at this point. 1.333 + if (aLastStatement) 1.334 + mState = COMPLETED; 1.335 + 1.336 + return true; 1.337 +} 1.338 + 1.339 +bool 1.340 +AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement) 1.341 +{ 1.342 + mMutex.AssertNotCurrentThreadOwns(); 1.343 + Telemetry::AutoTimer<Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_MS> finallySendExecutionDuration(mRequestStartDate); 1.344 + while (true) { 1.345 + // lock the sqlite mutex so sqlite3_errmsg cannot change 1.346 + SQLiteMutexAutoLock lockedScope(mDBMutex); 1.347 + 1.348 + int rc = mConnection->stepStatement(mNativeConnection, aStatement); 1.349 + // Stop if we have no more results. 1.350 + if (rc == SQLITE_DONE) 1.351 + { 1.352 + Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true); 1.353 + return false; 1.354 + } 1.355 + 1.356 + // If we got results, we can return now. 1.357 + if (rc == SQLITE_ROW) 1.358 + { 1.359 + Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true); 1.360 + return true; 1.361 + } 1.362 + 1.363 + // Some errors are not fatal, and we can handle them and continue. 1.364 + if (rc == SQLITE_BUSY) { 1.365 + // Don't hold the lock while we call outside our module. 1.366 + SQLiteMutexAutoUnlock unlockedScope(mDBMutex); 1.367 + 1.368 + // Yield, and try again 1.369 + (void)::PR_Sleep(PR_INTERVAL_NO_WAIT); 1.370 + continue; 1.371 + } 1.372 + 1.373 + // Set an error state. 1.374 + mState = ERROR; 1.375 + Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, false); 1.376 + 1.377 + // Construct the error message before giving up the mutex (which we cannot 1.378 + // hold during the call to notifyError). 1.379 + nsCOMPtr<mozIStorageError> errorObj( 1.380 + new Error(rc, ::sqlite3_errmsg(mNativeConnection)) 1.381 + ); 1.382 + // We cannot hold the DB mutex while calling notifyError. 1.383 + SQLiteMutexAutoUnlock unlockedScope(mDBMutex); 1.384 + (void)notifyError(errorObj); 1.385 + 1.386 + // Finally, indicate that we should stop processing. 1.387 + return false; 1.388 + } 1.389 +} 1.390 + 1.391 +nsresult 1.392 +AsyncExecuteStatements::buildAndNotifyResults(sqlite3_stmt *aStatement) 1.393 +{ 1.394 + NS_ASSERTION(mCallback, "Trying to dispatch results without a callback!"); 1.395 + mMutex.AssertNotCurrentThreadOwns(); 1.396 + 1.397 + // Build result object if we need it. 1.398 + if (!mResultSet) 1.399 + mResultSet = new ResultSet(); 1.400 + NS_ENSURE_TRUE(mResultSet, NS_ERROR_OUT_OF_MEMORY); 1.401 + 1.402 + nsRefPtr<Row> row(new Row()); 1.403 + NS_ENSURE_TRUE(row, NS_ERROR_OUT_OF_MEMORY); 1.404 + 1.405 + nsresult rv = row->initialize(aStatement); 1.406 + NS_ENSURE_SUCCESS(rv, rv); 1.407 + 1.408 + rv = mResultSet->add(row); 1.409 + NS_ENSURE_SUCCESS(rv, rv); 1.410 + 1.411 + // If we have hit our maximum number of allowed results, or if we have hit 1.412 + // the maximum amount of time we want to wait for results, notify the 1.413 + // calling thread about it. 1.414 + TimeStamp now = TimeStamp::Now(); 1.415 + TimeDuration delta = now - mIntervalStart; 1.416 + if (mResultSet->rows() >= MAX_ROWS_PER_RESULT || delta > mMaxWait) { 1.417 + // Notify the caller 1.418 + rv = notifyResults(); 1.419 + if (NS_FAILED(rv)) 1.420 + return NS_OK; // we'll try again with the next result 1.421 + 1.422 + // Reset our start time 1.423 + mIntervalStart = now; 1.424 + } 1.425 + 1.426 + return NS_OK; 1.427 +} 1.428 + 1.429 +nsresult 1.430 +AsyncExecuteStatements::notifyComplete() 1.431 +{ 1.432 + mMutex.AssertNotCurrentThreadOwns(); 1.433 + NS_ASSERTION(mState != PENDING, 1.434 + "Still in a pending state when calling Complete!"); 1.435 + 1.436 + // Reset our statements before we try to commit or rollback. If we are 1.437 + // canceling and have statements that think they have pending work, the 1.438 + // rollback will fail. 1.439 + for (uint32_t i = 0; i < mStatements.Length(); i++) 1.440 + mStatements[i].reset(); 1.441 + 1.442 + // Release references to the statement data as soon as possible. If this 1.443 + // is the last reference, statements will be finalized immediately on the 1.444 + // async thread, hence avoiding several bounces between threads and possible 1.445 + // race conditions with AsyncClose(). 1.446 + mStatements.Clear(); 1.447 + 1.448 + // Handle our transaction, if we have one 1.449 + if (mHasTransaction) { 1.450 + if (mState == COMPLETED) { 1.451 + nsresult rv = mConnection->commitTransactionInternal(mNativeConnection); 1.452 + if (NS_FAILED(rv)) { 1.453 + mState = ERROR; 1.454 + (void)notifyError(mozIStorageError::ERROR, 1.455 + "Transaction failed to commit"); 1.456 + } 1.457 + } 1.458 + else { 1.459 + NS_WARN_IF(NS_FAILED(mConnection->rollbackTransactionInternal(mNativeConnection))); 1.460 + } 1.461 + mHasTransaction = false; 1.462 + } 1.463 + 1.464 + // Always generate a completion notification; it is what guarantees that our 1.465 + // destruction does not happen here on the async thread. 1.466 + nsRefPtr<CompletionNotifier> completionEvent = 1.467 + new CompletionNotifier(mCallback, mState); 1.468 + 1.469 + // We no longer own mCallback (the CompletionNotifier takes ownership). 1.470 + mCallback = nullptr; 1.471 + 1.472 + (void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL); 1.473 + 1.474 + return NS_OK; 1.475 +} 1.476 + 1.477 +nsresult 1.478 +AsyncExecuteStatements::notifyError(int32_t aErrorCode, 1.479 + const char *aMessage) 1.480 +{ 1.481 + mMutex.AssertNotCurrentThreadOwns(); 1.482 + mDBMutex.assertNotCurrentThreadOwns(); 1.483 + 1.484 + if (!mCallback) 1.485 + return NS_OK; 1.486 + 1.487 + nsCOMPtr<mozIStorageError> errorObj(new Error(aErrorCode, aMessage)); 1.488 + NS_ENSURE_TRUE(errorObj, NS_ERROR_OUT_OF_MEMORY); 1.489 + 1.490 + return notifyError(errorObj); 1.491 +} 1.492 + 1.493 +nsresult 1.494 +AsyncExecuteStatements::notifyError(mozIStorageError *aError) 1.495 +{ 1.496 + mMutex.AssertNotCurrentThreadOwns(); 1.497 + mDBMutex.assertNotCurrentThreadOwns(); 1.498 + 1.499 + if (!mCallback) 1.500 + return NS_OK; 1.501 + 1.502 + nsRefPtr<ErrorNotifier> notifier = 1.503 + new ErrorNotifier(mCallback, aError, this); 1.504 + NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY); 1.505 + 1.506 + return mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL); 1.507 +} 1.508 + 1.509 +nsresult 1.510 +AsyncExecuteStatements::notifyResults() 1.511 +{ 1.512 + mMutex.AssertNotCurrentThreadOwns(); 1.513 + NS_ASSERTION(mCallback, "notifyResults called without a callback!"); 1.514 + 1.515 + nsRefPtr<CallbackResultNotifier> notifier = 1.516 + new CallbackResultNotifier(mCallback, mResultSet, this); 1.517 + NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY); 1.518 + 1.519 + nsresult rv = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL); 1.520 + if (NS_SUCCEEDED(rv)) 1.521 + mResultSet = nullptr; // we no longer own it on success 1.522 + return rv; 1.523 +} 1.524 + 1.525 +NS_IMPL_ISUPPORTS( 1.526 + AsyncExecuteStatements, 1.527 + nsIRunnable, 1.528 + mozIStoragePendingStatement 1.529 +) 1.530 + 1.531 +bool 1.532 +AsyncExecuteStatements::statementsNeedTransaction() 1.533 +{ 1.534 + // If there is more than one write statement, run in a transaction. 1.535 + // Additionally, if we have only one statement but it needs a transaction, due 1.536 + // to multiple BindingParams, we will wrap it in one. 1.537 + for (uint32_t i = 0, transactionsCount = 0; i < mStatements.Length(); ++i) { 1.538 + transactionsCount += mStatements[i].needsTransaction(); 1.539 + if (transactionsCount > 1) { 1.540 + return true; 1.541 + } 1.542 + } 1.543 + return false; 1.544 +} 1.545 + 1.546 +//////////////////////////////////////////////////////////////////////////////// 1.547 +//// mozIStoragePendingStatement 1.548 + 1.549 +NS_IMETHODIMP 1.550 +AsyncExecuteStatements::Cancel() 1.551 +{ 1.552 +#ifdef DEBUG 1.553 + bool onCallingThread = false; 1.554 + (void)mCallingThread->IsOnCurrentThread(&onCallingThread); 1.555 + NS_ASSERTION(onCallingThread, "Not canceling from the calling thread!"); 1.556 +#endif 1.557 + 1.558 + // If we have already canceled, we have an error, but always indicate that 1.559 + // we are trying to cancel. 1.560 + NS_ENSURE_FALSE(mCancelRequested, NS_ERROR_UNEXPECTED); 1.561 + 1.562 + { 1.563 + MutexAutoLock lockedScope(mMutex); 1.564 + 1.565 + // We need to indicate that we want to try and cancel now. 1.566 + mCancelRequested = true; 1.567 + } 1.568 + 1.569 + return NS_OK; 1.570 +} 1.571 + 1.572 +//////////////////////////////////////////////////////////////////////////////// 1.573 +//// nsIRunnable 1.574 + 1.575 +NS_IMETHODIMP 1.576 +AsyncExecuteStatements::Run() 1.577 +{ 1.578 + MOZ_ASSERT(!mConnection->isClosed()); 1.579 + 1.580 + // Do not run if we have been canceled. 1.581 + { 1.582 + MutexAutoLock lockedScope(mMutex); 1.583 + if (mCancelRequested) 1.584 + mState = CANCELED; 1.585 + } 1.586 + if (mState == CANCELED) 1.587 + return notifyComplete(); 1.588 + 1.589 + if (statementsNeedTransaction()) { 1.590 + Connection* rawConnection = static_cast<Connection*>(mConnection.get()); 1.591 + if (NS_SUCCEEDED(mConnection->beginTransactionInternal(mNativeConnection, 1.592 + mozIStorageConnection::TRANSACTION_IMMEDIATE))) { 1.593 + mHasTransaction = true; 1.594 + } 1.595 +#ifdef DEBUG 1.596 + else { 1.597 + NS_WARNING("Unable to create a transaction for async execution."); 1.598 + } 1.599 +#endif 1.600 + } 1.601 + 1.602 + // Execute each statement, giving the callback results if it returns any. 1.603 + for (uint32_t i = 0; i < mStatements.Length(); i++) { 1.604 + bool finished = (i == (mStatements.Length() - 1)); 1.605 + 1.606 + sqlite3_stmt *stmt; 1.607 + { // lock the sqlite mutex so sqlite3_errmsg cannot change 1.608 + SQLiteMutexAutoLock lockedScope(mDBMutex); 1.609 + 1.610 + int rc = mStatements[i].getSqliteStatement(&stmt); 1.611 + if (rc != SQLITE_OK) { 1.612 + // Set our error state. 1.613 + mState = ERROR; 1.614 + 1.615 + // Build the error object; can't call notifyError with the lock held 1.616 + nsCOMPtr<mozIStorageError> errorObj( 1.617 + new Error(rc, ::sqlite3_errmsg(mNativeConnection)) 1.618 + ); 1.619 + { 1.620 + // We cannot hold the DB mutex and call notifyError. 1.621 + SQLiteMutexAutoUnlock unlockedScope(mDBMutex); 1.622 + (void)notifyError(errorObj); 1.623 + } 1.624 + break; 1.625 + } 1.626 + } 1.627 + 1.628 + // If we have parameters to bind, bind them, execute, and process. 1.629 + if (mStatements[i].hasParametersToBeBound()) { 1.630 + if (!bindExecuteAndProcessStatement(mStatements[i], finished)) 1.631 + break; 1.632 + } 1.633 + // Otherwise, just execute and process the statement. 1.634 + else if (!executeAndProcessStatement(stmt, finished)) { 1.635 + break; 1.636 + } 1.637 + } 1.638 + 1.639 + // If we still have results that we haven't notified about, take care of 1.640 + // them now. 1.641 + if (mResultSet) 1.642 + (void)notifyResults(); 1.643 + 1.644 + // Notify about completion 1.645 + return notifyComplete(); 1.646 +} 1.647 + 1.648 +} // namespace storage 1.649 +} // namespace mozilla