/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozStorageAsyncStatementExecution_h #define mozStorageAsyncStatementExecution_h #include "nscore.h" #include "nsTArray.h" #include "mozilla/Mutex.h" #include "mozilla/TimeStamp.h" #include "mozilla/Attributes.h" #include "nsThreadUtils.h" #include "SQLiteMutex.h" #include "mozIStoragePendingStatement.h" #include "mozIStorageStatementCallback.h" #include "mozStorageHelper.h" struct sqlite3_stmt; namespace mozilla { namespace storage { class Connection; class ResultSet; class StatementData; } // namespace storage } // namespace mozilla namespace mozilla::storage { class AsyncExecuteStatements final : public Runnable, public mozIStoragePendingStatement { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRUNNABLE NS_DECL_MOZISTORAGEPENDINGSTATEMENT /** * Describes the state of execution. */ enum ExecutionState { PENDING = -1, COMPLETED = mozIStorageStatementCallback::REASON_FINISHED, CANCELED = mozIStorageStatementCallback::REASON_CANCELED, ERROR = mozIStorageStatementCallback::REASON_ERROR }; typedef nsTArray StatementDataArray; /** * Executes a statement in the background, and passes results back to the * caller. * * @param aStatements * The statements to execute and possibly bind in the background. * Ownership is transfered from the caller. * @param aConnection * The connection that created the statements to execute. * @param aNativeConnection * The native Sqlite connection that created the statements to execute. * @param aCallback * The callback that is notified of results, completion, and errors. * @param _stmt * The handle to control the execution of the statements. */ static nsresult execute(StatementDataArray&& aStatements, Connection* aConnection, sqlite3* aNativeConnection, mozIStorageStatementCallback* aCallback, mozIStoragePendingStatement** _stmt); /** * Indicates when events on the calling thread should run or not. Certain * events posted back to the calling thread should call this see if they * should run or not. * * @pre mMutex is not held * * @returns true if the event should notify still, false otherwise. */ bool shouldNotify(); /** * Used by notifyComplete(), notifyError() and notifyResults() to notify on * the calling thread. */ nsresult notifyCompleteOnCallingThread(); nsresult notifyErrorOnCallingThread(mozIStorageError* aError); nsresult notifyResultsOnCallingThread(ResultSet* aResultSet); private: AsyncExecuteStatements(StatementDataArray&& aStatements, Connection* aConnection, sqlite3* aNativeConnection, mozIStorageStatementCallback* aCallback); ~AsyncExecuteStatements(); /** * Binds and then executes a given statement until completion, an error * occurs, or we are canceled. If aLastStatement is true, we should set * mState accordingly. * * @pre mMutex is not held * * @param aData * The StatementData to bind, execute, and then process. * @param aLastStatement * Indicates if this is the last statement or not. If it is, we have * to set the proper state. * @returns true if we should continue to process statements, false otherwise. */ bool bindExecuteAndProcessStatement(StatementData& aData, bool aLastStatement); /** * Executes a given statement until completion, an error occurs, or we are * canceled. If aLastStatement is true, we should set mState accordingly. * * @pre mMutex is not held * * @param aData * The StatementData to execute, and then process. * @param aLastStatement * Indicates if this is the last statement or not. If it is, we have * to set the proper state. * @returns true if we should continue to process statements, false otherwise. */ bool executeAndProcessStatement(StatementData& aData, bool aLastStatement); /** * Executes a statement to completion, properly handling any error conditions. * * @pre mMutex is not held * * @param aData * The StatementData to execute to completion. * @returns true if results were obtained, false otherwise. */ bool executeStatement(StatementData& aData); /** * Builds a result set up with a row from a given statement. If we meet the * right criteria, go ahead and notify about this results too. * * @pre mMutex is not held * * @param aStatement * The statement to get the row data from. */ nsresult buildAndNotifyResults(sqlite3_stmt* aStatement); /** * Notifies callback about completion, and does any necessary cleanup. * * @pre mMutex is not held */ nsresult notifyComplete(); /** * Notifies callback about an error. * * @pre mMutex is not held * @pre mDBMutex is not held * * @param aErrorCode * The error code defined in mozIStorageError for the error. * @param aMessage * The error string, if any. * @param aError * The error object to notify the caller with. */ nsresult notifyError(int32_t aErrorCode, const char* aMessage); nsresult notifyError(mozIStorageError* aError); /** * Notifies the callback about a result set. * * @pre mMutex is not held */ nsresult notifyResults(); /** * Tests whether the current statements should be wrapped in an explicit * transaction. * * @return true if an explicit transaction is needed, false otherwise. */ bool statementsNeedTransaction(); StatementDataArray mStatements; RefPtr mConnection; sqlite3* mNativeConnection; bool mHasTransaction; // Note, this may not be a threadsafe object - never addref/release off // the calling thread. We take a reference when this is created, and // release it in the CompletionNotifier::Run() call back to this thread. nsCOMPtr mCallback; nsCOMPtr mCallingThread; RefPtr mResultSet; /** * The maximum amount of time we want to wait between results. Defined by * MAX_MILLISECONDS_BETWEEN_RESULTS and set at construction. */ const TimeDuration mMaxWait; /** * The start time since our last set of results. */ TimeStamp mIntervalStart; /** * Indicates our state of execution. */ ExecutionState mState; /** * Indicates if we should try to cancel at a cancelation point. */ bool mCancelRequested; /** * This is the mutex that protects our state from changing between threads. * This includes the following variables: * - mCancelRequested is only set on the calling thread while the lock is * held. It is always read from within the lock on the background thread, * but not on the calling thread (see shouldNotify for why). */ Mutex& mMutex; /** * The wrapped SQLite recursive connection mutex. We use it whenever we call * sqlite3_step and care about having reliable error messages. By taking it * prior to the call and holding it until the point where we no longer care * about the error message, the user gets reliable error messages. */ SQLiteMutex& mDBMutex; }; } // namespace mozilla::storage #endif // mozStorageAsyncStatementExecution_h