Bug 1178721 - Implement SuspendWorkersForWindow;r=khuey

This commit is contained in:
Eddy Bruel 2015-10-07 12:20:59 +02:00
parent 012753246e
commit ec8b2add25
13 changed files with 246 additions and 21 deletions

View File

@ -2081,7 +2081,7 @@ nsDOMWindowUtils::SuspendTimeouts()
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
window->SuspendTimeouts();
window->SuspendTimeouts(1, true, false);
return NS_OK;
}
@ -2092,7 +2092,7 @@ nsDOMWindowUtils::ResumeTimeouts()
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
window->ResumeTimeouts();
window->ResumeTimeouts(true, false);
return NS_OK;
}

View File

@ -13376,9 +13376,11 @@ nsGlobalWindow::RestoreWindowState(nsISupports *aState)
void
nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
bool aFreezeChildren)
bool aFreezeChildren,
bool aFreezeWorkers)
{
FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren));
FORWARD_TO_INNER_VOID(SuspendTimeouts,
(aIncrease, aFreezeChildren, aFreezeWorkers));
bool suspended = (mTimeoutsSuspendDepth != 0);
mTimeoutsSuspendDepth += aIncrease;
@ -13391,8 +13393,12 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
}
DisableGamepadUpdates();
// Freeze all of the workers for this window.
mozilla::dom::workers::FreezeWorkersForWindow(this);
// Freeze or suspend all of the workers for this window.
if (aFreezeWorkers) {
mozilla::dom::workers::FreezeWorkersForWindow(this);
} else {
mozilla::dom::workers::SuspendWorkersForWindow(this);
}
TimeStamp now = TimeStamp::Now();
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
@ -13448,7 +13454,7 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
continue;
}
win->SuspendTimeouts(aIncrease, aFreezeChildren);
win->SuspendTimeouts(aIncrease, aFreezeChildren, aFreezeWorkers);
if (inner && aFreezeChildren) {
inner->Freeze();
@ -13459,9 +13465,10 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
}
nsresult
nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
nsGlobalWindow::ResumeTimeouts(bool aThawChildren, bool aThawWorkers)
{
FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
FORWARD_TO_INNER(ResumeTimeouts, (aThawChildren, aThawWorkers),
NS_ERROR_NOT_INITIALIZED);
NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!");
--mTimeoutsSuspendDepth;
@ -13482,8 +13489,12 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
nsRefPtr<Promise> d = mAudioContexts[i]->Resume(dummy);
}
// Thaw all of the workers for this window.
mozilla::dom::workers::ThawWorkersForWindow(this);
// Thaw or resume all of the workers for this window.
if (aThawWorkers) {
mozilla::dom::workers::ThawWorkersForWindow(this);
} else {
mozilla::dom::workers::ResumeWorkersForWindow(this);
}
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mTimeRemaining).
@ -13562,7 +13573,7 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
inner->Thaw();
}
rv = win->ResumeTimeouts(aThawChildren);
rv = win->ResumeTimeouts(aThawChildren, aThawWorkers);
NS_ENSURE_SUCCESS(rv, rv);
}
}

View File

@ -445,8 +445,10 @@ public:
virtual already_AddRefed<nsISupports> SaveWindowState() override;
virtual nsresult RestoreWindowState(nsISupports *aState) override;
virtual void SuspendTimeouts(uint32_t aIncrease = 1,
bool aFreezeChildren = true) override;
virtual nsresult ResumeTimeouts(bool aThawChildren = true) override;
bool aFreezeChildren = true,
bool aFreezeWorkers = true) override;
virtual nsresult ResumeTimeouts(bool aThawChildren = true,
bool aThawWorkers = true) override;
virtual uint32_t TimeoutSuspendCount() override;
virtual nsresult FireDelayedDOMEvents() override;
virtual bool IsFrozen() const override

View File

@ -291,10 +291,12 @@ public:
// Suspend timeouts in this window and in child windows.
virtual void SuspendTimeouts(uint32_t aIncrease = 1,
bool aFreezeChildren = true) = 0;
bool aFreezeChildren = true,
bool aFreezeWorkers = true) = 0;
// Resume suspended timeouts in this window and in child windows.
virtual nsresult ResumeTimeouts(bool aThawChildren = true) = 0;
virtual nsresult ResumeTimeouts(bool aThawChildren = true,
bool aThawWorkers = true) = 0;
virtual uint32_t TimeoutSuspendCount() = 0;

View File

@ -1284,6 +1284,26 @@ ThawWorkersForWindow(nsPIDOMWindow* aWindow)
}
}
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->SuspendWorkersForWindow(aWindow);
}
}
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->ResumeWorkersForWindow(aWindow);
}
}
WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher(
WorkerPrivate* aWorkerPrivate)
: mMutex("WorkerCrossThreadDispatcher::mMutex"),
@ -2404,6 +2424,34 @@ RuntimeService::ThawWorkersForWindow(nsPIDOMWindow* aWindow)
}
}
void
RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
GetWorkersForWindow(aWindow, workers);
for (uint32_t index = 0; index < workers.Length(); index++) {
workers[index]->Suspend();
}
}
void
RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
GetWorkersForWindow(aWindow, workers);
for (uint32_t index = 0; index < workers.Length(); index++) {
workers[index]->Resume();
}
}
nsresult
RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
const nsAString& aScriptURL,

View File

@ -148,6 +148,12 @@ public:
void
ThawWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
nsresult
CreateSharedWorker(const GlobalObject& aGlobal,
const nsAString& aScriptURL,

View File

@ -678,7 +678,8 @@ private:
return true;
}
if (aWorkerPrivate->IsFrozen()) {
if (aWorkerPrivate->IsFrozen() || aWorkerPrivate->IsSuspended()) {
MOZ_ASSERT(!IsDebuggerRunnable());
aWorkerPrivate->QueueRunnable(this);
return true;
}
@ -1006,7 +1007,8 @@ private:
else {
AssertIsOnMainThread();
if (aWorkerPrivate->IsFrozen()) {
if (aWorkerPrivate->IsFrozen() || aWorkerPrivate->IsSuspended()) {
MOZ_ASSERT(!IsDebuggerRunnable());
aWorkerPrivate->QueueRunnable(this);
return true;
}
@ -2108,8 +2110,8 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
mParent(aParent), mScriptURL(aScriptURL),
mWorkerName(aWorkerName), mLoadingWorkerScript(false),
mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
mWorkerType(aWorkerType),
mParentSuspended(false), mIsChromeWorker(aIsChromeWorker),
mMainThreadObjectsForgotten(false), mWorkerType(aWorkerType),
mCreationTimeStamp(TimeStamp::Now()),
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
{
@ -2576,7 +2578,7 @@ WorkerPrivateParent<Derived>::Thaw(JSContext* aCx, nsPIDOMWindow* aWindow)
// Execute queued runnables before waking up the worker, otherwise the worker
// could post new messages before we run those that have been queued.
if (!mQueuedRunnables.IsEmpty()) {
if (!IsSuspended() && !mQueuedRunnables.IsEmpty()) {
AssertIsOnMainThread();
MOZ_ASSERT(IsDedicatedWorker());
@ -2597,6 +2599,50 @@ WorkerPrivateParent<Derived>::Thaw(JSContext* aCx, nsPIDOMWindow* aWindow)
return true;
}
template <class Derived>
void
WorkerPrivateParent<Derived>::Suspend()
{
AssertIsOnMainThread();
MOZ_ASSERT(!mParentSuspended, "Suspended more than once!");
mParentSuspended = true;
}
template <class Derived>
void
WorkerPrivateParent<Derived>::Resume()
{
AssertIsOnMainThread();
MOZ_ASSERT(mParentSuspended, "Resumed more than once!");
mParentSuspended = false;
{
MutexAutoLock lock(mMutex);
if (mParentStatus >= Terminating) {
return;
}
}
// Execute queued runnables before waking up, otherwise the worker could post
// new messages before we run those that have been queued.
if (!IsFrozen() && !mQueuedRunnables.IsEmpty()) {
AssertIsOnMainThread();
MOZ_ASSERT(IsDedicatedWorker());
nsTArray<nsCOMPtr<nsIRunnable>> runnables;
mQueuedRunnables.SwapElements(runnables);
for (uint32_t index = 0; index < runnables.Length(); index++) {
runnables[index]->Run();
}
}
}
template <class Derived>
bool
WorkerPrivateParent<Derived>::Close()

View File

@ -185,6 +185,7 @@ private:
uint64_t mBusyCount;
Status mParentStatus;
bool mParentFrozen;
bool mParentSuspended;
bool mIsChromeWorker;
bool mMainThreadObjectsForgotten;
WorkerType mWorkerType;
@ -305,6 +306,12 @@ public:
bool
Thaw(JSContext* aCx, nsPIDOMWindow* aWindow);
void
Suspend();
void
Resume();
bool
Terminate(JSContext* aCx)
{
@ -399,6 +406,13 @@ public:
return mParentFrozen;
}
bool
IsSuspended() const
{
AssertIsOnParentThread();
return mParentSuspended;
}
bool
IsAcceptingEvents()
{

View File

@ -295,6 +295,12 @@ FreezeWorkersForWindow(nsPIDOMWindow* aWindow);
void
ThawWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
class WorkerTask
{
protected:

View File

@ -0,0 +1,6 @@
"use strict";
var dbg = new Debugger(global);
dbg.onDebuggerStatement = function (frame) {
postMessage("debugger");
};

View File

@ -0,0 +1,6 @@
"use strict";
self.onmessage = function () {
postMessage("worker");
debugger;
};

View File

@ -27,6 +27,8 @@ support-files =
WorkerDebugger_childWorker.js
WorkerDebugger_worker.js
WorkerDebugger_sharedWorker.js
WorkerDebugger_suspended_debugger.js
WorkerDebugger_suspended_worker.js
WorkerTest.jsm
WorkerTest_subworker.js
WorkerTest_worker.js
@ -61,6 +63,7 @@ support-files =
[test_WorkerDebuggerGlobalScope.reportError.xul]
[test_WorkerDebuggerGlobalScope.setImmediate.xul]
[test_WorkerDebuggerManager.xul]
[test_WorkerDebugger_suspended.xul]
[test_bug883784.xul]
[test_chromeWorker.xul]
[test_chromeWorkerJSM.xul]

View File

@ -0,0 +1,75 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger with suspended workers"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebugger_suspended_worker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebugger_suspended_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker, wait for its debugger to be registered, and " +
"initialize it.");
let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
let worker = new Worker(WORKER_URL);
let dbg = yield promise;
info("Send a request to the worker. This should cause both the " +
"worker and the worker debugger to send a response.");
promise = waitForMultiple([
waitForWorkerMessage(worker, "worker"),
waitForDebuggerMessage(dbg, "debugger")
]);
worker.postMessage("ping");
yield promise;
info("Suspend the workers for this window, and send another request " +
"to the worker. This should cause only the worker debugger to " +
"send a response.");
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suspendTimeouts();
function onmessage() {
ok(false, "The worker should not send a response.");
};
worker.addEventListener("message", onmessage);
promise = waitForDebuggerMessage(dbg, "debugger");
worker.postMessage("ping");
yield promise;
worker.removeEventListener("message", onmessage);
info("Resume the workers for this window. This should cause the " +
"worker to send a response to the previous request.");
promise = waitForWorkerMessage(worker, "worker");
windowUtils.resumeTimeouts();
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>