1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/TransactionThreadPool.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,685 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 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 "TransactionThreadPool.h" 1.11 + 1.12 +#include "nsIObserverService.h" 1.13 +#include "nsIThreadPool.h" 1.14 + 1.15 +#include "nsComponentManagerUtils.h" 1.16 +#include "nsThreadUtils.h" 1.17 +#include "nsServiceManagerUtils.h" 1.18 +#include "nsXPCOMCIDInternal.h" 1.19 + 1.20 +#include "ProfilerHelpers.h" 1.21 + 1.22 +using mozilla::MonitorAutoLock; 1.23 + 1.24 +USING_INDEXEDDB_NAMESPACE 1.25 + 1.26 +namespace { 1.27 + 1.28 +const uint32_t kThreadLimit = 20; 1.29 +const uint32_t kIdleThreadLimit = 5; 1.30 +const uint32_t kIdleThreadTimeoutMs = 30000; 1.31 + 1.32 +TransactionThreadPool* gThreadPool = nullptr; 1.33 +bool gShutdown = false; 1.34 + 1.35 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.36 + 1.37 +class TransactionThreadPoolListener : public nsIThreadPoolListener 1.38 +{ 1.39 +public: 1.40 + NS_DECL_THREADSAFE_ISUPPORTS 1.41 + NS_DECL_NSITHREADPOOLLISTENER 1.42 + 1.43 +private: 1.44 + virtual ~TransactionThreadPoolListener() 1.45 + { } 1.46 +}; 1.47 + 1.48 +#endif // MOZ_ENABLE_PROFILER_SPS 1.49 + 1.50 +} // anonymous namespace 1.51 + 1.52 +BEGIN_INDEXEDDB_NAMESPACE 1.53 + 1.54 +class FinishTransactionRunnable MOZ_FINAL : public nsIRunnable 1.55 +{ 1.56 +public: 1.57 + NS_DECL_THREADSAFE_ISUPPORTS 1.58 + NS_DECL_NSIRUNNABLE 1.59 + 1.60 + inline FinishTransactionRunnable(IDBTransaction* aTransaction, 1.61 + nsCOMPtr<nsIRunnable>& aFinishRunnable); 1.62 + 1.63 +private: 1.64 + IDBTransaction* mTransaction; 1.65 + nsCOMPtr<nsIRunnable> mFinishRunnable; 1.66 +}; 1.67 + 1.68 +END_INDEXEDDB_NAMESPACE 1.69 + 1.70 +TransactionThreadPool::TransactionThreadPool() 1.71 +{ 1.72 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.73 + NS_ASSERTION(!gThreadPool, "More than one instance!"); 1.74 +} 1.75 + 1.76 +TransactionThreadPool::~TransactionThreadPool() 1.77 +{ 1.78 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.79 + NS_ASSERTION(gThreadPool == this, "Different instances!"); 1.80 + gThreadPool = nullptr; 1.81 +} 1.82 + 1.83 +// static 1.84 +TransactionThreadPool* 1.85 +TransactionThreadPool::GetOrCreate() 1.86 +{ 1.87 + if (!gThreadPool && !gShutdown) { 1.88 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.89 + nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool()); 1.90 + 1.91 + nsresult rv = pool->Init(); 1.92 + NS_ENSURE_SUCCESS(rv, nullptr); 1.93 + 1.94 + gThreadPool = pool.forget(); 1.95 + } 1.96 + return gThreadPool; 1.97 +} 1.98 + 1.99 +// static 1.100 +TransactionThreadPool* 1.101 +TransactionThreadPool::Get() 1.102 +{ 1.103 + return gThreadPool; 1.104 +} 1.105 + 1.106 +// static 1.107 +void 1.108 +TransactionThreadPool::Shutdown() 1.109 +{ 1.110 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.111 + 1.112 + gShutdown = true; 1.113 + 1.114 + if (gThreadPool) { 1.115 + if (NS_FAILED(gThreadPool->Cleanup())) { 1.116 + NS_WARNING("Failed to shutdown thread pool!"); 1.117 + } 1.118 + delete gThreadPool; 1.119 + gThreadPool = nullptr; 1.120 + } 1.121 +} 1.122 + 1.123 +nsresult 1.124 +TransactionThreadPool::Init() 1.125 +{ 1.126 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.127 + 1.128 + nsresult rv; 1.129 + mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); 1.130 + NS_ENSURE_SUCCESS(rv, rv); 1.131 + 1.132 + rv = mThreadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Trans")); 1.133 + NS_ENSURE_SUCCESS(rv, rv); 1.134 + 1.135 + rv = mThreadPool->SetThreadLimit(kThreadLimit); 1.136 + NS_ENSURE_SUCCESS(rv, rv); 1.137 + 1.138 + rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); 1.139 + NS_ENSURE_SUCCESS(rv, rv); 1.140 + 1.141 + rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); 1.142 + NS_ENSURE_SUCCESS(rv, rv); 1.143 + 1.144 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.145 + nsCOMPtr<nsIThreadPoolListener> listener = 1.146 + new TransactionThreadPoolListener(); 1.147 + 1.148 + rv = mThreadPool->SetListener(listener); 1.149 + NS_ENSURE_SUCCESS(rv, rv); 1.150 +#endif 1.151 + 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +nsresult 1.156 +TransactionThreadPool::Cleanup() 1.157 +{ 1.158 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.159 + 1.160 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", "TransactionThreadPool::Cleanup"); 1.161 + 1.162 + nsresult rv = mThreadPool->Shutdown(); 1.163 + NS_ENSURE_SUCCESS(rv, rv); 1.164 + 1.165 + // Make sure the pool is still accessible while any callbacks generated from 1.166 + // the other threads are processed. 1.167 + rv = NS_ProcessPendingEvents(nullptr); 1.168 + NS_ENSURE_SUCCESS(rv, rv); 1.169 + 1.170 + if (!mCompleteCallbacks.IsEmpty()) { 1.171 + // Run all callbacks manually now. 1.172 + for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { 1.173 + mCompleteCallbacks[index].mCallback->Run(); 1.174 + } 1.175 + mCompleteCallbacks.Clear(); 1.176 + 1.177 + // And make sure they get processed. 1.178 + rv = NS_ProcessPendingEvents(nullptr); 1.179 + NS_ENSURE_SUCCESS(rv, rv); 1.180 + } 1.181 + 1.182 + return NS_OK; 1.183 +} 1.184 + 1.185 +// static 1.186 +PLDHashOperator 1.187 +TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey<TransactionInfo>* aKey, 1.188 + void* aUserArg) 1.189 +{ 1.190 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.191 + 1.192 + TransactionInfo* maybeUnblockedInfo = aKey->GetKey(); 1.193 + TransactionInfo* finishedInfo = static_cast<TransactionInfo*>(aUserArg); 1.194 + 1.195 + NS_ASSERTION(maybeUnblockedInfo->blockedOn.Contains(finishedInfo), 1.196 + "Huh?"); 1.197 + maybeUnblockedInfo->blockedOn.RemoveEntry(finishedInfo); 1.198 + if (!maybeUnblockedInfo->blockedOn.Count()) { 1.199 + // Let this transaction run. 1.200 + maybeUnblockedInfo->queue->Unblock(); 1.201 + } 1.202 + 1.203 + return PL_DHASH_NEXT; 1.204 +} 1.205 + 1.206 +void 1.207 +TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) 1.208 +{ 1.209 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.210 + NS_ASSERTION(aTransaction, "Null pointer!"); 1.211 + 1.212 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", 1.213 + "TransactionThreadPool::FinishTransaction"); 1.214 + 1.215 + // AddRef here because removing from the hash will call Release. 1.216 + nsRefPtr<IDBTransaction> transaction(aTransaction); 1.217 + 1.218 + const nsACString& databaseId = aTransaction->mDatabase->Id(); 1.219 + 1.220 + DatabaseTransactionInfo* dbTransactionInfo; 1.221 + if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { 1.222 + NS_ERROR("We don't know anyting about this database?!"); 1.223 + return; 1.224 + } 1.225 + 1.226 + DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = 1.227 + dbTransactionInfo->transactions; 1.228 + 1.229 + uint32_t transactionCount = transactionsInProgress.Count(); 1.230 + 1.231 +#ifdef DEBUG 1.232 + if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { 1.233 + NS_ASSERTION(transactionCount == 1, 1.234 + "More transactions running than should be!"); 1.235 + } 1.236 +#endif 1.237 + 1.238 + if (transactionCount == 1) { 1.239 +#ifdef DEBUG 1.240 + { 1.241 + const TransactionInfo* info = transactionsInProgress.Get(aTransaction); 1.242 + NS_ASSERTION(info->transaction == aTransaction, "Transaction mismatch!"); 1.243 + } 1.244 +#endif 1.245 + mTransactionsInProgress.Remove(databaseId); 1.246 + 1.247 + // See if we need to fire any complete callbacks. 1.248 + uint32_t index = 0; 1.249 + while (index < mCompleteCallbacks.Length()) { 1.250 + if (MaybeFireCallback(mCompleteCallbacks[index])) { 1.251 + mCompleteCallbacks.RemoveElementAt(index); 1.252 + } 1.253 + else { 1.254 + index++; 1.255 + } 1.256 + } 1.257 + 1.258 + return; 1.259 + } 1.260 + TransactionInfo* info = transactionsInProgress.Get(aTransaction); 1.261 + NS_ASSERTION(info, "We've never heard of this transaction?!?"); 1.262 + 1.263 + const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames; 1.264 + for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; 1.265 + index++) { 1.266 + TransactionInfoPair* blockInfo = 1.267 + dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); 1.268 + NS_ASSERTION(blockInfo, "Huh?"); 1.269 + 1.270 + if (aTransaction->mMode == IDBTransaction::READ_WRITE && 1.271 + blockInfo->lastBlockingReads == info) { 1.272 + blockInfo->lastBlockingReads = nullptr; 1.273 + } 1.274 + 1.275 + uint32_t i = blockInfo->lastBlockingWrites.IndexOf(info); 1.276 + if (i != blockInfo->lastBlockingWrites.NoIndex) { 1.277 + blockInfo->lastBlockingWrites.RemoveElementAt(i); 1.278 + } 1.279 + } 1.280 + 1.281 + info->blocking.EnumerateEntries(MaybeUnblockTransaction, info); 1.282 + 1.283 + transactionsInProgress.Remove(aTransaction); 1.284 +} 1.285 + 1.286 +TransactionThreadPool::TransactionQueue& 1.287 +TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) 1.288 +{ 1.289 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.290 + NS_ASSERTION(aTransaction, "Null pointer!"); 1.291 + 1.292 + const nsACString& databaseId = aTransaction->mDatabase->Id(); 1.293 + 1.294 + const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames; 1.295 + const uint16_t mode = aTransaction->mMode; 1.296 + 1.297 + // See if we can run this transaction now. 1.298 + DatabaseTransactionInfo* dbTransactionInfo; 1.299 + if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { 1.300 + // First transaction for this database. 1.301 + dbTransactionInfo = new DatabaseTransactionInfo(); 1.302 + mTransactionsInProgress.Put(databaseId, dbTransactionInfo); 1.303 + } 1.304 + 1.305 + DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = 1.306 + dbTransactionInfo->transactions; 1.307 + TransactionInfo* info = transactionsInProgress.Get(aTransaction); 1.308 + if (info) { 1.309 + // We recognize this one. 1.310 + return *info->queue; 1.311 + } 1.312 + 1.313 + TransactionInfo* transactionInfo = new TransactionInfo(aTransaction); 1.314 + 1.315 + dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);; 1.316 + 1.317 + for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; 1.318 + index++) { 1.319 + TransactionInfoPair* blockInfo = 1.320 + dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); 1.321 + if (!blockInfo) { 1.322 + blockInfo = new TransactionInfoPair(); 1.323 + blockInfo->lastBlockingReads = nullptr; 1.324 + dbTransactionInfo->blockingTransactions.Put(objectStoreNames[index], 1.325 + blockInfo); 1.326 + } 1.327 + 1.328 + // Mark what we are blocking on. 1.329 + if (blockInfo->lastBlockingReads) { 1.330 + TransactionInfo* blockingInfo = blockInfo->lastBlockingReads; 1.331 + transactionInfo->blockedOn.PutEntry(blockingInfo); 1.332 + blockingInfo->blocking.PutEntry(transactionInfo); 1.333 + } 1.334 + 1.335 + if (mode == IDBTransaction::READ_WRITE && 1.336 + blockInfo->lastBlockingWrites.Length()) { 1.337 + for (uint32_t index = 0, 1.338 + count = blockInfo->lastBlockingWrites.Length(); index < count; 1.339 + index++) { 1.340 + TransactionInfo* blockingInfo = blockInfo->lastBlockingWrites[index]; 1.341 + transactionInfo->blockedOn.PutEntry(blockingInfo); 1.342 + blockingInfo->blocking.PutEntry(transactionInfo); 1.343 + } 1.344 + } 1.345 + 1.346 + if (mode == IDBTransaction::READ_WRITE) { 1.347 + blockInfo->lastBlockingReads = transactionInfo; 1.348 + blockInfo->lastBlockingWrites.Clear(); 1.349 + } 1.350 + else { 1.351 + blockInfo->lastBlockingWrites.AppendElement(transactionInfo); 1.352 + } 1.353 + } 1.354 + 1.355 + if (!transactionInfo->blockedOn.Count()) { 1.356 + transactionInfo->queue->Unblock(); 1.357 + } 1.358 + 1.359 + return *transactionInfo->queue; 1.360 +} 1.361 + 1.362 +nsresult 1.363 +TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, 1.364 + nsIRunnable* aRunnable, 1.365 + bool aFinish, 1.366 + nsIRunnable* aFinishRunnable) 1.367 +{ 1.368 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.369 + NS_ASSERTION(aTransaction, "Null pointer!"); 1.370 + NS_ASSERTION(aRunnable, "Null pointer!"); 1.371 + 1.372 + if (aTransaction->mDatabase->IsInvalidated() && !aFinish) { 1.373 + return NS_ERROR_NOT_AVAILABLE; 1.374 + } 1.375 + 1.376 + TransactionQueue& queue = GetQueueForTransaction(aTransaction); 1.377 + 1.378 + queue.Dispatch(aRunnable); 1.379 + if (aFinish) { 1.380 + queue.Finish(aFinishRunnable); 1.381 + } 1.382 + return NS_OK; 1.383 +} 1.384 + 1.385 +void 1.386 +TransactionThreadPool::WaitForDatabasesToComplete( 1.387 + nsTArray<nsRefPtr<IDBDatabase> >& aDatabases, 1.388 + nsIRunnable* aCallback) 1.389 +{ 1.390 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.391 + NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!"); 1.392 + NS_ASSERTION(aCallback, "Null pointer!"); 1.393 + 1.394 + DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); 1.395 + 1.396 + callback->mCallback = aCallback; 1.397 + callback->mDatabases.SwapElements(aDatabases); 1.398 + 1.399 + if (MaybeFireCallback(*callback)) { 1.400 + mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); 1.401 + } 1.402 +} 1.403 + 1.404 +// static 1.405 +PLDHashOperator 1.406 +TransactionThreadPool::CollectTransactions(IDBTransaction* aKey, 1.407 + TransactionInfo* aValue, 1.408 + void* aUserArg) 1.409 +{ 1.410 + nsAutoTArray<nsRefPtr<IDBTransaction>, 50>* transactionArray = 1.411 + static_cast<nsAutoTArray<nsRefPtr<IDBTransaction>, 50>*>(aUserArg); 1.412 + transactionArray->AppendElement(aKey); 1.413 + 1.414 + return PL_DHASH_NEXT; 1.415 +} 1.416 + 1.417 +void 1.418 +TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase) 1.419 +{ 1.420 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.421 + NS_ASSERTION(aDatabase, "Null pointer!"); 1.422 + 1.423 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", 1.424 + "TransactionThreadPool::" 1.425 + "AbortTransactionsForDatabase"); 1.426 + 1.427 + // Get list of transactions for this database id 1.428 + DatabaseTransactionInfo* dbTransactionInfo; 1.429 + if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) { 1.430 + // If there are no transactions, we're done. 1.431 + return; 1.432 + } 1.433 + 1.434 + // Collect any running transactions 1.435 + DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = 1.436 + dbTransactionInfo->transactions; 1.437 + 1.438 + NS_ASSERTION(transactionsInProgress.Count(), "Should never be 0!"); 1.439 + 1.440 + nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions; 1.441 + transactionsInProgress.EnumerateRead(CollectTransactions, &transactions); 1.442 + 1.443 + // Abort transactions. Do this after collecting the transactions in case 1.444 + // calling Abort() modifies the data structures we're iterating above. 1.445 + for (uint32_t index = 0; index < transactions.Length(); index++) { 1.446 + if (transactions[index]->Database() != aDatabase) { 1.447 + continue; 1.448 + } 1.449 + 1.450 + // This can fail, for example if the transaction is in the process of 1.451 + // being comitted. That is expected and fine, so we ignore any returned 1.452 + // errors. 1.453 + ErrorResult rv; 1.454 + transactions[index]->Abort(rv); 1.455 + } 1.456 +} 1.457 + 1.458 +struct MOZ_STACK_CLASS TransactionSearchInfo 1.459 +{ 1.460 + TransactionSearchInfo(nsIOfflineStorage* aDatabase) 1.461 + : db(aDatabase), found(false) 1.462 + { 1.463 + } 1.464 + 1.465 + nsIOfflineStorage* db; 1.466 + bool found; 1.467 +}; 1.468 + 1.469 +// static 1.470 +PLDHashOperator 1.471 +TransactionThreadPool::FindTransaction(IDBTransaction* aKey, 1.472 + TransactionInfo* aValue, 1.473 + void* aUserArg) 1.474 +{ 1.475 + TransactionSearchInfo* info = static_cast<TransactionSearchInfo*>(aUserArg); 1.476 + 1.477 + if (aKey->Database() == info->db) { 1.478 + info->found = true; 1.479 + return PL_DHASH_STOP; 1.480 + } 1.481 + 1.482 + return PL_DHASH_NEXT; 1.483 +} 1.484 +bool 1.485 +TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase) 1.486 +{ 1.487 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.488 + NS_ASSERTION(aDatabase, "Null pointer!"); 1.489 + 1.490 + DatabaseTransactionInfo* dbTransactionInfo = nullptr; 1.491 + dbTransactionInfo = mTransactionsInProgress.Get(aDatabase->Id()); 1.492 + if (!dbTransactionInfo) { 1.493 + return false; 1.494 + } 1.495 + 1.496 + TransactionSearchInfo info(aDatabase); 1.497 + dbTransactionInfo->transactions.EnumerateRead(FindTransaction, &info); 1.498 + 1.499 + return info.found; 1.500 +} 1.501 + 1.502 +bool 1.503 +TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback aCallback) 1.504 +{ 1.505 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.506 + 1.507 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", 1.508 + "TransactionThreadPool::MaybeFireCallback"); 1.509 + 1.510 + for (uint32_t index = 0; index < aCallback.mDatabases.Length(); index++) { 1.511 + IDBDatabase* database = aCallback.mDatabases[index]; 1.512 + if (!database) { 1.513 + MOZ_CRASH(); 1.514 + } 1.515 + 1.516 + if (mTransactionsInProgress.Get(database->Id(), nullptr)) { 1.517 + return false; 1.518 + } 1.519 + } 1.520 + 1.521 + aCallback.mCallback->Run(); 1.522 + return true; 1.523 +} 1.524 + 1.525 +TransactionThreadPool:: 1.526 +TransactionQueue::TransactionQueue(IDBTransaction* aTransaction) 1.527 +: mMonitor("TransactionQueue::mMonitor"), 1.528 + mTransaction(aTransaction), 1.529 + mShouldFinish(false) 1.530 +{ 1.531 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.532 + NS_ASSERTION(aTransaction, "Null pointer!"); 1.533 +} 1.534 + 1.535 +void 1.536 +TransactionThreadPool::TransactionQueue::Unblock() 1.537 +{ 1.538 + MonitorAutoLock lock(mMonitor); 1.539 + 1.540 + // NB: Finish may be called before Unblock. 1.541 + 1.542 + TransactionThreadPool::Get()->mThreadPool-> 1.543 + Dispatch(this, NS_DISPATCH_NORMAL); 1.544 +} 1.545 + 1.546 +void 1.547 +TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable) 1.548 +{ 1.549 + MonitorAutoLock lock(mMonitor); 1.550 + 1.551 + NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!"); 1.552 + 1.553 + mQueue.AppendElement(aRunnable); 1.554 + 1.555 + mMonitor.Notify(); 1.556 +} 1.557 + 1.558 +void 1.559 +TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable) 1.560 +{ 1.561 + MonitorAutoLock lock(mMonitor); 1.562 + 1.563 + NS_ASSERTION(!mShouldFinish, "Finish called more than once!"); 1.564 + 1.565 + mShouldFinish = true; 1.566 + mFinishRunnable = aFinishRunnable; 1.567 + 1.568 + mMonitor.Notify(); 1.569 +} 1.570 + 1.571 +NS_IMPL_ISUPPORTS(TransactionThreadPool::TransactionQueue, nsIRunnable) 1.572 + 1.573 +NS_IMETHODIMP 1.574 +TransactionThreadPool::TransactionQueue::Run() 1.575 +{ 1.576 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.577 + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); 1.578 + 1.579 + PROFILER_LABEL("IndexedDB", "TransactionQueue::Run"); 1.580 + 1.581 + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work", 1.582 + "IDBTransaction[%llu] DT Start", 1.583 + mTransaction->GetSerialNumber()); 1.584 + 1.585 + nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue; 1.586 + nsCOMPtr<nsIRunnable> finishRunnable; 1.587 + bool shouldFinish = false; 1.588 + 1.589 + do { 1.590 + NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!"); 1.591 + 1.592 + { 1.593 + MonitorAutoLock lock(mMonitor); 1.594 + while (!mShouldFinish && mQueue.IsEmpty()) { 1.595 + if (NS_FAILED(mMonitor.Wait())) { 1.596 + NS_ERROR("Failed to wait!"); 1.597 + } 1.598 + } 1.599 + 1.600 + mQueue.SwapElements(queue); 1.601 + if (mShouldFinish) { 1.602 + mFinishRunnable.swap(finishRunnable); 1.603 + shouldFinish = true; 1.604 + } 1.605 + } 1.606 + 1.607 + uint32_t count = queue.Length(); 1.608 + for (uint32_t index = 0; index < count; index++) { 1.609 + nsCOMPtr<nsIRunnable>& runnable = queue[index]; 1.610 + runnable->Run(); 1.611 + runnable = nullptr; 1.612 + } 1.613 + 1.614 + if (count) { 1.615 + queue.Clear(); 1.616 + } 1.617 + } while (!shouldFinish); 1.618 + 1.619 + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work", 1.620 + "IDBTransaction[%llu] DT Done", 1.621 + mTransaction->GetSerialNumber()); 1.622 + 1.623 + nsCOMPtr<nsIRunnable> finishTransactionRunnable = 1.624 + new FinishTransactionRunnable(mTransaction, finishRunnable); 1.625 + if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable, 1.626 + NS_DISPATCH_NORMAL))) { 1.627 + NS_WARNING("Failed to dispatch finishTransactionRunnable!"); 1.628 + } 1.629 + 1.630 + return NS_OK; 1.631 +} 1.632 + 1.633 +FinishTransactionRunnable::FinishTransactionRunnable( 1.634 + IDBTransaction* aTransaction, 1.635 + nsCOMPtr<nsIRunnable>& aFinishRunnable) 1.636 +: mTransaction(aTransaction) 1.637 +{ 1.638 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.639 + NS_ASSERTION(aTransaction, "Null pointer!"); 1.640 + mFinishRunnable.swap(aFinishRunnable); 1.641 +} 1.642 + 1.643 +NS_IMPL_ISUPPORTS(FinishTransactionRunnable, nsIRunnable) 1.644 + 1.645 +NS_IMETHODIMP 1.646 +FinishTransactionRunnable::Run() 1.647 +{ 1.648 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.649 + 1.650 + PROFILER_MAIN_THREAD_LABEL("IndexedDB", "FinishTransactionRunnable::Run"); 1.651 + 1.652 + if (!gThreadPool) { 1.653 + NS_ERROR("Running after shutdown!"); 1.654 + return NS_ERROR_FAILURE; 1.655 + } 1.656 + 1.657 + gThreadPool->FinishTransaction(mTransaction); 1.658 + 1.659 + if (mFinishRunnable) { 1.660 + mFinishRunnable->Run(); 1.661 + mFinishRunnable = nullptr; 1.662 + } 1.663 + 1.664 + return NS_OK; 1.665 +} 1.666 + 1.667 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.668 + 1.669 +NS_IMPL_ISUPPORTS(TransactionThreadPoolListener, nsIThreadPoolListener) 1.670 + 1.671 +NS_IMETHODIMP 1.672 +TransactionThreadPoolListener::OnThreadCreated() 1.673 +{ 1.674 + MOZ_ASSERT(!NS_IsMainThread()); 1.675 + char aLocal; 1.676 + profiler_register_thread("IndexedDB Transaction", &aLocal); 1.677 + return NS_OK; 1.678 +} 1.679 + 1.680 +NS_IMETHODIMP 1.681 +TransactionThreadPoolListener::OnThreadShuttingDown() 1.682 +{ 1.683 + MOZ_ASSERT(!NS_IsMainThread()); 1.684 + profiler_unregister_thread(); 1.685 + return NS_OK; 1.686 +} 1.687 + 1.688 +#endif // MOZ_ENABLE_PROFILER_SPS