← All reports

Swift IPC handlers return replies and may be async

Component: WebKit IPC | 9fdd260

Source/WebKit/Scripts/webkit/messages.py

+ result.append(' MainActor.assumeIsolated {\n')
+ result.append(' guard let target else {\n')
+ result.append(' return\n')
+ result.append(' }\n')
+ result.append(' Task.immediateOnMainActor {\n')
+ result.append(' await %s(\n' % invocation_class)
...
+ assignment = 'let reply = ' if message.reply_parameters else ''
+ call = ['%stry await mayThrowInvalidMessage(' % assignment]
...
+ result.append('%s completionHandler.pointee(%s)\n' % (base, swift_reply_completion_argument(message, 'reply')))

Configurations/CommonBase.xcconfig

-WK_SWIFT_MEMORY_SAFETY_ERROR_FLAGS = -Werror StrictMemorySafety -Werror ForeignReferenceType;
+WK_SWIFT_MEMORY_SAFETY_ERROR_FLAGS = -Werror StrictMemorySafety -Werror ForeignReferenceType -Werror RegionIsolation;

WebKit은 .messages.in 파일을 입력으로 받아 messages.py로 IPC receiver glue 코드를 생성합니다. Swift receiver의 경우에는 C++에서 호출 가능한 wrapper가 인자를 decode하고, handler를 호출한 뒤 reply를 전송합니다. 다만 @MainActor 멤버는 생성되는 C++ header로 export되지 않습니다. 그래서 wrapper는 nonisolated 상태로 두고, actor 진입은 wrapper가 직접 처리해야 합니다. 한편 Swift 쪽과 C++ 쪽에서 처리되는 메시지 사이의 dispatch 순서도 중요한데, 이 때문에 handler는 첫 suspension 지점까지 inline으로 실행되어야 합니다. Task.immediate는 이를 보장하지만 Task.init은 보장하지 않습니다.

이번 reland에서는 생성되는 Swift handler가 reply를 직접 반환하고, async로 선언될 수도 있도록 변경되었습니다. 동기 wrapper는 MainActor.assumeIsolated로 @MainActor에 진입한 뒤, Task.immediate로 handler를 시작합니다. OS 26 이전 시스템에서는 back-deploy된 startOnMainActor SPI가 대신 사용됩니다. UIProcess의 WebBackForwardList handler들도 이 경로로 옮겨졌고, -Werror RegionIsolation도 함께 활성화되었습니다. 또한 Windows Swift compiler 버그를 우회하기 위해, 인자를 메시지마다 생성되는 Invocation 클래스에 담아 전달하도록 구현되었습니다.

Swift IPC reply를 누락하거나 두 번 전송하는 실수는 이제 구조적으로 발생할 수 없습니다. 다만 handler가 suspend될 수 있게 되면서, UIProcess IPC의 순서 및 reentrancy에 대한 가정이 달라집니다. WebContent가 제어하는 입력을 받는 실제 UIProcess handler들이 이미 이 경로에서 실행되고 있습니다.