LCOV - code coverage report
Current view: directory - storage/src - mozStorageAsyncStatementExecution.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 222 203 91.4 %
Date: 2012-06-02 Functions: 29 28 96.6 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * the Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2008
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
      25                 :  *   David Rajchenbach-Teller <dteller@mozilla.com> (added Telemetry)
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "nsAutoPtr.h"
      42                 : 
      43                 : #include "sqlite3.h"
      44                 : 
      45                 : #include "mozIStorageStatementCallback.h"
      46                 : #include "mozStorageBindingParams.h"
      47                 : #include "mozStorageHelper.h"
      48                 : #include "mozStorageResultSet.h"
      49                 : #include "mozStorageRow.h"
      50                 : #include "mozStorageConnection.h"
      51                 : #include "mozStorageError.h"
      52                 : #include "mozStoragePrivateHelpers.h"
      53                 : #include "mozStorageStatementData.h"
      54                 : #include "mozStorageAsyncStatementExecution.h"
      55                 : 
      56                 : #include "mozilla/Telemetry.h"
      57                 : 
      58                 : namespace mozilla {
      59                 : namespace storage {
      60                 : 
      61                 : /**
      62                 :  * The following constants help batch rows into result sets.
      63                 :  * MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that
      64                 :  * takes less than 200 milliseconds is considered to feel instantaneous to end
      65                 :  * users.  MAX_ROWS_PER_RESULT was arbitrarily chosen to reduce the number of
      66                 :  * dispatches to calling thread, while also providing reasonably-sized sets of
      67                 :  * data for consumers.  Both of these constants are used because we assume that
      68                 :  * consumers are trying to avoid blocking their execution thread for long
      69                 :  * periods of time, and dispatching many small events to the calling thread will
      70                 :  * end up blocking it.
      71                 :  */
      72                 : #define MAX_MILLISECONDS_BETWEEN_RESULTS 75
      73                 : #define MAX_ROWS_PER_RESULT 15
      74                 : 
      75                 : ////////////////////////////////////////////////////////////////////////////////
      76                 : //// Local Classes
      77                 : 
      78                 : namespace {
      79                 : 
      80                 : typedef AsyncExecuteStatements::ExecutionState ExecutionState;
      81                 : typedef AsyncExecuteStatements::StatementDataArray StatementDataArray;
      82                 : 
      83                 : /**
      84                 :  * Notifies a callback with a result set.
      85                 :  */
      86                 : class CallbackResultNotifier : public nsRunnable
      87           41420 : {
      88                 : public:
      89           10355 :   CallbackResultNotifier(mozIStorageStatementCallback *aCallback,
      90                 :                          mozIStorageResultSet *aResults,
      91                 :                          AsyncExecuteStatements *aEventStatus) :
      92                 :       mCallback(aCallback)
      93                 :     , mResults(aResults)
      94           10355 :     , mEventStatus(aEventStatus)
      95                 :   {
      96           10355 :   }
      97                 : 
      98           10355 :   NS_IMETHOD Run()
      99                 :   {
     100           10355 :     NS_ASSERTION(mCallback, "Trying to notify about results without a callback!");
     101                 : 
     102           10355 :     if (mEventStatus->shouldNotify()) {
     103                 :       // Hold a strong reference to the callback while notifying it, so that if
     104                 :       // it spins the event loop, the callback won't be released and freed out
     105                 :       // from under us.
     106                 :       nsCOMPtr<mozIStorageStatementCallback> callback =
     107           20420 :         do_QueryInterface(mCallback);
     108                 : 
     109           10210 :       (void)mCallback->HandleResult(mResults);
     110                 :     }
     111                 : 
     112           10355 :     return NS_OK;
     113                 :   }
     114                 : 
     115                 : private:
     116                 :   mozIStorageStatementCallback *mCallback;
     117                 :   nsCOMPtr<mozIStorageResultSet> mResults;
     118                 :   nsRefPtr<AsyncExecuteStatements> mEventStatus;
     119                 : };
     120                 : 
     121                 : /**
     122                 :  * Notifies the calling thread that an error has occurred.
     123                 :  */
     124                 : class ErrorNotifier : public nsRunnable
     125              96 : {
     126                 : public:
     127              24 :   ErrorNotifier(mozIStorageStatementCallback *aCallback,
     128                 :                 mozIStorageError *aErrorObj,
     129                 :                 AsyncExecuteStatements *aEventStatus) :
     130                 :       mCallback(aCallback)
     131                 :     , mErrorObj(aErrorObj)
     132              24 :     , mEventStatus(aEventStatus)
     133                 :   {
     134              24 :   }
     135                 : 
     136              24 :   NS_IMETHOD Run()
     137                 :   {
     138              24 :     if (mEventStatus->shouldNotify() && mCallback) {
     139                 :       // Hold a strong reference to the callback while notifying it, so that if
     140                 :       // it spins the event loop, the callback won't be released and freed out
     141                 :       // from under us.
     142                 :       nsCOMPtr<mozIStorageStatementCallback> callback =
     143              48 :         do_QueryInterface(mCallback);
     144                 : 
     145              24 :       (void)mCallback->HandleError(mErrorObj);
     146                 :     }
     147                 : 
     148              24 :     return NS_OK;
     149                 :   }
     150                 : 
     151                 : private:
     152                 :   mozIStorageStatementCallback *mCallback;
     153                 :   nsCOMPtr<mozIStorageError> mErrorObj;
     154                 :   nsRefPtr<AsyncExecuteStatements> mEventStatus;
     155                 : };
     156                 : 
     157                 : /**
     158                 :  * Notifies the calling thread that the statement has finished executing.  Takes
     159                 :  * ownership of the StatementData so it is released on the proper thread.
     160                 :  */
     161                 : class CompletionNotifier : public nsRunnable
     162          171332 : {
     163                 : public:
     164                 :   /**
     165                 :    * This takes ownership of the callback and the StatementData.  They are
     166                 :    * released on the thread this is dispatched to (which should always be the
     167                 :    * calling thread).
     168                 :    */
     169           42833 :   CompletionNotifier(mozIStorageStatementCallback *aCallback,
     170                 :                      ExecutionState aReason,
     171                 :                      StatementDataArray &aStatementData)
     172                 :     : mCallback(aCallback)
     173           42833 :     , mReason(aReason)
     174                 :   {
     175           42833 :     mStatementData.SwapElements(aStatementData);
     176           42833 :   }
     177                 : 
     178           42833 :   NS_IMETHOD Run()
     179                 :   {
     180           42833 :     if (mCallback) {
     181           41351 :       (void)mCallback->HandleCompletion(mReason);
     182           41351 :       NS_RELEASE(mCallback);
     183                 :     }
     184                 : 
     185                 :     // The async thread could still hold onto a reference to us, so we need to
     186                 :     // make sure we release our reference to the StatementData now in case our
     187                 :     // destructor happens in a different thread.
     188           42833 :     mStatementData.Clear();
     189                 : 
     190           42833 :     return NS_OK;
     191                 :   }
     192                 : 
     193                 : private:
     194                 :   StatementDataArray mStatementData;
     195                 :   mozIStorageStatementCallback *mCallback;
     196                 :   ExecutionState mReason;
     197                 : };
     198                 : 
     199                 : } // anonymous namespace
     200                 : 
     201                 : ////////////////////////////////////////////////////////////////////////////////
     202                 : //// AsyncExecuteStatements
     203                 : 
     204                 : /* static */
     205                 : nsresult
     206           42833 : AsyncExecuteStatements::execute(StatementDataArray &aStatements,
     207                 :                                 Connection *aConnection,
     208                 :                                 mozIStorageStatementCallback *aCallback,
     209                 :                                 mozIStoragePendingStatement **_stmt)
     210                 : {
     211                 :   // Create our event to run in the background
     212                 :   nsRefPtr<AsyncExecuteStatements> event =
     213           85666 :     new AsyncExecuteStatements(aStatements, aConnection, aCallback);
     214           42833 :   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
     215                 : 
     216                 :   // Dispatch it to the background
     217           42833 :   nsIEventTarget *target = aConnection->getAsyncExecutionTarget();
     218                 : 
     219                 :   // If we don't have a valid target, this is a bug somewhere else. In the past,
     220                 :   // this assert found cases where a Run method would schedule a new statement
     221                 :   // without checking if asyncClose had been called. The caller must prevent
     222                 :   // that from happening or, if the work is not critical, just avoid creating
     223                 :   // the new statement during shutdown. See bug 718449 for an example.
     224           42833 :   MOZ_ASSERT(target);
     225           42833 :   if (!target) {
     226               0 :     return NS_ERROR_NOT_AVAILABLE;
     227                 :   }
     228                 : 
     229           42833 :   nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
     230           42833 :   NS_ENSURE_SUCCESS(rv, rv);
     231                 : 
     232                 :   // Return it as the pending statement object and track it.
     233           42833 :   NS_ADDREF(*_stmt = event);
     234           42833 :   return NS_OK;
     235                 : }
     236                 : 
     237           42833 : AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
     238                 :                                                Connection *aConnection,
     239                 :                                                mozIStorageStatementCallback *aCallback)
     240                 : : mConnection(aConnection)
     241                 : , mTransactionManager(nsnull)
     242                 : , mCallback(aCallback)
     243                 : , mCallingThread(::do_GetCurrentThread())
     244                 : , mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS))
     245                 : , mIntervalStart(TimeStamp::Now())
     246                 : , mState(PENDING)
     247                 : , mCancelRequested(false)
     248                 : , mMutex(aConnection->sharedAsyncExecutionMutex)
     249                 : , mDBMutex(aConnection->sharedDBMutex)
     250           42833 :   , mRequestStartDate(TimeStamp::Now())
     251                 : {
     252           42833 :   (void)mStatements.SwapElements(aStatements);
     253           42833 :   NS_ASSERTION(mStatements.Length(), "We weren't given any statements!");
     254           42833 :   NS_IF_ADDREF(mCallback);
     255           42833 : }
     256                 : 
     257                 : bool
     258           10379 : AsyncExecuteStatements::shouldNotify()
     259                 : {
     260                 : #ifdef DEBUG
     261           10379 :   mMutex.AssertNotCurrentThreadOwns();
     262                 : 
     263           10379 :   bool onCallingThread = false;
     264           10379 :   (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     265           10379 :   NS_ASSERTION(onCallingThread, "runEvent not running on the calling thread!");
     266                 : #endif
     267                 : 
     268                 :   // We do not need to acquire mMutex here because it can only ever be written
     269                 :   // to on the calling thread, and the only thread that can call us is the
     270                 :   // calling thread, so we know that our access is serialized.
     271           10379 :   return !mCancelRequested;
     272                 : }
     273                 : 
     274                 : bool
     275           49122 : AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
     276                 :                                                        bool aLastStatement)
     277                 : {
     278           49122 :   mMutex.AssertNotCurrentThreadOwns();
     279                 : 
     280           49122 :   sqlite3_stmt *aStatement = nsnull;
     281                 :   // This cannot fail; we are only called if it's available.
     282           49122 :   (void)aData.getSqliteStatement(&aStatement);
     283           49122 :   NS_ASSERTION(aStatement, "You broke the code; do not call here like that!");
     284           49122 :   BindingParamsArray *paramsArray(aData);
     285                 : 
     286                 :   // Iterate through all of our parameters, bind them, and execute.
     287           49122 :   bool continueProcessing = true;
     288           49122 :   BindingParamsArray::iterator itr = paramsArray->begin();
     289           49122 :   BindingParamsArray::iterator end = paramsArray->end();
     290          147514 :   while (itr != end && continueProcessing) {
     291                 :     // Bind the data to our statement.
     292                 :     nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = 
     293           98552 :       do_QueryInterface(*itr);
     294           98552 :     nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement);
     295           49276 :     if (error) {
     296                 :       // Set our error state.
     297               6 :       mState = ERROR;
     298                 : 
     299                 :       // And notify.
     300               6 :       (void)notifyError(error);
     301               6 :       return false;
     302                 :     }
     303                 : 
     304                 :     // Advance our iterator, execute, and then process the statement.
     305           49270 :     itr++;
     306           49270 :     bool lastStatement = aLastStatement && itr == end;
     307           49270 :     continueProcessing = executeAndProcessStatement(aStatement, lastStatement);
     308                 : 
     309                 :     // Always reset our statement.
     310           98546 :     (void)::sqlite3_reset(aStatement);
     311                 :   }
     312                 : 
     313           49116 :   return continueProcessing;
     314                 : }
     315                 : 
     316                 : bool
     317           54209 : AsyncExecuteStatements::executeAndProcessStatement(sqlite3_stmt *aStatement,
     318                 :                                                    bool aLastStatement)
     319                 : {
     320           54209 :   mMutex.AssertNotCurrentThreadOwns();
     321                 : 
     322                 :   // Execute our statement
     323                 :   bool hasResults;
     324           78891 :   do {
     325           78912 :     hasResults = executeStatement(aStatement);
     326                 : 
     327                 :     // If we had an error, bail.
     328           78912 :     if (mState == ERROR)
     329              16 :       return false;
     330                 : 
     331                 :     // If we have been canceled, there is no point in going on...
     332                 :     {
     333          157792 :       MutexAutoLock lockedScope(mMutex);
     334           78896 :       if (mCancelRequested) {
     335               5 :         mState = CANCELED;
     336               5 :         return false;
     337                 :       }
     338                 :     }
     339                 : 
     340                 :     // Build our result set and notify if we got anything back and have a
     341                 :     // callback to notify.
     342          103324 :     if (mCallback && hasResults &&
     343           24433 :         NS_FAILED(buildAndNotifyResults(aStatement))) {
     344                 :       // We had an error notifying, so we notify on error and stop processing.
     345               0 :       mState = ERROR;
     346                 : 
     347                 :       // Notify, and stop processing statements.
     348                 :       (void)notifyError(mozIStorageError::ERROR,
     349               0 :                         "An error occurred while notifying about results");
     350                 : 
     351               0 :       return false;
     352                 :     }
     353                 :   } while (hasResults);
     354                 : 
     355                 : #ifdef DEBUG
     356                 :   // Check to make sure that this statement was smart about what it did.
     357           54188 :   checkAndLogStatementPerformance(aStatement);
     358                 : #endif
     359                 : 
     360                 :   // If we are done, we need to set our state accordingly while we still hold
     361                 :   // our mutex.  We would have already returned if we were canceled or had
     362                 :   // an error at this point.
     363           54188 :   if (aLastStatement)
     364           42786 :     mState = COMPLETED;
     365                 : 
     366           54188 :   return true;
     367                 : }
     368                 : 
     369                 : bool
     370           78912 : AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
     371                 : {
     372           78912 :   mMutex.AssertNotCurrentThreadOwns();
     373          157824 :   Telemetry::AutoTimer<Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_MS> finallySendExecutionDuration(mRequestStartDate);
     374               0 :   while (true) {
     375                 :     // lock the sqlite mutex so sqlite3_errmsg cannot change
     376          157824 :     SQLiteMutexAutoLock lockedScope(mDBMutex);
     377                 : 
     378           78912 :     int rc = mConnection->stepStatement(aStatement);
     379                 :     // Stop if we have no more results.
     380           78912 :     if (rc == SQLITE_DONE)
     381                 :     {
     382           54188 :       Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true);
     383           54188 :       return false;
     384                 :     }
     385                 : 
     386                 :     // If we got results, we can return now.
     387           24724 :     if (rc == SQLITE_ROW)
     388                 :     {
     389           24708 :       Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true);
     390           24708 :       return true;
     391                 :     }
     392                 : 
     393                 :     // Some errors are not fatal, and we can handle them and continue.
     394              16 :     if (rc == SQLITE_BUSY) {
     395                 :       // Don't hold the lock while we call outside our module.
     396               0 :       SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     397                 : 
     398                 :       // Yield, and try again
     399               0 :       (void)::PR_Sleep(PR_INTERVAL_NO_WAIT);
     400               0 :       continue;
     401                 :     }
     402                 : 
     403                 :     // Set an error state.
     404              16 :     mState = ERROR;
     405              16 :     Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, false);
     406                 : 
     407                 :     // Construct the error message before giving up the mutex (which we cannot
     408                 :     // hold during the call to notifyError).
     409              16 :     sqlite3 *db = mConnection->GetNativeConnection();
     410              48 :     nsCOMPtr<mozIStorageError> errorObj(new Error(rc, ::sqlite3_errmsg(db)));
     411                 :     // We cannot hold the DB mutex while calling notifyError.
     412              32 :     SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     413              16 :     (void)notifyError(errorObj);
     414                 : 
     415                 :     // Finally, indicate that we should stop processing.
     416              16 :     return false;
     417                 :   }
     418                 : }
     419                 : 
     420                 : nsresult
     421           24433 : AsyncExecuteStatements::buildAndNotifyResults(sqlite3_stmt *aStatement)
     422                 : {
     423           24433 :   NS_ASSERTION(mCallback, "Trying to dispatch results without a callback!");
     424           24433 :   mMutex.AssertNotCurrentThreadOwns();
     425                 : 
     426                 :   // Build result object if we need it.
     427           24433 :   if (!mResultSet)
     428           10355 :     mResultSet = new ResultSet();
     429           24433 :   NS_ENSURE_TRUE(mResultSet, NS_ERROR_OUT_OF_MEMORY);
     430                 : 
     431           48866 :   nsRefPtr<Row> row(new Row());
     432           24433 :   NS_ENSURE_TRUE(row, NS_ERROR_OUT_OF_MEMORY);
     433                 : 
     434           24433 :   nsresult rv = row->initialize(aStatement);
     435           24433 :   NS_ENSURE_SUCCESS(rv, rv);
     436                 : 
     437           24433 :   rv = mResultSet->add(row);
     438           24433 :   NS_ENSURE_SUCCESS(rv, rv);
     439                 : 
     440                 :   // If we have hit our maximum number of allowed results, or if we have hit
     441                 :   // the maximum amount of time we want to wait for results, notify the
     442                 :   // calling thread about it.
     443           24433 :   TimeStamp now = TimeStamp::Now();
     444           24433 :   TimeDuration delta = now - mIntervalStart;
     445           24433 :   if (mResultSet->rows() >= MAX_ROWS_PER_RESULT || delta > mMaxWait) {
     446                 :     // Notify the caller
     447             891 :     rv = notifyResults();
     448             891 :     if (NS_FAILED(rv))
     449               0 :       return NS_OK; // we'll try again with the next result
     450                 : 
     451                 :     // Reset our start time
     452             891 :     mIntervalStart = now;
     453                 :   }
     454                 : 
     455           24433 :   return NS_OK;
     456                 : }
     457                 : 
     458                 : nsresult
     459           42833 : AsyncExecuteStatements::notifyComplete()
     460                 : {
     461           42833 :   mMutex.AssertNotCurrentThreadOwns();
     462           42833 :   NS_ASSERTION(mState != PENDING,
     463                 :                "Still in a pending state when calling Complete!");
     464                 : 
     465                 :   // Finalize our statements before we try to commit or rollback.  If we are
     466                 :   // canceling and have statements that think they have pending work, the
     467                 :   // rollback will fail.
     468           96914 :   for (PRUint32 i = 0; i < mStatements.Length(); i++)
     469           54081 :     mStatements[i].finalize();
     470                 : 
     471                 :   // Handle our transaction, if we have one
     472           42833 :   if (mTransactionManager) {
     473            5138 :     if (mState == COMPLETED) {
     474            5137 :       nsresult rv = mTransactionManager->Commit();
     475            5137 :       if (NS_FAILED(rv)) {
     476               0 :         mState = ERROR;
     477                 :         (void)notifyError(mozIStorageError::ERROR,
     478               0 :                           "Transaction failed to commit");
     479                 :       }
     480                 :     }
     481                 :     else {
     482               1 :       (void)mTransactionManager->Rollback();
     483                 :     }
     484            5138 :     delete mTransactionManager;
     485            5138 :     mTransactionManager = nsnull;
     486                 :   }
     487                 : 
     488                 :   // Always generate a completion notification; it is what guarantees that our
     489                 :   // destruction does not happen here on the async thread.
     490                 :   nsRefPtr<CompletionNotifier> completionEvent =
     491           85666 :     new CompletionNotifier(mCallback, mState, mStatements);
     492           42833 :   NS_ASSERTION(mStatements.IsEmpty(),
     493                 :                "Should have given up ownership of mStatements!");
     494                 : 
     495                 :   // We no longer own mCallback (the CompletionNotifier takes ownership).
     496           42833 :   mCallback = nsnull;
     497                 : 
     498           42833 :   (void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
     499                 : 
     500           42833 :   return NS_OK;
     501                 : }
     502                 : 
     503                 : nsresult
     504               0 : AsyncExecuteStatements::notifyError(PRInt32 aErrorCode,
     505                 :                                     const char *aMessage)
     506                 : {
     507               0 :   mMutex.AssertNotCurrentThreadOwns();
     508               0 :   mDBMutex.assertNotCurrentThreadOwns();
     509                 : 
     510               0 :   if (!mCallback)
     511               0 :     return NS_OK;
     512                 : 
     513               0 :   nsCOMPtr<mozIStorageError> errorObj(new Error(aErrorCode, aMessage));
     514               0 :   NS_ENSURE_TRUE(errorObj, NS_ERROR_OUT_OF_MEMORY);
     515                 : 
     516               0 :   return notifyError(errorObj);
     517                 : }
     518                 : 
     519                 : nsresult
     520              26 : AsyncExecuteStatements::notifyError(mozIStorageError *aError)
     521                 : {
     522              26 :   mMutex.AssertNotCurrentThreadOwns();
     523              26 :   mDBMutex.assertNotCurrentThreadOwns();
     524                 : 
     525              26 :   if (!mCallback)
     526               2 :     return NS_OK;
     527                 : 
     528                 :   nsRefPtr<ErrorNotifier> notifier =
     529              48 :     new ErrorNotifier(mCallback, aError, this);
     530              24 :   NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY);
     531                 : 
     532              24 :   return mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
     533                 : }
     534                 : 
     535                 : nsresult
     536           10355 : AsyncExecuteStatements::notifyResults()
     537                 : {
     538           10355 :   mMutex.AssertNotCurrentThreadOwns();
     539           10355 :   NS_ASSERTION(mCallback, "notifyResults called without a callback!");
     540                 : 
     541                 :   nsRefPtr<CallbackResultNotifier> notifier =
     542           31065 :     new CallbackResultNotifier(mCallback, mResultSet, this);
     543           10355 :   NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY);
     544                 : 
     545           10355 :   nsresult rv = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
     546           10355 :   if (NS_SUCCEEDED(rv))
     547           10355 :     mResultSet = nsnull; // we no longer own it on success
     548           10355 :   return rv;
     549                 : }
     550                 : 
     551          771924 : NS_IMPL_THREADSAFE_ISUPPORTS2(
     552                 :   AsyncExecuteStatements,
     553                 :   nsIRunnable,
     554                 :   mozIStoragePendingStatement
     555                 : )
     556                 : 
     557                 : bool
     558           42817 : AsyncExecuteStatements::statementsNeedTransaction()
     559                 : {
     560                 :   // If there is more than one write statement, run in a transaction.
     561                 :   // Additionally, if we have only one statement but it needs a transaction, due
     562                 :   // to multiple BindingParams, we will wrap it in one.
     563           86311 :   for (PRUint32 i = 0, transactionsCount = 0; i < mStatements.Length(); ++i) {
     564           48632 :     transactionsCount += mStatements[i].needsTransaction();
     565           48632 :     if (transactionsCount > 1) {
     566            5138 :       return true;
     567                 :     }
     568                 :   }
     569           37679 :   return false;
     570                 : }
     571                 : 
     572                 : ////////////////////////////////////////////////////////////////////////////////
     573                 : //// mozIStoragePendingStatement
     574                 : 
     575                 : NS_IMETHODIMP
     576              58 : AsyncExecuteStatements::Cancel()
     577                 : {
     578                 : #ifdef DEBUG
     579              58 :   bool onCallingThread = false;
     580              58 :   (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     581              58 :   NS_ASSERTION(onCallingThread, "Not canceling from the calling thread!");
     582                 : #endif
     583                 : 
     584                 :   // If we have already canceled, we have an error, but always indicate that
     585                 :   // we are trying to cancel.
     586              58 :   NS_ENSURE_FALSE(mCancelRequested, NS_ERROR_UNEXPECTED);
     587                 : 
     588                 :   {
     589             112 :     MutexAutoLock lockedScope(mMutex);
     590                 : 
     591                 :     // We need to indicate that we want to try and cancel now.
     592              56 :     mCancelRequested = true;
     593                 :   }
     594                 : 
     595              56 :   return NS_OK;
     596                 : }
     597                 : 
     598                 : ////////////////////////////////////////////////////////////////////////////////
     599                 : //// nsIRunnable
     600                 : 
     601                 : NS_IMETHODIMP
     602           42833 : AsyncExecuteStatements::Run()
     603                 : {
     604                 :   // Do not run if we have been canceled.
     605                 :   {
     606           85666 :     MutexAutoLock lockedScope(mMutex);
     607           42833 :     if (mCancelRequested)
     608              16 :       mState = CANCELED;
     609                 :   }
     610           42833 :   if (mState == CANCELED)
     611              16 :     return notifyComplete();
     612                 : 
     613           42817 :   if (statementsNeedTransaction()) {
     614                 :     mTransactionManager = new mozStorageTransaction(mConnection, false,
     615           10276 :                                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
     616                 :   }
     617                 : 
     618                 :   // Execute each statement, giving the callback results if it returns any.
     619           96851 :   for (PRUint32 i = 0; i < mStatements.Length(); i++) {
     620           54065 :     bool finished = (i == (mStatements.Length() - 1));
     621                 : 
     622                 :     sqlite3_stmt *stmt;
     623                 :     { // lock the sqlite mutex so sqlite3_errmsg cannot change
     624          108130 :       SQLiteMutexAutoLock lockedScope(mDBMutex);
     625                 : 
     626           54065 :       int rc = mStatements[i].getSqliteStatement(&stmt);
     627           54065 :       if (rc != SQLITE_OK) {
     628                 :         // Set our error state.
     629               4 :         mState = ERROR;
     630                 : 
     631                 :         // Build the error object; can't call notifyError with the lock held
     632               4 :         sqlite3 *db = mConnection->GetNativeConnection();
     633                 :         nsCOMPtr<mozIStorageError> errorObj(
     634               4 :           new Error(rc, ::sqlite3_errmsg(db))
     635              16 :         );
     636                 :         {
     637                 :           // We cannot hold the DB mutex and call notifyError.
     638               8 :           SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     639               4 :           (void)notifyError(errorObj);
     640                 :         }
     641                 :         break;
     642                 :       }
     643                 :     }
     644                 : 
     645                 :     // If we have parameters to bind, bind them, execute, and process.
     646           54061 :     if (mStatements[i].hasParametersToBeBound()) {
     647           49122 :       if (!bindExecuteAndProcessStatement(mStatements[i], finished))
     648              14 :         break;
     649                 :     }
     650                 :     // Otherwise, just execute and process the statement.
     651            4939 :     else if (!executeAndProcessStatement(stmt, finished)) {
     652              13 :       break;
     653                 :     }
     654                 :   }
     655                 : 
     656                 :   // If we still have results that we haven't notified about, take care of
     657                 :   // them now.
     658           42817 :   if (mResultSet)
     659            9464 :     (void)notifyResults();
     660                 : 
     661                 :   // Notify about completion
     662           42817 :   return notifyComplete();
     663                 : }
     664                 : 
     665                 : } // namespace storage
     666                 : } // namespace mozilla

Generated by: LCOV version 1.7