Bug 932162 - dispatch IndexedDB FileInfo releases to the main thread. r=khuey

Try runs:
Green run prior to b2g mochitest fix and assertion changes:
https://tbpl.mozilla.org/?tree=Try&rev=b071f8ef9617
green runs with assertion changes, not b2g disabling:
https://tbpl.mozilla.org/?tree=Try&rev=b071f8ef9617
green b2g run:
https://tbpl.mozilla.org/?tree=Try&rev=67510897d368
This commit is contained in:
Andrew Sutherland 2013-11-19 06:00:20 -05:00
parent 24c0584c57
commit f9c6c42306
5 changed files with 210 additions and 6 deletions

View File

@ -10,11 +10,28 @@
USING_INDEXEDDB_NAMESPACE
namespace {
class CleanupFileRunnable MOZ_FINAL : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId);
private:
nsRefPtr<FileManager> mFileManager;
int64_t mFileId;
};
} // anonymous namespace
// static
FileInfo*
FileInfo::Create(FileManager* aFileManager, int64_t aId)
{
NS_ASSERTION(aId > 0, "Wrong id!");
MOZ_ASSERT(aId > 0, "Wrong id!");
if (aId <= INT16_MAX) {
return new FileInfo16(aFileManager, aId);
@ -98,16 +115,40 @@ FileInfo::UpdateReferences(mozilla::ThreadSafeAutoRefCnt& aRefCount,
void
FileInfo::Cleanup()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<CleanupFileRunnable> cleaner =
new CleanupFileRunnable(mFileManager, Id());
if (quota::QuotaManager::IsShuttingDown()) {
// IndexedDatabaseManager is main-thread only.
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(cleaner);
return;
}
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "Shouldn't be null!");
cleaner->Run();
}
if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, Id()))) {
CleanupFileRunnable::CleanupFileRunnable(FileManager* aFileManager,
int64_t aFileId)
: mFileManager(aFileManager), mFileId(aFileId)
{
}
NS_IMPL_ISUPPORTS1(CleanupFileRunnable,
nsIRunnable)
NS_IMETHODIMP
CleanupFileRunnable::Run()
{
if (mozilla::dom::quota::QuotaManager::IsShuttingDown()) {
return NS_OK;
}
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
MOZ_ASSERT(mgr);
if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, mFileId))) {
NS_WARNING("Failed to delete file asynchronously!");
}
return NS_OK;
}

View File

@ -0,0 +1,98 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Test</title>
<script type="text/javascript">
function report(result) {
var message = { source: "iframe" };
message.result = result;
window.parent.postMessage(message, "*");
}
function runIndexedDBTest() {
var db = null;
// Create the data-store
function createDatastore() {
try {
var request = indexedDB.open(window.location.pathname, 1);
request.onupgradeneeded = function(event) {
event.target.result.createObjectStore("foo");
}
request.onsuccess = function(event) {
db = event.target.result;
createAndStoreBlob();
}
}
catch (e) {
dump("EXCEPTION IN CREATION: " + e + "\n " + e.stack + "\n");
report(false);
}
}
function createAndStoreBlob() {
const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
var blob = new Blob(BLOB_DATA, { type: "text/plain" });
var objectStore = db.transaction("foo", "readwrite").objectStore("foo");
objectStore.add({ blob: blob }, 42).onsuccess = refetchBlob;
}
function refetchBlob() {
var foo = db.transaction("foo").objectStore("foo");
foo.get(42).onsuccess = fetchedBlobCreateWorkerAndSendBlob;
}
function fetchedBlobCreateWorkerAndSendBlob(event) {
var idbBlob = event.target.result.blob;
var compositeBlob = new Blob(['I like the following blob: ', idbBlob],
{ type: "text/fancy" });
function workerScript() {
onmessage = function(event) {
// Save the Blob to the worker's global scope.
self.holdOntoBlob = event.data;
// Send any message so we can serialize and keep our runtime behaviour
// consistent.
postMessage('kung fu death grip established');
}
}
var url =
URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
// Keep a reference to the worker on the window.
var worker = window.worker = new Worker(url);
worker.postMessage(compositeBlob);
worker.onmessage = workerLatchedBlobDeleteFromDB;
}
function workerLatchedBlobDeleteFromDB() {
// Delete the reference to the Blob from the database leaving the worker
// thread reference as the only live reference once a GC has cleaned
// out our references that we sent to the worker. The page that owns
// us triggers a GC just for that reason.
var objectStore = db.transaction("foo", "readwrite").objectStore("foo");
objectStore.delete(42).onsuccess = closeDBTellOwningThread;
}
function closeDBTellOwningThread(event) {
// Now that worker has latched the blob, clean up the database.
db.close();
db = null;
report('ready');
}
createDatastore();
}
</script>
</head>
<body onload="runIndexedDBTest();">
</body>
</html>

View File

@ -2,6 +2,7 @@
support-files =
bfcache_iframe1.html
bfcache_iframe2.html
blob_worker_crash_iframe.html
error_events_abort_transactions_iframe.html
event_propagation_iframe.html
exceptions_in_events_iframe.html
@ -26,6 +27,7 @@ support-files =
[test_bfcache.html]
[test_blob_archive.html]
[test_blob_simple.html]
[test_blob_worker_crash.html]
[test_clear.html]
[test_complex_keyPaths.html]
[test_count.html]

View File

@ -0,0 +1,62 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Blob Worker Crash Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
/*
* This tests ensures that if the last live reference to a Blob is on the
* worker and the database has already been shutdown, that there is no crash
* when the owning page gets cleaned up which causes the termination of the
* worker which in turn garbage collects during its shutdown.
*
* We do the IndexedDB stuff in the iframe so we can kill it as part of our
* test. Doing it out here is no good.
*/
function testSteps()
{
info("Open iframe, wait for it to do its IndexedDB stuff.");
let iframe = document.getElementById("iframe1");
window.addEventListener("message", grabEventAndContinueHandler, false);
// Put it in a different origin to be safe
//allowUnlimitedQuota("http://example.org/");
iframe.src = //"http://example.org" +
window.location.pathname.replace(
"test_blob_worker_crash.html",
"blob_worker_crash_iframe.html");
let event = yield unexpectedSuccessHandler;
is(event.data.result, "ready", "worker initialized correctly");
info("Trigger a GC to clean-up the iframe's main-thread IndexedDB");
scheduleGC();
yield undefined;
info("Kill the iframe, forget about it, trigger a GC.");
iframe.parentNode.removeChild(iframe);
iframe = null;
scheduleGC();
yield undefined;
info("If we are still alive, then we win!");
ok('Did not crash / trigger an assert!');
finishTest();
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
<iframe id="iframe1"></iframe>
</html>

View File

@ -504,6 +504,7 @@
"dom/indexedDB/test/test_autoIncrement_indexes.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/indexedDB/test/test_bfcache.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/indexedDB/test/test_blob_archive.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/indexedDB/test/test_blob_worker_crash.html": "Bug 931116, b2g desktop specific, bug 927889 still present",
"dom/indexedDB/test/test_blob_simple.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/indexedDB/test/test_clear.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/indexedDB/test/test_complex_keyPaths.html": "Bug 931116, b2g desktop specific, initial triage",