dom/indexedDB/TransactionThreadPool.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "TransactionThreadPool.h"
michael@0 8
michael@0 9 #include "nsIObserverService.h"
michael@0 10 #include "nsIThreadPool.h"
michael@0 11
michael@0 12 #include "nsComponentManagerUtils.h"
michael@0 13 #include "nsThreadUtils.h"
michael@0 14 #include "nsServiceManagerUtils.h"
michael@0 15 #include "nsXPCOMCIDInternal.h"
michael@0 16
michael@0 17 #include "ProfilerHelpers.h"
michael@0 18
michael@0 19 using mozilla::MonitorAutoLock;
michael@0 20
michael@0 21 USING_INDEXEDDB_NAMESPACE
michael@0 22
michael@0 23 namespace {
michael@0 24
michael@0 25 const uint32_t kThreadLimit = 20;
michael@0 26 const uint32_t kIdleThreadLimit = 5;
michael@0 27 const uint32_t kIdleThreadTimeoutMs = 30000;
michael@0 28
michael@0 29 TransactionThreadPool* gThreadPool = nullptr;
michael@0 30 bool gShutdown = false;
michael@0 31
michael@0 32 #ifdef MOZ_ENABLE_PROFILER_SPS
michael@0 33
michael@0 34 class TransactionThreadPoolListener : public nsIThreadPoolListener
michael@0 35 {
michael@0 36 public:
michael@0 37 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 38 NS_DECL_NSITHREADPOOLLISTENER
michael@0 39
michael@0 40 private:
michael@0 41 virtual ~TransactionThreadPoolListener()
michael@0 42 { }
michael@0 43 };
michael@0 44
michael@0 45 #endif // MOZ_ENABLE_PROFILER_SPS
michael@0 46
michael@0 47 } // anonymous namespace
michael@0 48
michael@0 49 BEGIN_INDEXEDDB_NAMESPACE
michael@0 50
michael@0 51 class FinishTransactionRunnable MOZ_FINAL : public nsIRunnable
michael@0 52 {
michael@0 53 public:
michael@0 54 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 55 NS_DECL_NSIRUNNABLE
michael@0 56
michael@0 57 inline FinishTransactionRunnable(IDBTransaction* aTransaction,
michael@0 58 nsCOMPtr<nsIRunnable>& aFinishRunnable);
michael@0 59
michael@0 60 private:
michael@0 61 IDBTransaction* mTransaction;
michael@0 62 nsCOMPtr<nsIRunnable> mFinishRunnable;
michael@0 63 };
michael@0 64
michael@0 65 END_INDEXEDDB_NAMESPACE
michael@0 66
michael@0 67 TransactionThreadPool::TransactionThreadPool()
michael@0 68 {
michael@0 69 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 70 NS_ASSERTION(!gThreadPool, "More than one instance!");
michael@0 71 }
michael@0 72
michael@0 73 TransactionThreadPool::~TransactionThreadPool()
michael@0 74 {
michael@0 75 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 76 NS_ASSERTION(gThreadPool == this, "Different instances!");
michael@0 77 gThreadPool = nullptr;
michael@0 78 }
michael@0 79
michael@0 80 // static
michael@0 81 TransactionThreadPool*
michael@0 82 TransactionThreadPool::GetOrCreate()
michael@0 83 {
michael@0 84 if (!gThreadPool && !gShutdown) {
michael@0 85 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 86 nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool());
michael@0 87
michael@0 88 nsresult rv = pool->Init();
michael@0 89 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 90
michael@0 91 gThreadPool = pool.forget();
michael@0 92 }
michael@0 93 return gThreadPool;
michael@0 94 }
michael@0 95
michael@0 96 // static
michael@0 97 TransactionThreadPool*
michael@0 98 TransactionThreadPool::Get()
michael@0 99 {
michael@0 100 return gThreadPool;
michael@0 101 }
michael@0 102
michael@0 103 // static
michael@0 104 void
michael@0 105 TransactionThreadPool::Shutdown()
michael@0 106 {
michael@0 107 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 108
michael@0 109 gShutdown = true;
michael@0 110
michael@0 111 if (gThreadPool) {
michael@0 112 if (NS_FAILED(gThreadPool->Cleanup())) {
michael@0 113 NS_WARNING("Failed to shutdown thread pool!");
michael@0 114 }
michael@0 115 delete gThreadPool;
michael@0 116 gThreadPool = nullptr;
michael@0 117 }
michael@0 118 }
michael@0 119
michael@0 120 nsresult
michael@0 121 TransactionThreadPool::Init()
michael@0 122 {
michael@0 123 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 124
michael@0 125 nsresult rv;
michael@0 126 mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
michael@0 127 NS_ENSURE_SUCCESS(rv, rv);
michael@0 128
michael@0 129 rv = mThreadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Trans"));
michael@0 130 NS_ENSURE_SUCCESS(rv, rv);
michael@0 131
michael@0 132 rv = mThreadPool->SetThreadLimit(kThreadLimit);
michael@0 133 NS_ENSURE_SUCCESS(rv, rv);
michael@0 134
michael@0 135 rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
michael@0 136 NS_ENSURE_SUCCESS(rv, rv);
michael@0 137
michael@0 138 rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
michael@0 139 NS_ENSURE_SUCCESS(rv, rv);
michael@0 140
michael@0 141 #ifdef MOZ_ENABLE_PROFILER_SPS
michael@0 142 nsCOMPtr<nsIThreadPoolListener> listener =
michael@0 143 new TransactionThreadPoolListener();
michael@0 144
michael@0 145 rv = mThreadPool->SetListener(listener);
michael@0 146 NS_ENSURE_SUCCESS(rv, rv);
michael@0 147 #endif
michael@0 148
michael@0 149 return NS_OK;
michael@0 150 }
michael@0 151
michael@0 152 nsresult
michael@0 153 TransactionThreadPool::Cleanup()
michael@0 154 {
michael@0 155 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 156
michael@0 157 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "TransactionThreadPool::Cleanup");
michael@0 158
michael@0 159 nsresult rv = mThreadPool->Shutdown();
michael@0 160 NS_ENSURE_SUCCESS(rv, rv);
michael@0 161
michael@0 162 // Make sure the pool is still accessible while any callbacks generated from
michael@0 163 // the other threads are processed.
michael@0 164 rv = NS_ProcessPendingEvents(nullptr);
michael@0 165 NS_ENSURE_SUCCESS(rv, rv);
michael@0 166
michael@0 167 if (!mCompleteCallbacks.IsEmpty()) {
michael@0 168 // Run all callbacks manually now.
michael@0 169 for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) {
michael@0 170 mCompleteCallbacks[index].mCallback->Run();
michael@0 171 }
michael@0 172 mCompleteCallbacks.Clear();
michael@0 173
michael@0 174 // And make sure they get processed.
michael@0 175 rv = NS_ProcessPendingEvents(nullptr);
michael@0 176 NS_ENSURE_SUCCESS(rv, rv);
michael@0 177 }
michael@0 178
michael@0 179 return NS_OK;
michael@0 180 }
michael@0 181
michael@0 182 // static
michael@0 183 PLDHashOperator
michael@0 184 TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey<TransactionInfo>* aKey,
michael@0 185 void* aUserArg)
michael@0 186 {
michael@0 187 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 188
michael@0 189 TransactionInfo* maybeUnblockedInfo = aKey->GetKey();
michael@0 190 TransactionInfo* finishedInfo = static_cast<TransactionInfo*>(aUserArg);
michael@0 191
michael@0 192 NS_ASSERTION(maybeUnblockedInfo->blockedOn.Contains(finishedInfo),
michael@0 193 "Huh?");
michael@0 194 maybeUnblockedInfo->blockedOn.RemoveEntry(finishedInfo);
michael@0 195 if (!maybeUnblockedInfo->blockedOn.Count()) {
michael@0 196 // Let this transaction run.
michael@0 197 maybeUnblockedInfo->queue->Unblock();
michael@0 198 }
michael@0 199
michael@0 200 return PL_DHASH_NEXT;
michael@0 201 }
michael@0 202
michael@0 203 void
michael@0 204 TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
michael@0 205 {
michael@0 206 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 207 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 208
michael@0 209 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
michael@0 210 "TransactionThreadPool::FinishTransaction");
michael@0 211
michael@0 212 // AddRef here because removing from the hash will call Release.
michael@0 213 nsRefPtr<IDBTransaction> transaction(aTransaction);
michael@0 214
michael@0 215 const nsACString& databaseId = aTransaction->mDatabase->Id();
michael@0 216
michael@0 217 DatabaseTransactionInfo* dbTransactionInfo;
michael@0 218 if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
michael@0 219 NS_ERROR("We don't know anyting about this database?!");
michael@0 220 return;
michael@0 221 }
michael@0 222
michael@0 223 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress =
michael@0 224 dbTransactionInfo->transactions;
michael@0 225
michael@0 226 uint32_t transactionCount = transactionsInProgress.Count();
michael@0 227
michael@0 228 #ifdef DEBUG
michael@0 229 if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {
michael@0 230 NS_ASSERTION(transactionCount == 1,
michael@0 231 "More transactions running than should be!");
michael@0 232 }
michael@0 233 #endif
michael@0 234
michael@0 235 if (transactionCount == 1) {
michael@0 236 #ifdef DEBUG
michael@0 237 {
michael@0 238 const TransactionInfo* info = transactionsInProgress.Get(aTransaction);
michael@0 239 NS_ASSERTION(info->transaction == aTransaction, "Transaction mismatch!");
michael@0 240 }
michael@0 241 #endif
michael@0 242 mTransactionsInProgress.Remove(databaseId);
michael@0 243
michael@0 244 // See if we need to fire any complete callbacks.
michael@0 245 uint32_t index = 0;
michael@0 246 while (index < mCompleteCallbacks.Length()) {
michael@0 247 if (MaybeFireCallback(mCompleteCallbacks[index])) {
michael@0 248 mCompleteCallbacks.RemoveElementAt(index);
michael@0 249 }
michael@0 250 else {
michael@0 251 index++;
michael@0 252 }
michael@0 253 }
michael@0 254
michael@0 255 return;
michael@0 256 }
michael@0 257 TransactionInfo* info = transactionsInProgress.Get(aTransaction);
michael@0 258 NS_ASSERTION(info, "We've never heard of this transaction?!?");
michael@0 259
michael@0 260 const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
michael@0 261 for (uint32_t index = 0, count = objectStoreNames.Length(); index < count;
michael@0 262 index++) {
michael@0 263 TransactionInfoPair* blockInfo =
michael@0 264 dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]);
michael@0 265 NS_ASSERTION(blockInfo, "Huh?");
michael@0 266
michael@0 267 if (aTransaction->mMode == IDBTransaction::READ_WRITE &&
michael@0 268 blockInfo->lastBlockingReads == info) {
michael@0 269 blockInfo->lastBlockingReads = nullptr;
michael@0 270 }
michael@0 271
michael@0 272 uint32_t i = blockInfo->lastBlockingWrites.IndexOf(info);
michael@0 273 if (i != blockInfo->lastBlockingWrites.NoIndex) {
michael@0 274 blockInfo->lastBlockingWrites.RemoveElementAt(i);
michael@0 275 }
michael@0 276 }
michael@0 277
michael@0 278 info->blocking.EnumerateEntries(MaybeUnblockTransaction, info);
michael@0 279
michael@0 280 transactionsInProgress.Remove(aTransaction);
michael@0 281 }
michael@0 282
michael@0 283 TransactionThreadPool::TransactionQueue&
michael@0 284 TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction)
michael@0 285 {
michael@0 286 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 287 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 288
michael@0 289 const nsACString& databaseId = aTransaction->mDatabase->Id();
michael@0 290
michael@0 291 const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
michael@0 292 const uint16_t mode = aTransaction->mMode;
michael@0 293
michael@0 294 // See if we can run this transaction now.
michael@0 295 DatabaseTransactionInfo* dbTransactionInfo;
michael@0 296 if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
michael@0 297 // First transaction for this database.
michael@0 298 dbTransactionInfo = new DatabaseTransactionInfo();
michael@0 299 mTransactionsInProgress.Put(databaseId, dbTransactionInfo);
michael@0 300 }
michael@0 301
michael@0 302 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress =
michael@0 303 dbTransactionInfo->transactions;
michael@0 304 TransactionInfo* info = transactionsInProgress.Get(aTransaction);
michael@0 305 if (info) {
michael@0 306 // We recognize this one.
michael@0 307 return *info->queue;
michael@0 308 }
michael@0 309
michael@0 310 TransactionInfo* transactionInfo = new TransactionInfo(aTransaction);
michael@0 311
michael@0 312 dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);;
michael@0 313
michael@0 314 for (uint32_t index = 0, count = objectStoreNames.Length(); index < count;
michael@0 315 index++) {
michael@0 316 TransactionInfoPair* blockInfo =
michael@0 317 dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]);
michael@0 318 if (!blockInfo) {
michael@0 319 blockInfo = new TransactionInfoPair();
michael@0 320 blockInfo->lastBlockingReads = nullptr;
michael@0 321 dbTransactionInfo->blockingTransactions.Put(objectStoreNames[index],
michael@0 322 blockInfo);
michael@0 323 }
michael@0 324
michael@0 325 // Mark what we are blocking on.
michael@0 326 if (blockInfo->lastBlockingReads) {
michael@0 327 TransactionInfo* blockingInfo = blockInfo->lastBlockingReads;
michael@0 328 transactionInfo->blockedOn.PutEntry(blockingInfo);
michael@0 329 blockingInfo->blocking.PutEntry(transactionInfo);
michael@0 330 }
michael@0 331
michael@0 332 if (mode == IDBTransaction::READ_WRITE &&
michael@0 333 blockInfo->lastBlockingWrites.Length()) {
michael@0 334 for (uint32_t index = 0,
michael@0 335 count = blockInfo->lastBlockingWrites.Length(); index < count;
michael@0 336 index++) {
michael@0 337 TransactionInfo* blockingInfo = blockInfo->lastBlockingWrites[index];
michael@0 338 transactionInfo->blockedOn.PutEntry(blockingInfo);
michael@0 339 blockingInfo->blocking.PutEntry(transactionInfo);
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 if (mode == IDBTransaction::READ_WRITE) {
michael@0 344 blockInfo->lastBlockingReads = transactionInfo;
michael@0 345 blockInfo->lastBlockingWrites.Clear();
michael@0 346 }
michael@0 347 else {
michael@0 348 blockInfo->lastBlockingWrites.AppendElement(transactionInfo);
michael@0 349 }
michael@0 350 }
michael@0 351
michael@0 352 if (!transactionInfo->blockedOn.Count()) {
michael@0 353 transactionInfo->queue->Unblock();
michael@0 354 }
michael@0 355
michael@0 356 return *transactionInfo->queue;
michael@0 357 }
michael@0 358
michael@0 359 nsresult
michael@0 360 TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
michael@0 361 nsIRunnable* aRunnable,
michael@0 362 bool aFinish,
michael@0 363 nsIRunnable* aFinishRunnable)
michael@0 364 {
michael@0 365 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 366 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 367 NS_ASSERTION(aRunnable, "Null pointer!");
michael@0 368
michael@0 369 if (aTransaction->mDatabase->IsInvalidated() && !aFinish) {
michael@0 370 return NS_ERROR_NOT_AVAILABLE;
michael@0 371 }
michael@0 372
michael@0 373 TransactionQueue& queue = GetQueueForTransaction(aTransaction);
michael@0 374
michael@0 375 queue.Dispatch(aRunnable);
michael@0 376 if (aFinish) {
michael@0 377 queue.Finish(aFinishRunnable);
michael@0 378 }
michael@0 379 return NS_OK;
michael@0 380 }
michael@0 381
michael@0 382 void
michael@0 383 TransactionThreadPool::WaitForDatabasesToComplete(
michael@0 384 nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
michael@0 385 nsIRunnable* aCallback)
michael@0 386 {
michael@0 387 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 388 NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!");
michael@0 389 NS_ASSERTION(aCallback, "Null pointer!");
michael@0 390
michael@0 391 DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
michael@0 392
michael@0 393 callback->mCallback = aCallback;
michael@0 394 callback->mDatabases.SwapElements(aDatabases);
michael@0 395
michael@0 396 if (MaybeFireCallback(*callback)) {
michael@0 397 mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
michael@0 398 }
michael@0 399 }
michael@0 400
michael@0 401 // static
michael@0 402 PLDHashOperator
michael@0 403 TransactionThreadPool::CollectTransactions(IDBTransaction* aKey,
michael@0 404 TransactionInfo* aValue,
michael@0 405 void* aUserArg)
michael@0 406 {
michael@0 407 nsAutoTArray<nsRefPtr<IDBTransaction>, 50>* transactionArray =
michael@0 408 static_cast<nsAutoTArray<nsRefPtr<IDBTransaction>, 50>*>(aUserArg);
michael@0 409 transactionArray->AppendElement(aKey);
michael@0 410
michael@0 411 return PL_DHASH_NEXT;
michael@0 412 }
michael@0 413
michael@0 414 void
michael@0 415 TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase)
michael@0 416 {
michael@0 417 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 418 NS_ASSERTION(aDatabase, "Null pointer!");
michael@0 419
michael@0 420 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
michael@0 421 "TransactionThreadPool::"
michael@0 422 "AbortTransactionsForDatabase");
michael@0 423
michael@0 424 // Get list of transactions for this database id
michael@0 425 DatabaseTransactionInfo* dbTransactionInfo;
michael@0 426 if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) {
michael@0 427 // If there are no transactions, we're done.
michael@0 428 return;
michael@0 429 }
michael@0 430
michael@0 431 // Collect any running transactions
michael@0 432 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress =
michael@0 433 dbTransactionInfo->transactions;
michael@0 434
michael@0 435 NS_ASSERTION(transactionsInProgress.Count(), "Should never be 0!");
michael@0 436
michael@0 437 nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions;
michael@0 438 transactionsInProgress.EnumerateRead(CollectTransactions, &transactions);
michael@0 439
michael@0 440 // Abort transactions. Do this after collecting the transactions in case
michael@0 441 // calling Abort() modifies the data structures we're iterating above.
michael@0 442 for (uint32_t index = 0; index < transactions.Length(); index++) {
michael@0 443 if (transactions[index]->Database() != aDatabase) {
michael@0 444 continue;
michael@0 445 }
michael@0 446
michael@0 447 // This can fail, for example if the transaction is in the process of
michael@0 448 // being comitted. That is expected and fine, so we ignore any returned
michael@0 449 // errors.
michael@0 450 ErrorResult rv;
michael@0 451 transactions[index]->Abort(rv);
michael@0 452 }
michael@0 453 }
michael@0 454
michael@0 455 struct MOZ_STACK_CLASS TransactionSearchInfo
michael@0 456 {
michael@0 457 TransactionSearchInfo(nsIOfflineStorage* aDatabase)
michael@0 458 : db(aDatabase), found(false)
michael@0 459 {
michael@0 460 }
michael@0 461
michael@0 462 nsIOfflineStorage* db;
michael@0 463 bool found;
michael@0 464 };
michael@0 465
michael@0 466 // static
michael@0 467 PLDHashOperator
michael@0 468 TransactionThreadPool::FindTransaction(IDBTransaction* aKey,
michael@0 469 TransactionInfo* aValue,
michael@0 470 void* aUserArg)
michael@0 471 {
michael@0 472 TransactionSearchInfo* info = static_cast<TransactionSearchInfo*>(aUserArg);
michael@0 473
michael@0 474 if (aKey->Database() == info->db) {
michael@0 475 info->found = true;
michael@0 476 return PL_DHASH_STOP;
michael@0 477 }
michael@0 478
michael@0 479 return PL_DHASH_NEXT;
michael@0 480 }
michael@0 481 bool
michael@0 482 TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase)
michael@0 483 {
michael@0 484 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 485 NS_ASSERTION(aDatabase, "Null pointer!");
michael@0 486
michael@0 487 DatabaseTransactionInfo* dbTransactionInfo = nullptr;
michael@0 488 dbTransactionInfo = mTransactionsInProgress.Get(aDatabase->Id());
michael@0 489 if (!dbTransactionInfo) {
michael@0 490 return false;
michael@0 491 }
michael@0 492
michael@0 493 TransactionSearchInfo info(aDatabase);
michael@0 494 dbTransactionInfo->transactions.EnumerateRead(FindTransaction, &info);
michael@0 495
michael@0 496 return info.found;
michael@0 497 }
michael@0 498
michael@0 499 bool
michael@0 500 TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback aCallback)
michael@0 501 {
michael@0 502 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 503
michael@0 504 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
michael@0 505 "TransactionThreadPool::MaybeFireCallback");
michael@0 506
michael@0 507 for (uint32_t index = 0; index < aCallback.mDatabases.Length(); index++) {
michael@0 508 IDBDatabase* database = aCallback.mDatabases[index];
michael@0 509 if (!database) {
michael@0 510 MOZ_CRASH();
michael@0 511 }
michael@0 512
michael@0 513 if (mTransactionsInProgress.Get(database->Id(), nullptr)) {
michael@0 514 return false;
michael@0 515 }
michael@0 516 }
michael@0 517
michael@0 518 aCallback.mCallback->Run();
michael@0 519 return true;
michael@0 520 }
michael@0 521
michael@0 522 TransactionThreadPool::
michael@0 523 TransactionQueue::TransactionQueue(IDBTransaction* aTransaction)
michael@0 524 : mMonitor("TransactionQueue::mMonitor"),
michael@0 525 mTransaction(aTransaction),
michael@0 526 mShouldFinish(false)
michael@0 527 {
michael@0 528 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 529 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 530 }
michael@0 531
michael@0 532 void
michael@0 533 TransactionThreadPool::TransactionQueue::Unblock()
michael@0 534 {
michael@0 535 MonitorAutoLock lock(mMonitor);
michael@0 536
michael@0 537 // NB: Finish may be called before Unblock.
michael@0 538
michael@0 539 TransactionThreadPool::Get()->mThreadPool->
michael@0 540 Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 541 }
michael@0 542
michael@0 543 void
michael@0 544 TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable)
michael@0 545 {
michael@0 546 MonitorAutoLock lock(mMonitor);
michael@0 547
michael@0 548 NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!");
michael@0 549
michael@0 550 mQueue.AppendElement(aRunnable);
michael@0 551
michael@0 552 mMonitor.Notify();
michael@0 553 }
michael@0 554
michael@0 555 void
michael@0 556 TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable)
michael@0 557 {
michael@0 558 MonitorAutoLock lock(mMonitor);
michael@0 559
michael@0 560 NS_ASSERTION(!mShouldFinish, "Finish called more than once!");
michael@0 561
michael@0 562 mShouldFinish = true;
michael@0 563 mFinishRunnable = aFinishRunnable;
michael@0 564
michael@0 565 mMonitor.Notify();
michael@0 566 }
michael@0 567
michael@0 568 NS_IMPL_ISUPPORTS(TransactionThreadPool::TransactionQueue, nsIRunnable)
michael@0 569
michael@0 570 NS_IMETHODIMP
michael@0 571 TransactionThreadPool::TransactionQueue::Run()
michael@0 572 {
michael@0 573 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 574 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 575
michael@0 576 PROFILER_LABEL("IndexedDB", "TransactionQueue::Run");
michael@0 577
michael@0 578 IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work",
michael@0 579 "IDBTransaction[%llu] DT Start",
michael@0 580 mTransaction->GetSerialNumber());
michael@0 581
michael@0 582 nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue;
michael@0 583 nsCOMPtr<nsIRunnable> finishRunnable;
michael@0 584 bool shouldFinish = false;
michael@0 585
michael@0 586 do {
michael@0 587 NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!");
michael@0 588
michael@0 589 {
michael@0 590 MonitorAutoLock lock(mMonitor);
michael@0 591 while (!mShouldFinish && mQueue.IsEmpty()) {
michael@0 592 if (NS_FAILED(mMonitor.Wait())) {
michael@0 593 NS_ERROR("Failed to wait!");
michael@0 594 }
michael@0 595 }
michael@0 596
michael@0 597 mQueue.SwapElements(queue);
michael@0 598 if (mShouldFinish) {
michael@0 599 mFinishRunnable.swap(finishRunnable);
michael@0 600 shouldFinish = true;
michael@0 601 }
michael@0 602 }
michael@0 603
michael@0 604 uint32_t count = queue.Length();
michael@0 605 for (uint32_t index = 0; index < count; index++) {
michael@0 606 nsCOMPtr<nsIRunnable>& runnable = queue[index];
michael@0 607 runnable->Run();
michael@0 608 runnable = nullptr;
michael@0 609 }
michael@0 610
michael@0 611 if (count) {
michael@0 612 queue.Clear();
michael@0 613 }
michael@0 614 } while (!shouldFinish);
michael@0 615
michael@0 616 IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work",
michael@0 617 "IDBTransaction[%llu] DT Done",
michael@0 618 mTransaction->GetSerialNumber());
michael@0 619
michael@0 620 nsCOMPtr<nsIRunnable> finishTransactionRunnable =
michael@0 621 new FinishTransactionRunnable(mTransaction, finishRunnable);
michael@0 622 if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable,
michael@0 623 NS_DISPATCH_NORMAL))) {
michael@0 624 NS_WARNING("Failed to dispatch finishTransactionRunnable!");
michael@0 625 }
michael@0 626
michael@0 627 return NS_OK;
michael@0 628 }
michael@0 629
michael@0 630 FinishTransactionRunnable::FinishTransactionRunnable(
michael@0 631 IDBTransaction* aTransaction,
michael@0 632 nsCOMPtr<nsIRunnable>& aFinishRunnable)
michael@0 633 : mTransaction(aTransaction)
michael@0 634 {
michael@0 635 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 636 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 637 mFinishRunnable.swap(aFinishRunnable);
michael@0 638 }
michael@0 639
michael@0 640 NS_IMPL_ISUPPORTS(FinishTransactionRunnable, nsIRunnable)
michael@0 641
michael@0 642 NS_IMETHODIMP
michael@0 643 FinishTransactionRunnable::Run()
michael@0 644 {
michael@0 645 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 646
michael@0 647 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "FinishTransactionRunnable::Run");
michael@0 648
michael@0 649 if (!gThreadPool) {
michael@0 650 NS_ERROR("Running after shutdown!");
michael@0 651 return NS_ERROR_FAILURE;
michael@0 652 }
michael@0 653
michael@0 654 gThreadPool->FinishTransaction(mTransaction);
michael@0 655
michael@0 656 if (mFinishRunnable) {
michael@0 657 mFinishRunnable->Run();
michael@0 658 mFinishRunnable = nullptr;
michael@0 659 }
michael@0 660
michael@0 661 return NS_OK;
michael@0 662 }
michael@0 663
michael@0 664 #ifdef MOZ_ENABLE_PROFILER_SPS
michael@0 665
michael@0 666 NS_IMPL_ISUPPORTS(TransactionThreadPoolListener, nsIThreadPoolListener)
michael@0 667
michael@0 668 NS_IMETHODIMP
michael@0 669 TransactionThreadPoolListener::OnThreadCreated()
michael@0 670 {
michael@0 671 MOZ_ASSERT(!NS_IsMainThread());
michael@0 672 char aLocal;
michael@0 673 profiler_register_thread("IndexedDB Transaction", &aLocal);
michael@0 674 return NS_OK;
michael@0 675 }
michael@0 676
michael@0 677 NS_IMETHODIMP
michael@0 678 TransactionThreadPoolListener::OnThreadShuttingDown()
michael@0 679 {
michael@0 680 MOZ_ASSERT(!NS_IsMainThread());
michael@0 681 profiler_unregister_thread();
michael@0 682 return NS_OK;
michael@0 683 }
michael@0 684
michael@0 685 #endif // MOZ_ENABLE_PROFILER_SPS

mercurial