Suspend dedicated worker threads while in the back/forward cache
https://bugs.webkit.org/show_bug.cgi?id=203186
<rdar://problem/56447493>
Reviewed by Ryosuke Niwa.
Source/WebCore:
When a page with a (dedicated) Worker enters the back/forward cache, we now
pause the worker thread, and resume it only when taking the page out of the
back/forward cache. This avoids having the worker use CPU while the page is
in the cache.
* workers/Worker.cpp:
(WebCore::Worker::suspend):
(WebCore::Worker::resume):
* workers/Worker.h:
* workers/WorkerGlobalScopeProxy.h:
* workers/WorkerMessagingProxy.cpp:
(WebCore::WorkerMessagingProxy::suspend):
(WebCore::WorkerMessagingProxy::resume):
(WebCore::WorkerMessagingProxy::workerThreadCreated):
* workers/WorkerMessagingProxy.h:
* workers/WorkerThread.cpp:
(WebCore::WorkerThread::WorkerThread):
(WebCore::WorkerThread::suspend):
(WebCore::WorkerThread::resume):
(WebCore::WorkerThread::stop):
* workers/WorkerThread.h:
LayoutTests:
Extend layout test coverage.
* fast/workers/resources/worker-setInterval.js: Added.
(onmessage):
(setInterval):
* fast/workers/worker-page-cache.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251416 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 2ec1a7b..232d1b9 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,18 @@
+2019-10-21 Chris Dumez <cdumez@apple.com>
+
+ Suspend dedicated worker threads while in the back/forward cache
+ https://bugs.webkit.org/show_bug.cgi?id=203186
+ <rdar://problem/56447493>
+
+ Reviewed by Ryosuke Niwa.
+
+ Extend layout test coverage.
+
+ * fast/workers/resources/worker-setInterval.js: Added.
+ (onmessage):
+ (setInterval):
+ * fast/workers/worker-page-cache.html:
+
2019-10-21 Myles C. Maxfield <mmaxfield@apple.com>
[Cocoa] Move ui-serif, ui-monospaced, and ui-rounded out from behind SPI
diff --git a/LayoutTests/fast/workers/resources/worker-setInterval.js b/LayoutTests/fast/workers/resources/worker-setInterval.js
new file mode 100644
index 0000000..b1e3d99
--- /dev/null
+++ b/LayoutTests/fast/workers/resources/worker-setInterval.js
@@ -0,0 +1,12 @@
+function onmessage(evt)
+{
+ postMessage("SUCCESS");
+}
+
+let i = 0;
+setInterval(() => {
+ postMessage("" + i);
+ i++;
+}, 0);
+
+addEventListener("message", onmessage, true);
diff --git a/LayoutTests/fast/workers/worker-page-cache.html b/LayoutTests/fast/workers/worker-page-cache.html
index 141631e..d4ac90e 100644
--- a/LayoutTests/fast/workers/worker-page-cache.html
+++ b/LayoutTests/fast/workers/worker-page-cache.html
@@ -26,11 +26,10 @@
});
let firstMessage = true;
+let messageCountAfterResume = 0;
onload = () => {
- worker = new Worker('resources/worker-event-listener.js');
- setInterval(() => {
- worker.postMessage("");
- }, 1);
+ worker = new Worker('resources/worker-setInterval.js');
+ worker.postMessage("");
worker.onmessage = function(evt) {
if (firstMessage) {
@@ -40,9 +39,12 @@
return;
}
if (restoredFromPageCache) {
- restoredFromPageCache = false;
- testPassed("Received message after restoring from page cache.");
- finishJSTest();
+ messageCountAfterResume++;
+ if (messageCountAfterResume == 10) {
+ restoredFromPageCache = false;
+ testPassed("Received message after restoring from page cache.");
+ finishJSTest();
+ }
}
};
}
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index a444eae..17b598b 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,33 @@
+2019-10-21 Chris Dumez <cdumez@apple.com>
+
+ Suspend dedicated worker threads while in the back/forward cache
+ https://bugs.webkit.org/show_bug.cgi?id=203186
+ <rdar://problem/56447493>
+
+ Reviewed by Ryosuke Niwa.
+
+ When a page with a (dedicated) Worker enters the back/forward cache, we now
+ pause the worker thread, and resume it only when taking the page out of the
+ back/forward cache. This avoids having the worker use CPU while the page is
+ in the cache.
+
+ * workers/Worker.cpp:
+ (WebCore::Worker::suspend):
+ (WebCore::Worker::resume):
+ * workers/Worker.h:
+ * workers/WorkerGlobalScopeProxy.h:
+ * workers/WorkerMessagingProxy.cpp:
+ (WebCore::WorkerMessagingProxy::suspend):
+ (WebCore::WorkerMessagingProxy::resume):
+ (WebCore::WorkerMessagingProxy::workerThreadCreated):
+ * workers/WorkerMessagingProxy.h:
+ * workers/WorkerThread.cpp:
+ (WebCore::WorkerThread::WorkerThread):
+ (WebCore::WorkerThread::suspend):
+ (WebCore::WorkerThread::resume):
+ (WebCore::WorkerThread::stop):
+ * workers/WorkerThread.h:
+
2019-10-21 Tim Horton <timothy_horton@apple.com>
Fix the build
diff --git a/Source/WebCore/workers/Worker.cpp b/Source/WebCore/workers/Worker.cpp
index 0f5078e..b3aa708 100644
--- a/Source/WebCore/workers/Worker.cpp
+++ b/Source/WebCore/workers/Worker.cpp
@@ -160,6 +160,22 @@
terminate();
}
+void Worker::suspend(ReasonForSuspension reason)
+{
+ if (reason == ReasonForSuspension::BackForwardCache) {
+ m_contextProxy.suspendForBackForwardCache();
+ m_isSuspendedForBackForwardCache = true;
+ }
+}
+
+void Worker::resume()
+{
+ if (m_isSuspendedForBackForwardCache) {
+ m_contextProxy.resumeForBackForwardCache();
+ m_isSuspendedForBackForwardCache = false;
+ }
+}
+
bool Worker::hasPendingActivity() const
{
return m_contextProxy.hasPendingActivity() || ActiveDOMObject::hasPendingActivity() || m_eventQueue->hasPendingEvents();
diff --git a/Source/WebCore/workers/Worker.h b/Source/WebCore/workers/Worker.h
index 4c54139..d52a802 100644
--- a/Source/WebCore/workers/Worker.h
+++ b/Source/WebCore/workers/Worker.h
@@ -84,6 +84,8 @@
void notifyFinished() final;
void stop() final;
+ void suspend(ReasonForSuspension) final;
+ void resume() final;
const char* activeDOMObjectName() const final;
static void networkStateChanged(bool isOnLine);
@@ -95,6 +97,7 @@
Optional<ContentSecurityPolicyResponseHeaders> m_contentSecurityPolicyResponseHeaders;
MonotonicTime m_workerCreationTime;
bool m_shouldBypassMainWorldContentSecurityPolicy { false };
+ bool m_isSuspendedForBackForwardCache { false };
JSC::RuntimeFlags m_runtimeFlags;
UniqueRef<GenericEventQueue> m_eventQueue;
};
diff --git a/Source/WebCore/workers/WorkerGlobalScopeProxy.h b/Source/WebCore/workers/WorkerGlobalScopeProxy.h
index 0657d6e..74c648b 100644
--- a/Source/WebCore/workers/WorkerGlobalScopeProxy.h
+++ b/Source/WebCore/workers/WorkerGlobalScopeProxy.h
@@ -52,6 +52,9 @@
virtual void workerObjectDestroyed() = 0;
virtual void notifyNetworkStateChange(bool isOnline) = 0;
+ virtual void suspendForBackForwardCache() = 0;
+ virtual void resumeForBackForwardCache() = 0;
+
protected:
virtual ~WorkerGlobalScopeProxy() = default;
};
diff --git a/Source/WebCore/workers/WorkerMessagingProxy.cpp b/Source/WebCore/workers/WorkerMessagingProxy.cpp
index a77bb7e..6d89e83 100644
--- a/Source/WebCore/workers/WorkerMessagingProxy.cpp
+++ b/Source/WebCore/workers/WorkerMessagingProxy.cpp
@@ -128,6 +128,22 @@
m_queuedEarlyTasks.append(makeUnique<ScriptExecutionContext::Task>(WTFMove(task)));
}
+void WorkerMessagingProxy::suspendForBackForwardCache()
+{
+ if (m_workerThread)
+ m_workerThread->suspend();
+ else
+ m_askedToSuspend = true;
+}
+
+void WorkerMessagingProxy::resumeForBackForwardCache()
+{
+ if (m_workerThread)
+ m_workerThread->resume();
+ else
+ m_askedToSuspend = false;
+}
+
void WorkerMessagingProxy::postTaskToLoader(ScriptExecutionContext::Task&& task)
{
// FIXME: In case of nested workers, this should go directly to the root Document context.
@@ -190,6 +206,11 @@
// Worker.terminate() could be called from JS before the thread was created.
m_workerThread->stop(nullptr);
} else {
+ if (m_askedToSuspend) {
+ m_askedToSuspend = false;
+ m_workerThread->suspend();
+ }
+
ASSERT(!m_unconfirmedMessageCount);
m_unconfirmedMessageCount = m_queuedEarlyTasks.size();
m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
diff --git a/Source/WebCore/workers/WorkerMessagingProxy.h b/Source/WebCore/workers/WorkerMessagingProxy.h
index ed17fc3..fd554e1 100644
--- a/Source/WebCore/workers/WorkerMessagingProxy.h
+++ b/Source/WebCore/workers/WorkerMessagingProxy.h
@@ -56,6 +56,8 @@
bool hasPendingActivity() const final;
void workerObjectDestroyed() final;
void notifyNetworkStateChange(bool isOnline) final;
+ void suspendForBackForwardCache() final;
+ void resumeForBackForwardCache() final;
// Implementation of WorkerObjectProxy.
// (Only use these functions in the worker context thread.)
@@ -96,6 +98,7 @@
unsigned m_unconfirmedMessageCount { 0 }; // Unconfirmed messages from worker object to worker thread.
bool m_workerThreadHadPendingActivity { false }; // The latest confirmation from worker thread reported that it was still active.
+ bool m_askedToSuspend { false };
bool m_askedToTerminate { false };
Vector<std::unique_ptr<ScriptExecutionContext::Task>> m_queuedEarlyTasks; // Tasks are queued here until there's a thread object created.
diff --git a/Source/WebCore/workers/WorkerThread.cpp b/Source/WebCore/workers/WorkerThread.cpp
index 19d03a1..275b5ee 100644
--- a/Source/WebCore/workers/WorkerThread.cpp
+++ b/Source/WebCore/workers/WorkerThread.cpp
@@ -265,6 +265,21 @@
m_runLoop.run(m_workerGlobalScope.get());
}
+void WorkerThread::suspend()
+{
+ m_isSuspended = true;
+ runLoop().postTask([&](ScriptExecutionContext&) {
+ m_suspensionSemaphore.wait();
+ });
+}
+
+void WorkerThread::resume()
+{
+ ASSERT(m_isSuspended);
+ m_isSuspended = false;
+ m_suspensionSemaphore.signal();
+}
+
void WorkerThread::stop(WTF::Function<void()>&& stoppedCallback)
{
// Mutex protection is necessary to ensure that m_workerGlobalScope isn't changed by
@@ -280,6 +295,10 @@
return;
}
+ // If the thread is suspended, resume it now so that we can dispatch the cleanup tasks below.
+ if (m_isSuspended)
+ resume();
+
ASSERT(!m_stoppedCallback);
m_stoppedCallback = WTFMove(stoppedCallback);
diff --git a/Source/WebCore/workers/WorkerThread.h b/Source/WebCore/workers/WorkerThread.h
index cd6c78d..14eebf1 100644
--- a/Source/WebCore/workers/WorkerThread.h
+++ b/Source/WebCore/workers/WorkerThread.h
@@ -31,6 +31,7 @@
#include <wtf/Forward.h>
#include <wtf/Function.h>
#include <wtf/RefCounted.h>
+#include <wtf/threads/BinarySemaphore.h>
namespace WebCore {
@@ -63,6 +64,9 @@
void stop(WTF::Function<void()>&& terminatedCallback);
+ void suspend();
+ void resume();
+
Thread* thread() const { return m_thread.get(); }
WorkerRunLoop& runLoop() { return m_runLoop; }
WorkerLoaderProxy& workerLoaderProxy() const { return m_workerLoaderProxy; }
@@ -133,6 +137,8 @@
RefPtr<SocketProvider> m_socketProvider;
WTF::Function<void()> m_stoppedCallback;
+ BinarySemaphore m_suspensionSemaphore;
+ bool m_isSuspended { false };
};
} // namespace WebCore