From ebc89f5e512e14d2d0d2a6c8a427681a5b30c179 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Wed, 10 Apr 2013 09:27:00 -0700 Subject: [PATCH] Bug 859591 - 'Aborted transactions sometimes block all remaining transactions forever'. r=khuey. --- dom/indexedDB/TransactionThreadPool.cpp | 3 +- dom/indexedDB/test/Makefile.in | 1 + .../test/test_transaction_abort_hang.html | 19 ++++ dom/indexedDB/test/unit/Makefile.in | 1 + .../test/unit/test_transaction_abort_hang.js | 91 +++++++++++++++++++ dom/indexedDB/test/unit/xpcshell.ini | 1 + 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 dom/indexedDB/test/test_transaction_abort_hang.html create mode 100644 dom/indexedDB/test/unit/test_transaction_abort_hang.js diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index 80f38fd7f038..d1c65f90b2ef 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -167,8 +167,7 @@ TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey* aK NS_ASSERTION(maybeUnblockedInfo->blockedOn.Contains(finishedInfo), "Huh?"); maybeUnblockedInfo->blockedOn.RemoveEntry(finishedInfo); - if (!maybeUnblockedInfo->blockedOn.Count() && - !maybeUnblockedInfo->transaction->IsAborted()) { + if (!maybeUnblockedInfo->blockedOn.Count()) { // Let this transaction run. maybeUnblockedInfo->queue->Unblock(); } diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index 9dc7f0e8384c..418bdf0191c8 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -98,6 +98,7 @@ MOCHITEST_FILES = \ test_third_party.html \ test_traffic_jam.html \ test_transaction_abort.html \ + test_transaction_abort_hang.html \ test_transaction_lifetimes.html \ test_transaction_lifetimes_nested.html \ test_transaction_ordering.html \ diff --git a/dom/indexedDB/test/test_transaction_abort_hang.html b/dom/indexedDB/test/test_transaction_abort_hang.html new file mode 100644 index 000000000000..3a0fcfb4e661 --- /dev/null +++ b/dom/indexedDB/test/test_transaction_abort_hang.html @@ -0,0 +1,19 @@ + + + + Indexed Database Property Test + + + + + + + + + + + + diff --git a/dom/indexedDB/test/unit/Makefile.in b/dom/indexedDB/test/unit/Makefile.in index b1596fcea5d1..5ca89e444df7 100644 --- a/dom/indexedDB/test/unit/Makefile.in +++ b/dom/indexedDB/test/unit/Makefile.in @@ -63,6 +63,7 @@ MOCHITEST_FILES = \ test_success_events_after_abort.js \ test_traffic_jam.js \ test_transaction_abort.js \ + test_transaction_abort_hang.js \ test_transaction_lifetimes.js \ test_transaction_lifetimes_nested.js \ test_transaction_ordering.js \ diff --git a/dom/indexedDB/test/unit/test_transaction_abort_hang.js b/dom/indexedDB/test/unit/test_transaction_abort_hang.js new file mode 100644 index 000000000000..35a9e4d59da7 --- /dev/null +++ b/dom/indexedDB/test/unit/test_transaction_abort_hang.js @@ -0,0 +1,91 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +var self = this; + +var testGenerator = testSteps(); + +function testSteps() +{ + const dbName = self.window ? + window.location.pathname : + "test_transaction_abort_hang"; + const objStoreName = "foo"; + const transactionCount = 30; + + let completedTransactionCount = 0; + let caughtError = false; + + let abortedTransactionIndex = Math.floor(transactionCount / 2); + if (abortedTransactionIndex % 2 == 0) { + abortedTransactionIndex++; + } + + let request = indexedDB.open(dbName, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + let event = yield; + + request.result.createObjectStore(objStoreName, { autoIncrement: true }); + + request.onupgradeneeded = null; + request.onsuccess = grabEventAndContinueHandler; + event = yield; + + let db = event.target.result; + + for (let i = 0; i < transactionCount; i++) { + const readonly = i % 2 == 0; + const mode = readonly ? "readonly" : "readwrite"; + + let transaction = db.transaction(objStoreName, mode); + + if (i == transactionCount - 1) { + // Last one, finish the test. + transaction.oncomplete = grabEventAndContinueHandler; + } else if (i == abortedTransactionIndex - 1) { + transaction.oncomplete = function(event) { + ok(true, "Completed transaction " + ++completedTransactionCount + + " (We may hang after this!)"); + }; + } else if (i == abortedTransactionIndex) { + // Special transaction that we abort outside the normal event flow. + transaction.onerror = function(event) { + ok(true, "Aborted transaction " + ++completedTransactionCount + + " (We didn't hang!)"); + is(event.target.error.name, "AbortError", + "AbortError set as the error on the request"); + is(event.target.transaction.error, null, + "No error set on the transaction"); + ok(!caughtError, "Haven't seen the error event yet"); + caughtError = true; + event.preventDefault(); + }; + // This has to happen after the we return to the event loop but before the + // transaction starts running. + executeSoon(function() { transaction.abort(); }); + } else { + transaction.oncomplete = function(event) { + ok(true, "Completed transaction " + ++completedTransactionCount); + }; + } + + if (readonly) { + transaction.objectStore(objStoreName).get(0); + } else { + try { transaction.objectStore(objStoreName).add({}); } catch(e) { } + } + } + ok(true, "Created all transactions"); + + event = yield; + + ok(true, "Completed transaction " + ++completedTransactionCount); + ok(caughtError, "Caught the error event when we aborted the transaction"); + + finishTest(); + yield; +} diff --git a/dom/indexedDB/test/unit/xpcshell.ini b/dom/indexedDB/test/unit/xpcshell.ini index b3595bd511e1..dbeda858f0bc 100644 --- a/dom/indexedDB/test/unit/xpcshell.ini +++ b/dom/indexedDB/test/unit/xpcshell.ini @@ -56,6 +56,7 @@ tail = [test_success_events_after_abort.js] [test_traffic_jam.js] [test_transaction_abort.js] +[test_transaction_abort_hang.js] [test_transaction_lifetimes.js] [test_transaction_lifetimes_nested.js] [test_transaction_ordering.js]