Title: [284223] trunk
Revision
284223
Author
[email protected]
Date
2021-10-14 18:44:56 -0700 (Thu, 14 Oct 2021)

Log Message

[JS IPC] Implement a way to synchronously wait for an incoming IPC message
https://bugs.webkit.org/show_bug.cgi?id=231745

Reviewed by Tim Horton.

Source/WebKit:

Add support for `IPC.waitForMessage()`, which blocks until we receive an incoming IPC message that matches the
given message name and destination ID. We use this new method to fix the failing API test
IPCTestingAPI.CanReceiveIPCSemaphore, which verifies that we're able to receive, decode, and call methods on
an IPC semaphore sent from the GPU process to the web process. In this case, we wait for the incoming
"RemoteRenderingBackendProxy::DidCreateWakeUpSemaphoreForDisplayListStream" message after creating the remote
rendering backend in the GPU process.

* Platform/IPC/Connection.h:
* WebProcess/WebPage/IPCTestingAPI.cpp:
(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::setWakeUpSemaphore):

Also add a method on JSIPCStreamClientConnection to install a given IPC semaphore as the wakeup semaphore for
the IPC stream. We'll use this in the next patch when implementing the ability to send messages through the IPC
stream.

(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::staticFunctions):
(WebKit::IPCTestingAPI::JSIPCStreamClientConnection::streamBuffer):

Drive-by fix: make sure we grab the VM lock before trying to unwrap JS objects.

(WebKit::IPCTestingAPI::JSIPC::staticFunctions):
(WebKit::IPCTestingAPI::JSIPC::sendMessage):

Drive-by fix: correct a minor typo in the exception message.

(WebKit::IPCTestingAPI::extractSyncIPCMessageInfo):

Pull logic for extracting an IPC::Connection, destination ID, message name, and IPC timeout into a separate
helper function, and use it in `waitForMessage` as well as `sendSyncMessage`.

(WebKit::IPCTestingAPI::JSIPC::waitForMessage):
(WebKit::IPCTestingAPI::JSIPC::sendSyncMessage):

Tools:

Adjust an existing JS IPC test, IPCTestingAPI.CanReceiveIPCSemaphore, so that it uses the new JSIPC method to
wait for RemoteRenderingBackend's IPC stream wakeup semaphore to arrive. This fixes
IPCTestingAPI.CanReceiveIPCSemaphore (which is one of the two IPC tests that time out after r284079).

We also adjust IPCTestingAPI.CanReceiveSharedMemory to wait for and install the wakeup semaphore into the IPC
stream connection; however, this alone isn't sufficient to fix the test, since the test also requires the
ability to send synchronous messages through the IPC stream.

See WebKit ChangeLog for more details.

* TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:
(TEST):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (284222 => 284223)


--- trunk/Source/WebKit/ChangeLog	2021-10-15 01:24:15 UTC (rev 284222)
+++ trunk/Source/WebKit/ChangeLog	2021-10-15 01:44:56 UTC (rev 284223)
@@ -1,3 +1,43 @@
+2021-10-14  Wenson Hsieh  <[email protected]>
+
+        [JS IPC] Implement a way to synchronously wait for an incoming IPC message
+        https://bugs.webkit.org/show_bug.cgi?id=231745
+
+        Reviewed by Tim Horton.
+
+        Add support for `IPC.waitForMessage()`, which blocks until we receive an incoming IPC message that matches the
+        given message name and destination ID. We use this new method to fix the failing API test
+        IPCTestingAPI.CanReceiveIPCSemaphore, which verifies that we're able to receive, decode, and call methods on
+        an IPC semaphore sent from the GPU process to the web process. In this case, we wait for the incoming
+        "RemoteRenderingBackendProxy::DidCreateWakeUpSemaphoreForDisplayListStream" message after creating the remote
+        rendering backend in the GPU process.
+
+        * Platform/IPC/Connection.h:
+        * WebProcess/WebPage/IPCTestingAPI.cpp:
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::setWakeUpSemaphore):
+
+        Also add a method on JSIPCStreamClientConnection to install a given IPC semaphore as the wakeup semaphore for
+        the IPC stream. We'll use this in the next patch when implementing the ability to send messages through the IPC
+        stream.
+
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::staticFunctions):
+        (WebKit::IPCTestingAPI::JSIPCStreamClientConnection::streamBuffer):
+
+        Drive-by fix: make sure we grab the VM lock before trying to unwrap JS objects.
+
+        (WebKit::IPCTestingAPI::JSIPC::staticFunctions):
+        (WebKit::IPCTestingAPI::JSIPC::sendMessage):
+
+        Drive-by fix: correct a minor typo in the exception message.
+
+        (WebKit::IPCTestingAPI::extractSyncIPCMessageInfo):
+
+        Pull logic for extracting an IPC::Connection, destination ID, message name, and IPC timeout into a separate
+        helper function, and use it in `waitForMessage` as well as `sendSyncMessage`.
+
+        (WebKit::IPCTestingAPI::JSIPC::waitForMessage):
+        (WebKit::IPCTestingAPI::JSIPC::sendSyncMessage):
+
 2021-10-14  Alex Christensen  <[email protected]>
 
         Fix build after r284216

Modified: trunk/Source/WebKit/Platform/IPC/Connection.h (284222 => 284223)


--- trunk/Source/WebKit/Platform/IPC/Connection.h	2021-10-15 01:24:15 UTC (rev 284222)
+++ trunk/Source/WebKit/Platform/IPC/Connection.h	2021-10-15 01:44:56 UTC (rev 284223)
@@ -56,6 +56,12 @@
 #include <wtf/glib/GSocketMonitor.h>
 #endif
 
+namespace WebKit {
+namespace IPCTestingAPI {
+class JSIPC;
+}
+}
+
 namespace IPC {
 
 enum class SendOption {
@@ -507,6 +513,7 @@
     HANDLE m_connectionPipe { INVALID_HANDLE_VALUE };
 #endif
     friend class StreamClientConnection;
+    friend class WebKit::IPCTestingAPI::JSIPC;
 };
 
 template<typename T>

Modified: trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp (284222 => 284223)


--- trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp	2021-10-15 01:24:15 UTC (rev 284222)
+++ trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp	2021-10-15 01:44:56 UTC (rev 284223)
@@ -112,6 +112,8 @@
         : m_streamConnection { connection, bufferSize }
     { }
 
+    void setWakeUpSemaphore(JSIPCSemaphore& jsSemaphore) { m_streamConnection.setWakeUpSemaphore(jsSemaphore.exchange()); }
+
     static JSClassRef wrapperClass();
     static JSIPCStreamClientConnection* unwrap(JSObjectRef);
     static void initialize(JSContextRef, JSObjectRef);
@@ -119,6 +121,7 @@
 
     static const JSStaticFunction* staticFunctions();
     static JSValueRef streamBuffer(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef setWakeUpSemaphore(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
 
     IPC::StreamClientConnection m_streamConnection;
 };
@@ -241,6 +244,7 @@
 
     static JSValueRef sendMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     static JSValueRef sendSyncMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef waitForMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
 
     static JSValueRef createStreamClientConnection(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
     static JSValueRef createSemaphore(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
@@ -392,6 +396,7 @@
 {
     static const JSStaticFunction functions[] = {
         { "streamBuffer", streamBuffer, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
+        { "setWakeUpSemaphore", setWakeUpSemaphore, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { 0, 0, 0 }
     };
     return functions;
@@ -399,6 +404,8 @@
 
 JSValueRef JSIPCStreamClientConnection::streamBuffer(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
     RefPtr jsStreamConnection = toWrapped(context, thisObject);
     if (!jsStreamConnection) {
         *exception = createTypeError(context, "Wrong type"_s);
@@ -408,6 +415,31 @@
     return JSIPCStreamConnectionBuffer::create(*jsStreamConnection)->createJSWrapper(context);
 }
 
+JSValueRef JSIPCStreamClientConnection::setWakeUpSemaphore(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
+    RefPtr jsStreamConnection = toWrapped(context, thisObject);
+    if (!jsStreamConnection) {
+        *exception = createTypeError(context, "Wrong type"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    if (argumentCount < 1) {
+        *exception = createTypeError(context, "Must specify an IPC semaphore as the first argument"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    RefPtr jsSemaphore = JSIPCSemaphore::toWrapped(context, arguments[0]);
+    if (!jsSemaphore) {
+        *exception = createTypeError(context, "Wrong type (expected Semaphore)"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    jsStreamConnection->setWakeUpSemaphore(*jsSemaphore);
+    return JSValueMakeUndefined(context);
+}
+
 JSObjectRef JSIPCStreamConnectionBuffer::createJSWrapper(JSContextRef context)
 {
     auto* globalObject = toJS(context);
@@ -760,6 +792,7 @@
         { "addOutgoingMessageListener", addOutgoingMessageListener, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "sendMessage", sendMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "sendSyncMessage", sendSyncMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
+        { "waitForMessage", waitForMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "createStreamClientConnection", createStreamClientConnection, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "createSemaphore", createSemaphore, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
         { "createSharedMemory", createSharedMemory, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
@@ -1315,7 +1348,7 @@
     }
 
     if (argumentCount < 3) {
-        *exception = createTypeError(context, "Must specify the target process, desination ID, and message ID as the first three arguments"_s);
+        *exception = createTypeError(context, "Must specify the target process, destination ID, and message ID as the first three arguments"_s);
         return JSValueMakeUndefined(context);
     }
 
@@ -1386,48 +1419,94 @@
     return returnValue;
 }
 
-JSValueRef JSIPC::sendSyncMessage(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+struct SyncIPCMessageInfo {
+    Ref<IPC::Connection> connection;
+    uint64_t destinationID;
+    IPC::MessageName messageName;
+    IPC::Timeout timeout;
+};
+
+static std::optional<SyncIPCMessageInfo> extractSyncIPCMessageInfo(JSContextRef context, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
-    auto* globalObject = toJS(context);
-    JSC::JSLockHolder lock(globalObject->vm());
-    RefPtr jsIPC = toWrapped(context, thisObject);
-    if (!jsIPC) {
-        *exception = createTypeError(context, "Wrong type"_s);
-        return JSValueMakeUndefined(context);
-    }
-
     if (argumentCount < 4) {
-        *exception = createTypeError(context, "Must specify the target process, desination ID, and message ID as the first three arguments"_s);
-        return JSValueMakeUndefined(context);
+        *exception = createTypeError(context, "Must specify the target process, destination ID, and message ID as the first three arguments"_s);
+        return std::nullopt;
     }
 
+    auto* globalObject = toJS(context);
     auto connection = processTargetFromArgument(globalObject, arguments[0], exception);
     if (!connection)
-        return JSValueMakeUndefined(context);
+        return std::nullopt;
 
     auto destinationID = destinationIDFromArgument(globalObject, arguments[1], exception);
     if (!destinationID)
-        return JSValueMakeUndefined(context);
+        return std::nullopt;
 
     auto messageID = messageIDFromArgument(globalObject, arguments[2], exception);
     if (!messageID)
-        return JSValueMakeUndefined(context);
+        return std::nullopt;
 
-    Seconds timeout;
+    Seconds timeoutDuration;
     {
         auto jsValue = toJS(globalObject, arguments[3]);
         if (!jsValue.isNumber()) {
             *exception = createTypeError(context, "Timeout must be a number"_s);
+            return std::nullopt;
+        }
+        timeoutDuration = Seconds { jsValue.asNumber() };
+    }
+
+    return { { connection.releaseNonNull(), *destinationID, static_cast<IPC::MessageName>(*messageID), { timeoutDuration } } };
+}
+
+JSValueRef JSIPC::waitForMessage(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
+    RefPtr jsIPC = toWrapped(context, thisObject);
+    if (!jsIPC) {
+        *exception = createTypeError(context, "Wrong type"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    auto info = extractSyncIPCMessageInfo(context, argumentCount, arguments, exception);
+    if (!info)
+        return JSValueMakeUndefined(context);
+
+    auto [connection, destinationID, messageName, timeout] = *info;
+    if (auto decoder = connection->waitForMessage(messageName, destinationID, timeout, { })) {
+        auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
+        auto jsResult = jsValueForArguments(globalObject, messageName, *decoder);
+        if (scope.exception()) {
+            *exception = toRef(globalObject, scope.exception());
+            scope.clearException();
             return JSValueMakeUndefined(context);
         }
-        timeout = Seconds { jsValue.asNumber() };
+        return jsResult ? toRef(globalObject, *jsResult) : JSValueMakeUndefined(context);
     }
+    return JSValueMakeUndefined(context);
+}
 
+JSValueRef JSIPC::sendSyncMessage(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    auto* globalObject = toJS(context);
+    JSC::JSLockHolder lock(globalObject->vm());
+    RefPtr jsIPC = toWrapped(context, thisObject);
+    if (!jsIPC) {
+        *exception = createTypeError(context, "Wrong type"_s);
+        return JSValueMakeUndefined(context);
+    }
+
+    auto info = extractSyncIPCMessageInfo(context, argumentCount, arguments, exception);
+    if (!info)
+        return JSValueMakeUndefined(context);
+
+    auto [connection, destinationID, messageName, timeout] = *info;
+
     // FIXME: Support the options.
 
     IPC::Connection::SyncRequestID syncRequestID;
-    auto messageName = static_cast<IPC::MessageName>(*messageID);
-    auto encoder = connection->createSyncMessageEncoder(messageName, *destinationID, syncRequestID);
+    auto encoder = connection->createSyncMessageEncoder(messageName, destinationID, syncRequestID);
 
     if (argumentCount > 4) {
         if (!encodeArgument(encoder.get(), *jsIPC, context, arguments[4], exception))

Modified: trunk/Tools/ChangeLog (284222 => 284223)


--- trunk/Tools/ChangeLog	2021-10-15 01:24:15 UTC (rev 284222)
+++ trunk/Tools/ChangeLog	2021-10-15 01:44:56 UTC (rev 284223)
@@ -1,3 +1,23 @@
+2021-10-14  Wenson Hsieh  <[email protected]>
+
+        [JS IPC] Implement a way to synchronously wait for an incoming IPC message
+        https://bugs.webkit.org/show_bug.cgi?id=231745
+
+        Reviewed by Tim Horton.
+
+        Adjust an existing JS IPC test, IPCTestingAPI.CanReceiveIPCSemaphore, so that it uses the new JSIPC method to
+        wait for RemoteRenderingBackend's IPC stream wakeup semaphore to arrive. This fixes
+        IPCTestingAPI.CanReceiveIPCSemaphore (which is one of the two IPC tests that time out after r284079).
+
+        We also adjust IPCTestingAPI.CanReceiveSharedMemory to wait for and install the wakeup semaphore into the IPC
+        stream connection; however, this alone isn't sufficient to fix the test, since the test also requires the
+        ability to send synchronous messages through the IPC stream.
+
+        See WebKit ChangeLog for more details.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm:
+        (TEST):
+
 2021-10-14  Alex Christensen  <[email protected]>
 
         Fix some iOS builds after r284203

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm (284222 => 284223)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm	2021-10-15 01:24:15 UTC (rev 284222)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm	2021-10-15 01:44:56 UTC (rev 284223)
@@ -299,9 +299,8 @@
         "    { type: 'RemoteRenderingBackendCreationParameters', 'identifier': 123, 'pageProxyID': IPC.webPageProxyID, 'pageID': IPC.pageID },"
         "    { type: 'StreamConnectionBuffer', value: streamConnection.streamBuffer() },"
         "]);"
-        "const result = IPC.sendSyncMessage('GPU', 123, IPC.messages.RemoteRenderingBackend_SemaphoreForGetPixelBuffer.name, 100, []);"
-        "semaphore.signal();"
-        "alert(result.arguments.length + ':' + result.arguments[0].type + ':' + result.arguments[0].value.waitFor(100));"
+        "const arguments = IPC.waitForMessage('GPU', 123, IPC.messages.RemoteRenderingBackendProxy_DidCreateWakeUpSemaphoreForDisplayListStream.name, 100);"
+        "alert(arguments.length + ':' + arguments[0].type + ':' + arguments[0].value.waitFor(100));"
         "</script>";
     [webView synchronouslyLoadHTMLString:html];
     TestWebKitAPI::Util::run(&done);
@@ -324,6 +323,8 @@
         "    { type: 'RemoteRenderingBackendCreationParameters', 'identifier': 123, 'pageProxyID': IPC.webPageProxyID, 'pageID': IPC.pageID },"
         "    { type: 'StreamConnectionBuffer', value: streamConnection.streamBuffer() },"
         "]);"
+        "const arguments = IPC.waitForMessage('GPU', 123, IPC.messages.RemoteRenderingBackendProxy_DidCreateWakeUpSemaphoreForDisplayListStream.name, 100);"
+        "streamConnection.setWakeUpSemaphore(arguments[0].value);"
         "const result = IPC.sendSyncMessage('GPU', 123, IPC.messages.RemoteRenderingBackend_UpdateSharedMemoryForGetPixelBuffer.name, 100, [{type: 'uint32_t', value: 8}]);"
         "alert(result.arguments.length);"
         "</script>";
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to