LCOV - code coverage report
Current view: directory - dom/indexedDB - TransactionThreadPool.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 311 245 78.8 %
Date: 2012-06-02 Functions: 28 25 89.3 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set ts=2 et sw=2 tw=80: */
       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 Indexed Database.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * The Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2010
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Ben Turner <bent.mozilla@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "TransactionThreadPool.h"
      41                 : 
      42                 : #include "nsIObserverService.h"
      43                 : #include "nsIThreadPool.h"
      44                 : 
      45                 : #include "nsComponentManagerUtils.h"
      46                 : #include "nsThreadUtils.h"
      47                 : #include "nsServiceManagerUtils.h"
      48                 : #include "nsXPCOMCIDInternal.h"
      49                 : 
      50                 : using mozilla::MonitorAutoLock;
      51                 : 
      52                 : USING_INDEXEDDB_NAMESPACE
      53                 : 
      54                 : namespace {
      55                 : 
      56                 : const PRUint32 kThreadLimit = 20;
      57                 : const PRUint32 kIdleThreadLimit = 5;
      58                 : const PRUint32 kIdleThreadTimeoutMs = 30000;
      59                 : 
      60                 : TransactionThreadPool* gInstance = nsnull;
      61                 : bool gShutdown = false;
      62                 : 
      63                 : inline
      64                 : nsresult
      65            2967 : CheckOverlapAndMergeObjectStores(nsTArray<nsString>& aLockedStores,
      66                 :                                  const nsTArray<nsString>& aObjectStores,
      67                 :                                  bool aShouldMerge,
      68                 :                                  bool* aStoresOverlap)
      69                 : {
      70            2967 :   PRUint32 length = aObjectStores.Length();
      71                 : 
      72            2967 :   bool overlap = false;
      73                 : 
      74            6934 :   for (PRUint32 index = 0; index < length; index++) {
      75            3967 :     const nsString& storeName = aObjectStores[index];
      76            3967 :     if (aLockedStores.Contains(storeName)) {
      77            2017 :       overlap = true;
      78                 :     }
      79            1950 :     else if (aShouldMerge && !aLockedStores.AppendElement(storeName)) {
      80               0 :       NS_WARNING("Out of memory!");
      81               0 :       return NS_ERROR_OUT_OF_MEMORY;
      82                 :     }
      83                 :   }
      84                 : 
      85            2967 :   *aStoresOverlap = overlap;
      86            2967 :   return NS_OK;
      87                 : }
      88                 : 
      89                 : } // anonymous namespace
      90                 : 
      91                 : BEGIN_INDEXEDDB_NAMESPACE
      92                 : 
      93                 : class FinishTransactionRunnable : public nsIRunnable
      94             540 : {
      95                 : public:
      96                 :   NS_DECL_ISUPPORTS
      97                 :   NS_DECL_NSIRUNNABLE
      98                 : 
      99                 :   inline FinishTransactionRunnable(IDBTransaction* aTransaction,
     100                 :                                    nsCOMPtr<nsIRunnable>& aFinishRunnable);
     101                 : 
     102                 : private:
     103                 :   IDBTransaction* mTransaction;
     104                 :   nsCOMPtr<nsIRunnable> mFinishRunnable;
     105                 : };
     106                 : 
     107                 : END_INDEXEDDB_NAMESPACE
     108                 : 
     109              50 : TransactionThreadPool::TransactionThreadPool()
     110                 : {
     111              50 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     112              50 :   NS_ASSERTION(!gInstance, "More than one instance!");
     113              50 : }
     114                 : 
     115             100 : TransactionThreadPool::~TransactionThreadPool()
     116                 : {
     117              50 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     118              50 :   NS_ASSERTION(gInstance == this, "Different instances!");
     119              50 :   gInstance = nsnull;
     120              50 : }
     121                 : 
     122                 : // static
     123                 : TransactionThreadPool*
     124            5682 : TransactionThreadPool::GetOrCreate()
     125                 : {
     126            5682 :   if (!gInstance && !gShutdown) {
     127              50 :     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     128             100 :     nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool());
     129                 : 
     130              50 :     nsresult rv = pool->Init();
     131              50 :     NS_ENSURE_SUCCESS(rv, nsnull);
     132                 : 
     133             100 :     gInstance = pool.forget();
     134                 :   }
     135            5682 :   return gInstance;
     136                 : }
     137                 : 
     138                 : // static
     139                 : TransactionThreadPool*
     140               0 : TransactionThreadPool::Get()
     141                 : {
     142               0 :   return gInstance;
     143                 : }
     144                 : 
     145                 : // static
     146                 : void
     147              54 : TransactionThreadPool::Shutdown()
     148                 : {
     149              54 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     150                 : 
     151              54 :   gShutdown = true;
     152                 : 
     153              54 :   if (gInstance) {
     154              50 :     if (NS_FAILED(gInstance->Cleanup())) {
     155               0 :       NS_WARNING("Failed to shutdown thread pool!");
     156                 :     }
     157              50 :     delete gInstance;
     158              50 :     gInstance = nsnull;
     159                 :   }
     160              54 : }
     161                 : 
     162                 : nsresult
     163              50 : TransactionThreadPool::Init()
     164                 : {
     165              50 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     166                 : 
     167              50 :   if (!mTransactionsInProgress.Init()) {
     168               0 :     NS_WARNING("Failed to init hash!");
     169               0 :     return NS_ERROR_FAILURE;
     170                 :   }
     171                 : 
     172                 :   nsresult rv;
     173              50 :   mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
     174              50 :   NS_ENSURE_SUCCESS(rv, rv);
     175                 : 
     176              50 :   rv = mThreadPool->SetThreadLimit(kThreadLimit);
     177              50 :   NS_ENSURE_SUCCESS(rv, rv);
     178                 : 
     179              50 :   rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
     180              50 :   NS_ENSURE_SUCCESS(rv, rv);
     181                 : 
     182              50 :   rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
     183              50 :   NS_ENSURE_SUCCESS(rv, rv);
     184                 : 
     185              50 :   return NS_OK;
     186                 : }
     187                 : 
     188                 : nsresult
     189              50 : TransactionThreadPool::Cleanup()
     190                 : {
     191              50 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     192                 : 
     193              50 :   nsresult rv = mThreadPool->Shutdown();
     194              50 :   NS_ENSURE_SUCCESS(rv, rv);
     195                 : 
     196                 :   // Make sure the pool is still accessible while any callbacks generated from
     197                 :   // the other threads are processed.
     198              50 :   rv = NS_ProcessPendingEvents(nsnull);
     199              50 :   NS_ENSURE_SUCCESS(rv, rv);
     200                 : 
     201              50 :   if (!mCompleteCallbacks.IsEmpty()) {
     202                 :     // Run all callbacks manually now.
     203               0 :     for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
     204               0 :       mCompleteCallbacks[index].mCallback->Run();
     205                 :     }
     206               0 :     mCompleteCallbacks.Clear();
     207                 : 
     208                 :     // And make sure they get processed.
     209               0 :     rv = NS_ProcessPendingEvents(nsnull);
     210               0 :     NS_ENSURE_SUCCESS(rv, rv);
     211                 :   }
     212                 : 
     213              50 :   return NS_OK;
     214                 : }
     215                 : 
     216                 : void
     217             540 : TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
     218                 : {
     219             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     220             540 :   NS_ASSERTION(aTransaction, "Null pointer!");
     221                 : 
     222                 :   // AddRef here because removing from the hash will call Release.
     223            1080 :   nsRefPtr<IDBTransaction> transaction(aTransaction);
     224                 : 
     225             540 :   nsIAtom* databaseId = aTransaction->mDatabase->Id();
     226                 : 
     227                 :   DatabaseTransactionInfo* dbTransactionInfo;
     228             540 :   if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
     229               0 :     NS_ERROR("We don't know anyting about this database?!");
     230                 :     return;
     231                 :   }
     232                 : 
     233                 :   nsTArray<TransactionInfo>& transactionsInProgress =
     234             540 :     dbTransactionInfo->transactions;
     235                 : 
     236             540 :   PRUint32 transactionCount = transactionsInProgress.Length();
     237                 : 
     238                 : #ifdef DEBUG
     239             540 :   if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {
     240              71 :     NS_ASSERTION(transactionCount == 1,
     241                 :                  "More transactions running than should be!");
     242                 :   }
     243                 : #endif
     244                 : 
     245             540 :   if (transactionCount == 1) {
     246                 : #ifdef DEBUG
     247                 :     {
     248             404 :       TransactionInfo& info = transactionsInProgress[0];
     249             404 :       NS_ASSERTION(info.transaction == aTransaction, "Transaction mismatch!");
     250                 :     }
     251                 : #endif
     252             404 :     mTransactionsInProgress.Remove(databaseId);
     253                 : 
     254                 :     // See if we need to fire any complete callbacks.
     255             404 :     for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
     256               0 :       MaybeFireCallback(index);
     257                 :     }
     258                 :   }
     259                 :   else {
     260                 :     // We need to rebuild the locked object store list.
     261             272 :     nsTArray<nsString> storesWriting, storesReading;
     262                 : 
     263             701 :     for (PRUint32 index = 0, count = transactionCount; index < count; index++) {
     264             565 :       IDBTransaction* transaction = transactionsInProgress[index].transaction;
     265             565 :       if (transaction == aTransaction) {
     266             136 :         NS_ASSERTION(count == transactionCount, "More than one match?!");
     267                 : 
     268             136 :         transactionsInProgress.RemoveElementAt(index);
     269             136 :         index--;
     270             136 :         count--;
     271                 : 
     272             136 :         continue;
     273                 :       }
     274                 : 
     275             429 :       const nsTArray<nsString>& objectStores = transaction->mObjectStoreNames;
     276                 : 
     277                 :       bool dummy;
     278             429 :       if (transaction->mMode == IDBTransaction::READ_WRITE) {
     279               3 :         if (NS_FAILED(CheckOverlapAndMergeObjectStores(storesWriting,
     280                 :                                                        objectStores,
     281                 :                                                        true, &dummy))) {
     282               0 :           NS_WARNING("Out of memory!");
     283                 :         }
     284                 :       }
     285             426 :       else if (transaction->mMode == IDBTransaction::READ_ONLY) {
     286             426 :         if (NS_FAILED(CheckOverlapAndMergeObjectStores(storesReading,
     287                 :                                                        objectStores,
     288                 :                                                        true, &dummy))) {
     289               0 :           NS_WARNING("Out of memory!");
     290                 :         }
     291                 :       }
     292                 :       else {
     293               0 :         NS_NOTREACHED("Unknown mode!");
     294                 :       }
     295                 :     }
     296                 : 
     297             136 :     NS_ASSERTION(transactionsInProgress.Length() == transactionCount - 1,
     298                 :                  "Didn't find the transaction we were looking for!");
     299                 : 
     300             136 :     dbTransactionInfo->storesWriting.SwapElements(storesWriting);
     301             136 :     dbTransactionInfo->storesReading.SwapElements(storesReading);
     302                 :   }
     303                 : 
     304                 :   // Try to dispatch all the queued transactions again.
     305            1080 :   nsTArray<QueuedDispatchInfo> queuedDispatch;
     306             540 :   queuedDispatch.SwapElements(mDelayedDispatchQueue);
     307                 : 
     308             540 :   transactionCount = queuedDispatch.Length();
     309            1673 :   for (PRUint32 index = 0; index < transactionCount; index++) {
     310            1133 :     if (NS_FAILED(Dispatch(queuedDispatch[index]))) {
     311               0 :       NS_WARNING("Dispatch failed!");
     312                 :     }
     313                 :   }
     314                 : }
     315                 : 
     316                 : nsresult
     317            6812 : TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction,
     318                 :                                          bool* aCanRun,
     319                 :                                          TransactionQueue** aExistingQueue)
     320                 : {
     321            6812 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     322            6812 :   NS_ASSERTION(aTransaction, "Null pointer!");
     323            6812 :   NS_ASSERTION(aCanRun, "Null pointer!");
     324            6812 :   NS_ASSERTION(aExistingQueue, "Null pointer!");
     325                 : 
     326            6812 :   nsIAtom* databaseId = aTransaction->mDatabase->Id();
     327            6812 :   const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
     328            6812 :   const PRUint16 mode = aTransaction->mMode;
     329                 : 
     330                 :   // See if we can run this transaction now.
     331                 :   DatabaseTransactionInfo* dbTransactionInfo;
     332            6812 :   if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
     333                 :     // First transaction for this database, fine to run.
     334             404 :     *aCanRun = true;
     335             404 :     *aExistingQueue = nsnull;
     336             404 :     return NS_OK;
     337                 :   }
     338                 : 
     339                 :   nsTArray<TransactionInfo>& transactionsInProgress =
     340            6408 :     dbTransactionInfo->transactions;
     341                 : 
     342            6408 :   PRUint32 transactionCount = transactionsInProgress.Length();
     343            6408 :   NS_ASSERTION(transactionCount, "Should never be 0!");
     344                 : 
     345            8711 :   for (PRUint32 index = 0; index < transactionCount; index++) {
     346                 :     // See if this transaction is in out list of current transactions.
     347            7442 :     const TransactionInfo& info = transactionsInProgress[index];
     348            7442 :     if (info.transaction == aTransaction) {
     349            5139 :       *aCanRun = true;
     350            5139 :       *aExistingQueue = info.queue;
     351            5139 :       return NS_OK;
     352                 :     }
     353                 :   }
     354                 : 
     355            1269 :   NS_ASSERTION(mode != IDBTransaction::VERSION_CHANGE, "How did we get here?");
     356                 : 
     357                 :   bool writeOverlap;
     358                 :   nsresult rv =
     359                 :     CheckOverlapAndMergeObjectStores(dbTransactionInfo->storesWriting,
     360                 :                                      objectStoreNames,
     361                 :                                      mode == IDBTransaction::READ_WRITE,
     362            1269 :                                      &writeOverlap);
     363            1269 :   NS_ENSURE_SUCCESS(rv, rv);
     364                 : 
     365                 :   bool readOverlap;
     366                 :   rv = CheckOverlapAndMergeObjectStores(dbTransactionInfo->storesReading,
     367                 :                                         objectStoreNames,
     368                 :                                         mode == IDBTransaction::READ_ONLY,
     369            1269 :                                         &readOverlap);
     370            1269 :   NS_ENSURE_SUCCESS(rv, rv);
     371                 : 
     372            1269 :   if (writeOverlap ||
     373                 :       (readOverlap && mode == IDBTransaction::READ_WRITE)) {
     374            1133 :     *aCanRun = false;
     375            1133 :     *aExistingQueue = nsnull;
     376            1133 :     return NS_OK;
     377                 :   }
     378                 : 
     379             136 :   *aCanRun = true;
     380             136 :   *aExistingQueue = nsnull;
     381             136 :   return NS_OK;
     382                 : }
     383                 : 
     384                 : nsresult
     385            6812 : TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
     386                 :                                 nsIRunnable* aRunnable,
     387                 :                                 bool aFinish,
     388                 :                                 nsIRunnable* aFinishRunnable)
     389                 : {
     390            6812 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     391            6812 :   NS_ASSERTION(aTransaction, "Null pointer!");
     392            6812 :   NS_ASSERTION(aRunnable, "Null pointer!");
     393                 : 
     394            6812 :   if (aTransaction->mDatabase->IsInvalidated() && !aFinish) {
     395               0 :     return NS_ERROR_NOT_AVAILABLE;
     396                 :   }
     397                 : 
     398                 :   bool canRun;
     399                 :   TransactionQueue* existingQueue;
     400            6812 :   nsresult rv = TransactionCanRun(aTransaction, &canRun, &existingQueue);
     401            6812 :   NS_ENSURE_SUCCESS(rv, rv);
     402                 : 
     403            6812 :   if (!canRun) {
     404            1133 :     QueuedDispatchInfo* info = mDelayedDispatchQueue.AppendElement();
     405            1133 :     NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
     406                 : 
     407            1133 :     info->transaction = aTransaction;
     408            1133 :     info->runnable = aRunnable;
     409            1133 :     info->finish = aFinish;
     410            1133 :     info->finishRunnable = aFinishRunnable;
     411                 : 
     412            1133 :     return NS_OK;
     413                 :   }
     414                 : 
     415            5679 :   if (existingQueue) {
     416            5139 :     existingQueue->Dispatch(aRunnable);
     417            5139 :     if (aFinish) {
     418             540 :       existingQueue->Finish(aFinishRunnable);
     419                 :     }
     420            5139 :     return NS_OK;
     421                 :   }
     422                 : 
     423             540 :   nsIAtom* databaseId = aTransaction->mDatabase->Id();
     424                 : 
     425                 : #ifdef DEBUG
     426             540 :   if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {
     427              71 :     NS_ASSERTION(!mTransactionsInProgress.Get(databaseId, nsnull),
     428                 :                  "Shouldn't have anything in progress!");
     429                 :   }
     430                 : #endif
     431                 : 
     432                 :   DatabaseTransactionInfo* dbTransactionInfo;
     433            1080 :   nsAutoPtr<DatabaseTransactionInfo> autoDBTransactionInfo;
     434                 : 
     435             540 :   if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
     436                 :     // Make a new struct for this transaction.
     437             404 :     autoDBTransactionInfo = new DatabaseTransactionInfo();
     438             404 :     dbTransactionInfo = autoDBTransactionInfo;
     439                 :   }
     440                 : 
     441             540 :   const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
     442                 : 
     443                 :   nsTArray<nsString>& storesInUse =
     444                 :     aTransaction->mMode == IDBTransaction::READ_WRITE ?
     445                 :     dbTransactionInfo->storesWriting :
     446             540 :     dbTransactionInfo->storesReading;
     447                 : 
     448             540 :   if (!storesInUse.AppendElements(objectStoreNames)) {
     449               0 :     NS_WARNING("Out of memory!");
     450               0 :     return NS_ERROR_OUT_OF_MEMORY;
     451                 :   }
     452                 : 
     453                 :   nsTArray<TransactionInfo>& transactionInfoArray =
     454             540 :     dbTransactionInfo->transactions;
     455                 : 
     456             540 :   TransactionInfo* transactionInfo = transactionInfoArray.AppendElement();
     457             540 :   NS_ENSURE_TRUE(transactionInfo, NS_ERROR_OUT_OF_MEMORY);
     458                 : 
     459             540 :   transactionInfo->transaction = aTransaction;
     460             540 :   transactionInfo->queue = new TransactionQueue(aTransaction, aRunnable);
     461             540 :   if (aFinish) {
     462               0 :     transactionInfo->queue->Finish(aFinishRunnable);
     463                 :   }
     464                 : 
     465             540 :   if (!transactionInfo->objectStoreNames.AppendElements(objectStoreNames)) {
     466               0 :     NS_WARNING("Out of memory!");
     467               0 :     return NS_ERROR_OUT_OF_MEMORY;
     468                 :   }
     469                 : 
     470             540 :   if (autoDBTransactionInfo) {
     471             404 :     if (!mTransactionsInProgress.Put(databaseId, autoDBTransactionInfo)) {
     472               0 :       NS_WARNING("Failed to put!");
     473               0 :       return NS_ERROR_OUT_OF_MEMORY;
     474                 :     }
     475             404 :     autoDBTransactionInfo.forget();
     476                 :   }
     477                 : 
     478             540 :   return mThreadPool->Dispatch(transactionInfo->queue, NS_DISPATCH_NORMAL);
     479                 : }
     480                 : 
     481                 : bool
     482               3 : TransactionThreadPool::WaitForAllDatabasesToComplete(
     483                 :                                    nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
     484                 :                                    nsIRunnable* aCallback)
     485                 : {
     486               3 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     487               3 :   NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!");
     488               3 :   NS_ASSERTION(aCallback, "Null pointer!");
     489                 : 
     490               3 :   DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
     491               3 :   if (!callback) {
     492               0 :     NS_WARNING("Out of memory!");
     493               0 :     return false;
     494                 :   }
     495                 : 
     496               3 :   callback->mCallback = aCallback;
     497               3 :   if (!callback->mDatabases.SwapElements(aDatabases)) {
     498               0 :     NS_ERROR("This should never fail!");
     499                 :   }
     500                 : 
     501               3 :   MaybeFireCallback(mCompleteCallbacks.Length() - 1);
     502               3 :   return true;
     503                 : }
     504                 : 
     505                 : void
     506               0 : TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase)
     507                 : {
     508               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     509               0 :   NS_ASSERTION(aDatabase, "Null pointer!");
     510                 : 
     511                 :   // Get list of transactions for this database id
     512                 :   DatabaseTransactionInfo* dbTransactionInfo;
     513               0 :   if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) {
     514                 :     // If there are no running transactions, there can't be any pending ones
     515               0 :     return;
     516                 :   }
     517                 : 
     518               0 :   nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions;
     519                 : 
     520                 :   // Collect any running transactions
     521                 :   nsTArray<TransactionInfo>& transactionsInProgress =
     522               0 :     dbTransactionInfo->transactions;
     523                 : 
     524               0 :   PRUint32 transactionCount = transactionsInProgress.Length();
     525               0 :   NS_ASSERTION(transactionCount, "Should never be 0!");
     526                 : 
     527               0 :   for (PRUint32 index = 0; index < transactionCount; index++) {
     528                 :     // See if any transaction belongs to this IDBDatabase instance
     529               0 :     IDBTransaction* transaction = transactionsInProgress[index].transaction;
     530               0 :     if (transaction->Database() == aDatabase) {
     531               0 :       transactions.AppendElement(transaction);
     532                 :     }
     533                 :   }
     534                 : 
     535                 :   // Collect any pending transactions.
     536               0 :   for (PRUint32 index = 0; index < mDelayedDispatchQueue.Length(); index++) {
     537                 :     // See if any transaction belongs to this IDBDatabase instance
     538               0 :     IDBTransaction* transaction = mDelayedDispatchQueue[index].transaction;
     539               0 :     if (transaction->Database() == aDatabase) {
     540               0 :       transactions.AppendElement(transaction);
     541                 :     }
     542                 :   }
     543                 : 
     544                 :   // Abort transactions. Do this after collecting the transactions in case
     545                 :   // calling Abort() modifies the data structures we're iterating above.
     546               0 :   for (PRUint32 index = 0; index < transactions.Length(); index++) {
     547                 :     // This can fail, for example if the transaction is in the process of
     548                 :     // being comitted. That is expected and fine, so we ignore any returned
     549                 :     // errors.
     550               0 :     transactions[index]->Abort();
     551                 :   }
     552                 : }
     553                 : 
     554                 : bool
     555               0 : TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase)
     556                 : {
     557               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     558               0 :   NS_ASSERTION(aDatabase, "Null pointer!");
     559                 : 
     560                 :   // Get list of transactions for this database id
     561                 :   DatabaseTransactionInfo* dbTransactionInfo;
     562               0 :   if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) {
     563               0 :     return false;
     564                 :   }
     565                 : 
     566                 :   nsTArray<TransactionInfo>& transactionsInProgress =
     567               0 :     dbTransactionInfo->transactions;
     568                 : 
     569               0 :   PRUint32 transactionCount = transactionsInProgress.Length();
     570               0 :   NS_ASSERTION(transactionCount, "Should never be 0!");
     571                 : 
     572               0 :   for (PRUint32 index = 0; index < transactionCount; index++) {
     573                 :     // See if any transaction belongs to this IDBDatabase instance
     574               0 :     if (transactionsInProgress[index].transaction->Database() == aDatabase) {
     575               0 :       return true;
     576                 :     }
     577                 :   }
     578                 : 
     579               0 :   return false;
     580                 : }
     581                 : 
     582                 : void
     583               3 : TransactionThreadPool::MaybeFireCallback(PRUint32 aCallbackIndex)
     584                 : {
     585               3 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     586                 : 
     587               3 :   DatabasesCompleteCallback& callback = mCompleteCallbacks[aCallbackIndex];
     588                 : 
     589               3 :   bool freeToRun = true;
     590               6 :   for (PRUint32 index = 0; index < callback.mDatabases.Length(); index++) {
     591               3 :     if (mTransactionsInProgress.Get(callback.mDatabases[index]->Id(), nsnull)) {
     592               0 :       freeToRun = false;
     593               0 :       break;
     594                 :     }
     595                 :   }
     596                 : 
     597               3 :   if (freeToRun) {
     598               3 :     callback.mCallback->Run();
     599               3 :     mCompleteCallbacks.RemoveElementAt(aCallbackIndex);
     600                 :   }
     601               3 : }
     602                 : 
     603             540 : TransactionThreadPool::
     604                 : TransactionQueue::TransactionQueue(IDBTransaction* aTransaction,
     605                 :                                    nsIRunnable* aRunnable)
     606                 : : mMonitor("TransactionQueue::mMonitor"),
     607                 :   mTransaction(aTransaction),
     608             540 :   mShouldFinish(false)
     609                 : {
     610             540 :   NS_ASSERTION(aTransaction, "Null pointer!");
     611             540 :   NS_ASSERTION(aRunnable, "Null pointer!");
     612             540 :   mQueue.AppendElement(aRunnable);
     613             540 : }
     614                 : 
     615                 : void
     616            5139 : TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable)
     617                 : {
     618           10278 :   MonitorAutoLock lock(mMonitor);
     619                 : 
     620            5139 :   NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!");
     621                 : 
     622            5139 :   mQueue.AppendElement(aRunnable);
     623                 : 
     624            5139 :   mMonitor.Notify();
     625            5139 : }
     626                 : 
     627                 : void
     628             540 : TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable)
     629                 : {
     630            1080 :   MonitorAutoLock lock(mMonitor);
     631                 : 
     632             540 :   NS_ASSERTION(!mShouldFinish, "Finish called more than once!");
     633                 : 
     634             540 :   mShouldFinish = true;
     635             540 :   mFinishRunnable = aFinishRunnable;
     636                 : 
     637             540 :   mMonitor.Notify();
     638             540 : }
     639                 : 
     640            4320 : NS_IMPL_THREADSAFE_ISUPPORTS1(TransactionThreadPool::TransactionQueue,
     641                 :                               nsIRunnable)
     642                 : 
     643                 : NS_IMETHODIMP
     644             540 : TransactionThreadPool::TransactionQueue::Run()
     645                 : {
     646            1080 :   nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue;
     647            1080 :   nsCOMPtr<nsIRunnable> finishRunnable;
     648             540 :   bool shouldFinish = false;
     649                 : 
     650            5320 :   while(!shouldFinish) {
     651            4240 :     NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!");
     652                 : 
     653                 :     {
     654            8480 :       MonitorAutoLock lock(mMonitor);
     655           11838 :       while (!mShouldFinish && mQueue.IsEmpty()) {
     656            3358 :         if (NS_FAILED(mMonitor.Wait())) {
     657               0 :           NS_ERROR("Failed to wait!");
     658                 :         }
     659                 :       }
     660                 : 
     661            4240 :       mQueue.SwapElements(queue);
     662            4240 :       if (mShouldFinish) {
     663             540 :         mFinishRunnable.swap(finishRunnable);
     664             540 :         shouldFinish = true;
     665                 :       }
     666                 :     }
     667                 : 
     668            4240 :     PRUint32 count = queue.Length();
     669            9919 :     for (PRUint32 index = 0; index < count; index++) {
     670            5679 :       nsCOMPtr<nsIRunnable>& runnable = queue[index];
     671            5679 :       runnable->Run();
     672            5679 :       runnable = nsnull;
     673                 :     }
     674                 : 
     675            4240 :     if (count) {
     676            4225 :       queue.Clear();
     677                 :     }
     678                 :   }
     679                 : 
     680                 :   nsCOMPtr<nsIRunnable> finishTransactionRunnable =
     681            1080 :     new FinishTransactionRunnable(mTransaction, finishRunnable);
     682             540 :   if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable,
     683                 :                                         NS_DISPATCH_NORMAL))) {
     684               0 :     NS_WARNING("Failed to dispatch finishTransactionRunnable!");
     685                 :   }
     686                 : 
     687             540 :   return NS_OK;
     688                 : }
     689                 : 
     690             540 : FinishTransactionRunnable::FinishTransactionRunnable(
     691                 :                                          IDBTransaction* aTransaction,
     692                 :                                          nsCOMPtr<nsIRunnable>& aFinishRunnable)
     693             540 : : mTransaction(aTransaction)
     694                 : {
     695             540 :   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     696             540 :   NS_ASSERTION(aTransaction, "Null pointer!");
     697             540 :   mFinishRunnable.swap(aFinishRunnable);
     698             540 : }
     699                 : 
     700            5940 : NS_IMPL_THREADSAFE_ISUPPORTS1(FinishTransactionRunnable, nsIRunnable)
     701                 : 
     702                 : NS_IMETHODIMP
     703             540 : FinishTransactionRunnable::Run()
     704                 : {
     705             540 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     706             540 :   if (!gInstance) {
     707               0 :     NS_ERROR("Running after shutdown!");
     708               0 :     return NS_ERROR_FAILURE;
     709                 :   }
     710                 : 
     711             540 :   gInstance->FinishTransaction(mTransaction);
     712                 : 
     713             540 :   if (mFinishRunnable) {
     714             540 :     mFinishRunnable->Run();
     715             540 :     mFinishRunnable = nsnull;
     716                 :   }
     717                 : 
     718             540 :   return NS_OK;
     719                 : }

Generated by: LCOV version 1.7