Using version 1 CFRunloopSource for faster task dispatch
https://bugs.webkit.org/show_bug.cgi?id=202874
Reviewed by Geoffrey Garen.
Source/WTF:
We used CFRunLoopWakeUp to wake up runloop to process source, which seems to be slow according to profiling. To
avoid calling CFRunLoopWakeUp, we should use version 1 CFRunloopSource instead of version 0. This patch brings
about 15% speedup for test PerformanceTests/IndexedDB/basic/objectstore-get.html.
* wtf/RunLoop.cpp:
(WTF::RunLoop::initializeWebRunLoop):
(WTF::RunLoop::web):
* wtf/RunLoop.h:
* wtf/cf/RunLoopCF.cpp:
(WTF::RunLoop::performWork):
(WTF::RunLoop::RunLoop):
(WTF::RunLoop::~RunLoop):
(WTF::RunLoop::wakeUp):
* wtf/cocoa/MainThreadCocoa.mm:
(WTF::initializeMainThreadPlatform):
(WTF::scheduleDispatchFunctionsOnMainThread):
(WTF::initializeWebThread):
(-[JSWTFMainThreadCaller call]): Deleted.
Tools:
Fix a flaky test.
* TestWebKitAPI/Tests/WebKit/getUserMedia.html:
LayoutTests:
Fix a flaky test.
* inspector/css/pseudo-creation-expected.txt:
* inspector/css/pseudo-creation.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251261 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index fd3edf1..911a3a8 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,15 @@
+2019-10-17 Sihui Liu <sihui_liu@apple.com>
+
+ Using version 1 CFRunloopSource for faster task dispatch
+ https://bugs.webkit.org/show_bug.cgi?id=202874
+
+ Reviewed by Geoffrey Garen.
+
+ Fix a flaky test.
+
+ * inspector/css/pseudo-creation-expected.txt:
+ * inspector/css/pseudo-creation.html:
+
2019-10-17 Ryosuke Niwa <rniwa@webkit.org>
Make requestIdleCallback suspendable
diff --git a/LayoutTests/inspector/css/pseudo-creation-expected.txt b/LayoutTests/inspector/css/pseudo-creation-expected.txt
index 5b83233..49b7ffa 100644
--- a/LayoutTests/inspector/css/pseudo-creation-expected.txt
+++ b/LayoutTests/inspector/css/pseudo-creation-expected.txt
@@ -12,11 +12,11 @@
Calling "createElementWithClass("test-pseudo-with-content")"...
Checking for nodes with class ".test-pseudo-with-content"...
-PASS: Created ::before pseudo element
PASS: There should be 1 node with the class ".test-pseudo-with-content".
+PASS: Created ::before pseudo element
Calling "removeElementWithClass("test-pseudo-with-content")"...
Checking for nodes with class ".test-pseudo-with-content"...
-PASS: Removed ::before pseudo element
PASS: There should be 0 node with the class ".test-pseudo-with-content".
+PASS: Removed ::before pseudo element
diff --git a/LayoutTests/inspector/css/pseudo-creation.html b/LayoutTests/inspector/css/pseudo-creation.html
index a01ce0d..84f5050 100644
--- a/LayoutTests/inspector/css/pseudo-creation.html
+++ b/LayoutTests/inspector/css/pseudo-creation.html
@@ -26,7 +26,8 @@
function test() {
let documentNode = null;
- let pseudoElement = null;
+ let pseudoElementAdded = null;
+ let pseudoElementRemoved = null;
function handlePromiseReject(error) {
console.log(error);
@@ -56,27 +57,37 @@
.catch(handlePromiseReject);
}
- function createElementWithClass(className) {
+ function createElementWithClass(className, shouldCheckElement) {
return evaluateWithLog(`createElementWithClass("${className}")`)
.then(() => checkElementsWithClass(className, 1))
+ .then(() => {
+ if (shouldCheckElement) {
+ if (pseudoElementAdded)
+ ProtocolTest.pass(`Created ::${pseudoElementAdded.pseudoType} pseudo element`);
+ else
+ return pseudoElementAddedPromise.then(() => { ProtocolTest.pass(`Created ::${pseudoElementAdded.pseudoType} pseudo element`); });
+ }
+ })
.catch(handlePromiseReject);
}
- function removeElementWithClass(className) {
+ function removeElementWithClass(className, shouldCheckElement) {
return evaluateWithLog(`removeElementWithClass("${className}")`)
.then(() => checkElementsWithClass(className, 0))
+ .then(() => {
+ if (shouldCheckElement) {
+ if (pseudoElementRemoved)
+ ProtocolTest.expectEqual(pseudoElementRemoved.pseudoElementId, pseudoElementAdded.nodeId, `Removed ::${pseudoElementAdded.pseudoType} pseudo element`);
+ else
+ return pseudoElementRemovedPromise.then(() => { ProtocolTest.expectEqual(pseudoElementRemoved.pseudoElementId, pseudoElementAdded.nodeId, `Removed ::${pseudoElementAdded.pseudoType} pseudo element`); });
+ }
+ })
.catch(handlePromiseReject);
}
- InspectorProtocol.eventHandler["DOM.pseudoElementAdded"] = (response) => {
- pseudoElement = response.params.pseudoElement;
+ let pseudoElementAddedPromise = InspectorProtocol.awaitEvent({event: "DOM.pseudoElementAdded"}).then((event) => { pseudoElementAdded = event.params.pseudoElement});
+ let pseudoElementRemovedPromise = InspectorProtocol.awaitEvent({event: "DOM.pseudoElementRemoved"}).then((event) => { pseudoElementRemoved = event.params});
- ProtocolTest.pass(`Created ::${pseudoElement.pseudoType} pseudo element`);
- };
-
- InspectorProtocol.eventHandler["DOM.pseudoElementRemoved"] = (response) => {
- ProtocolTest.expectEqual(response.params.pseudoElementId, pseudoElement.nodeId, `Removed ::${pseudoElement.pseudoType} pseudo element`);
- };
ProtocolTest.log("Requesting document...");
InspectorProtocol.sendCommand("DOM.getDocument", {}, (response) => {
@@ -85,10 +96,10 @@
documentNode = response.result.root;
Promise.resolve()
- .then(() => createElementWithClass("test-pseudo-without-content"))
- .then(() => removeElementWithClass("test-pseudo-without-content"))
- .then(() => createElementWithClass("test-pseudo-with-content"))
- .then(() => removeElementWithClass("test-pseudo-with-content"))
+ .then(() => createElementWithClass("test-pseudo-without-content"), false)
+ .then(() => removeElementWithClass("test-pseudo-without-content"), false)
+ .then(() => createElementWithClass("test-pseudo-with-content", true))
+ .then(() => removeElementWithClass("test-pseudo-with-content", true))
.then(() => ProtocolTest.completeTest())
.catch(handlePromiseReject);
});
diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog
index 7555d4e..0e44522 100644
--- a/Source/WTF/ChangeLog
+++ b/Source/WTF/ChangeLog
@@ -1,3 +1,29 @@
+2019-10-17 Sihui Liu <sihui_liu@apple.com>
+
+ Using version 1 CFRunloopSource for faster task dispatch
+ https://bugs.webkit.org/show_bug.cgi?id=202874
+
+ Reviewed by Geoffrey Garen.
+
+ We used CFRunLoopWakeUp to wake up runloop to process source, which seems to be slow according to profiling. To
+ avoid calling CFRunLoopWakeUp, we should use version 1 CFRunloopSource instead of version 0. This patch brings
+ about 15% speedup for test PerformanceTests/IndexedDB/basic/objectstore-get.html.
+
+ * wtf/RunLoop.cpp:
+ (WTF::RunLoop::initializeWebRunLoop):
+ (WTF::RunLoop::web):
+ * wtf/RunLoop.h:
+ * wtf/cf/RunLoopCF.cpp:
+ (WTF::RunLoop::performWork):
+ (WTF::RunLoop::RunLoop):
+ (WTF::RunLoop::~RunLoop):
+ (WTF::RunLoop::wakeUp):
+ * wtf/cocoa/MainThreadCocoa.mm:
+ (WTF::initializeMainThreadPlatform):
+ (WTF::scheduleDispatchFunctionsOnMainThread):
+ (WTF::initializeWebThread):
+ (-[JSWTFMainThreadCaller call]): Deleted.
+
2019-10-16 Wenson Hsieh <wenson_hsieh@apple.com>
Unreviewed, fix the internal macOS 10.13 and 10.14 builds after r251171
diff --git a/Source/WTF/wtf/RunLoop.cpp b/Source/WTF/wtf/RunLoop.cpp
index 1be22d8..938b9e9 100644
--- a/Source/WTF/wtf/RunLoop.cpp
+++ b/Source/WTF/wtf/RunLoop.cpp
@@ -33,6 +33,9 @@
namespace WTF {
static RunLoop* s_mainRunLoop;
+#if USE(WEB_THREAD)
+static RunLoop* s_webRunLoop;
+#endif
// Helper class for ThreadSpecificData.
class RunLoop::Holder {
@@ -69,6 +72,19 @@
return *s_mainRunLoop;
}
+#if USE(WEB_THREAD)
+void RunLoop::initializeWebRunLoop()
+{
+ s_webRunLoop = &RunLoop::current();
+}
+
+RunLoop& RunLoop::web()
+{
+ ASSERT(s_webRunLoop);
+ return *s_webRunLoop;
+}
+#endif
+
bool RunLoop::isMain()
{
ASSERT(s_mainRunLoop);
diff --git a/Source/WTF/wtf/RunLoop.h b/Source/WTF/wtf/RunLoop.h
index 9de8859..a85118d 100644
--- a/Source/WTF/wtf/RunLoop.h
+++ b/Source/WTF/wtf/RunLoop.h
@@ -49,9 +49,15 @@
// Must be called from the main thread (except for the Mac platform, where it
// can be called from any thread).
WTF_EXPORT_PRIVATE static void initializeMainRunLoop();
+#if USE(WEB_THREAD)
+ WTF_EXPORT_PRIVATE static void initializeWebRunLoop();
+#endif
WTF_EXPORT_PRIVATE static RunLoop& current();
WTF_EXPORT_PRIVATE static RunLoop& main();
+#if USE(WEB_THREAD)
+ WTF_EXPORT_PRIVATE static RunLoop& web();
+#endif
WTF_EXPORT_PRIVATE static bool isMain();
~RunLoop();
@@ -178,9 +184,10 @@
Lock m_loopLock;
#elif USE(COCOA_EVENT_LOOP)
- static void performWork(void*);
+ static void performWork(CFMachPortRef, void* msg, CFIndex size, void* info);
RetainPtr<CFRunLoopRef> m_runLoop;
RetainPtr<CFRunLoopSourceRef> m_runLoopSource;
+ RetainPtr<CFMachPortRef> m_port;
#elif USE(GLIB_EVENT_LOOP)
GRefPtr<GMainContext> m_mainContext;
Vector<GRefPtr<GMainLoop>> m_mainLoops;
diff --git a/Source/WTF/wtf/cf/RunLoopCF.cpp b/Source/WTF/wtf/cf/RunLoopCF.cpp
index 27d3fe2..b2e2e5b 100644
--- a/Source/WTF/wtf/cf/RunLoopCF.cpp
+++ b/Source/WTF/wtf/cf/RunLoopCF.cpp
@@ -28,26 +28,29 @@
#include <CoreFoundation/CoreFoundation.h>
#include <dispatch/dispatch.h>
+#include <mach/mach.h>
#include <wtf/AutodrainedPool.h>
namespace WTF {
-void RunLoop::performWork(void* context)
+void RunLoop::performWork(CFMachPortRef, void*, CFIndex, void* info)
{
AutodrainedPool pool;
- static_cast<RunLoop*>(context)->performWork();
+ static_cast<RunLoop*>(info)->performWork();
}
RunLoop::RunLoop()
: m_runLoop(CFRunLoopGetCurrent())
{
- CFRunLoopSourceContext context = { 0, this, 0, 0, 0, 0, 0, 0, 0, performWork };
- m_runLoopSource = adoptCF(CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context));
+ CFMachPortContext context = { 0, this, nullptr, nullptr, nullptr };
+ m_port = adoptCF(CFMachPortCreate(kCFAllocatorDefault, performWork, &context, nullptr));
+ m_runLoopSource = adoptCF(CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_port.get(), 0));
CFRunLoopAddSource(m_runLoop.get(), m_runLoopSource.get(), kCFRunLoopCommonModes);
}
RunLoop::~RunLoop()
{
+ CFMachPortInvalidate(m_port.get());
CFRunLoopSourceInvalidate(m_runLoopSource.get());
}
@@ -58,8 +61,16 @@
void RunLoop::wakeUp()
{
- CFRunLoopSourceSignal(m_runLoopSource.get());
- CFRunLoopWakeUp(m_runLoop.get());
+ mach_msg_header_t header;
+ header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ header.msgh_size = sizeof(mach_msg_header_t);
+ header.msgh_remote_port = CFMachPortGetPort(m_port.get());
+ header.msgh_local_port = MACH_PORT_NULL;
+ header.msgh_id = 0;
+ mach_msg_return_t result = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+ RELEASE_ASSERT(result == MACH_MSG_SUCCESS || result == MACH_SEND_TIMED_OUT);
+ if (result == MACH_SEND_TIMED_OUT)
+ mach_msg_destroy(&header);
}
RunLoop::CycleResult RunLoop::cycle(const String& mode)
diff --git a/Source/WTF/wtf/cocoa/MainThreadCocoa.mm b/Source/WTF/wtf/cocoa/MainThreadCocoa.mm
index 27eb00d..270b7b0 100644
--- a/Source/WTF/wtf/cocoa/MainThreadCocoa.mm
+++ b/Source/WTF/wtf/cocoa/MainThreadCocoa.mm
@@ -36,6 +36,7 @@
#import <wtf/Assertions.h>
#import <wtf/HashSet.h>
#import <wtf/RetainPtr.h>
+#import <wtf/RunLoop.h>
#import <wtf/SchedulePair.h>
#import <wtf/Threading.h>
@@ -43,19 +44,6 @@
#include <wtf/ios/WebCoreThread.h>
#endif
-@interface JSWTFMainThreadCaller : NSObject
-- (void)call;
-@end
-
-@implementation JSWTFMainThreadCaller
-
-- (void)call
-{
- WTF::dispatchFunctionsFromMainThread();
-}
-
-@end
-
#define LOG_CHANNEL_PREFIX Log
namespace WTF {
@@ -66,8 +54,6 @@
WTFLogChannel LogThreading = { WTFLogChannelState::On, "Threading", WTFLogLevel::Error, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
#endif
-
-static JSWTFMainThreadCaller* staticMainThreadCaller;
static bool isTimerPosted; // This is only accessed on the main thread.
#if USE(WEB_THREAD)
@@ -84,9 +70,6 @@
if (!pthread_main_np())
RELEASE_LOG_FAULT(Threading, "WebKit Threading Violation - initial use of WebKit from a secondary thread.");
ASSERT(pthread_main_np());
-
- ASSERT(!staticMainThreadCaller);
- staticMainThreadCaller = [[JSWTFMainThreadCaller alloc] init];
}
static void timerFired(CFRunLoopTimerRef timer, void*)
@@ -112,8 +95,6 @@
void scheduleDispatchFunctionsOnMainThread()
{
- ASSERT(staticMainThreadCaller);
-
#if USE(WEB_THREAD)
if (isWebThread()) {
postTimer();
@@ -121,7 +102,9 @@
}
if (mainThreadPthread) {
- [staticMainThreadCaller performSelector:@selector(call) onThread:mainThreadNSThread withObject:nil waitUntilDone:NO];
+ RunLoop::web().dispatch([] {
+ WTF::dispatchFunctionsFromMainThread();
+ });
return;
}
#else
@@ -131,7 +114,9 @@
}
#endif
- [staticMainThreadCaller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:NO];
+ RunLoop::main().dispatch([] {
+ WTF::dispatchFunctionsFromMainThread();
+ });
}
void dispatchAsyncOnMainThreadWithWebThreadLockIfNeeded(void (^block)())
@@ -196,6 +181,7 @@
mainThreadPthread = pthread_self();
mainThreadNSThread = [NSThread currentThread];
sWebThread = &Thread::current();
+ RunLoop::initializeWebRunLoop();
});
}
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 64aaa6d..49e847e 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,14 @@
+2019-10-17 Sihui Liu <sihui_liu@apple.com>
+
+ Using version 1 CFRunloopSource for faster task dispatch
+ https://bugs.webkit.org/show_bug.cgi?id=202874
+
+ Reviewed by Geoffrey Garen.
+
+ Fix a flaky test.
+
+ * TestWebKitAPI/Tests/WebKit/getUserMedia.html:
+
2019-10-17 Jonathan Bedard <jbedard@apple.com>
Python 3: Add support in webkitpy.common.net.buildbot
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html b/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html
index cb32d80..7e4617b 100644
--- a/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html
+++ b/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html
@@ -2,14 +2,15 @@
<html>
<head>
<script>
-
- let stream = null;
+ let streamPromise = Promise.resolve();
function promptForCapture()
{
- navigator.mediaDevices.enumerateDevices().then(() => {
+ streamPromise = navigator.mediaDevices.enumerateDevices().then(() => {
return navigator.mediaDevices.getUserMedia({ audio: false, video: true })
- }).then((s) => {
+ });
+
+ streamPromise.then((stream) => {
stream = s;
video.srcObject = stream;
console.log("Got user media");
@@ -18,23 +19,29 @@
function stop(kind)
{
- let activeTracks = [];
- stream.getTracks().forEach(track => {
- if (!kind || track.kind == kind)
- track.stop();
- else
- activeTracks.push(track);
+ streamPromise.then((stream) => {
+ let activeTracks = [];
+ stream.getTracks().forEach(track => {
+ if (!kind || track.kind == kind)
+ track.stop();
+ else
+ activeTracks.push(track);
+ });
+
+ if (!activeTracks.length) {
+ streamPromiseDidResolve = false;
+ video.srcObject = null;
+ }
});
-
- if (!activeTracks.length) {
- stream = null;
- video.srcObject = null;
- }
}
+ let streamPromiseDidResolve = false;
+
function haveStream()
{
- return stream !== null;
+ // Our caller polls repeatedly until our promise resolves.
+ streamPromise.then((stream) => streamPromiseDidResolve = !!stream);
+ return streamPromiseDidResolve;
}
function doMultipleGetUserMediaSynchronously()
@@ -53,12 +60,12 @@
function captureAudio()
{
- navigator.mediaDevices.getUserMedia({audio: true}).then(s => stream = s);
+ streamPromise = navigator.mediaDevices.getUserMedia({audio: true});
}
function captureAudioAndVideo()
{
- navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(s => stream = s);
+ streamPromise = navigator.mediaDevices.getUserMedia({audio: true, video: true});
}
</script>
<head>