RESOLVED FIXED Bug 220038
Unable to postMessage a WebAssembly module to a worklet
https://bugs.webkit.org/show_bug.cgi?id=220038
Summary Unable to postMessage a WebAssembly module to a worklet
Richard Newman
Reported 2020-12-19 09:50:16 PST
WebAssembly modules can be sent between workers and pages via postMessage. Both Firefox and Chrome also allow WebAssembly modules to be sent from a page to an AudioWorklet via its MessagePort: https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode/port E.g., ``` this.port.postMessage({ message: 'hey', module }); ``` in Safari Technology Preview (117, WebKit 15611.1.7.2), this throws ``` DataCloneError: The object can not be cloned. ``` (FWIW, adding `module` to the transfer list doesn't help; I guess Safari doesn't support transfer lists either?) The lack of this capability prevents a common code loading approach for AudioWorklets, where some large chunk of WebAssembly is loaded in a worker and sent via the page down to the AudioWorklet via the MessagePort. I have not yet verified whether there's a workaround by posting a multi-megabyte ArrayBuffer and compiling it inside the worklet.
Attachments
WIP Patch (8.86 KB, patch)
2021-01-28 17:06 PST, Chris Dumez
no flags
Test case (87.28 KB, application/zip)
2021-08-12 09:39 PDT, letz
no flags
WIP patch (19.28 KB, patch)
2021-08-18 14:39 PDT, Chris Dumez
no flags
WIP patch (19.16 KB, patch)
2021-08-18 14:45 PDT, Chris Dumez
no flags
WIP patch (29.80 KB, patch)
2021-08-18 16:09 PDT, Chris Dumez
ews-feeder: commit-queue-
WIP patch (23.10 KB, patch)
2021-08-18 17:18 PDT, Chris Dumez
ews-feeder: commit-queue-
WIP patch (23.56 KB, patch)
2021-08-19 08:25 PDT, Chris Dumez
ews-feeder: commit-queue-
Patch (30.42 KB, patch)
2021-08-19 08:42 PDT, Chris Dumez
ews-feeder: commit-queue-
Patch (30.45 KB, patch)
2021-08-19 08:59 PDT, Chris Dumez
no flags
Patch (35.85 KB, patch)
2021-08-19 10:40 PDT, Chris Dumez
no flags
Patch (35.88 KB, patch)
2021-08-23 08:11 PDT, Chris Dumez
no flags
Patch (37.13 KB, patch)
2021-08-23 10:21 PDT, Chris Dumez
no flags
Radar WebKit Bug Importer
Comment 1 2020-12-22 12:28:34 PST
Yusuke Suzuki
Comment 2 2020-12-30 21:47:06 PST
Currently, MessagePort postMessage w/ WebAssembly.Module is not supported. We need to know the notion of the followings. 1. This pair of MessagePorts are in the same process so that we can send SerializedScriptValue without using IPC back-and-force 2. This pair of MessagePorts are in the different process so that we should just throw an onmessageerror exception. Chris, do you know about MessagePort's remote/not-remote flags? Is it specified? Note: https://bugzilla.mozilla.org/show_bug.cgi?id=1605566
Yusuke Suzuki
Comment 3 2020-12-30 22:05:13 PST
Note that LayoutTests/imported/w3c/web-platform-tests/wasm/serialization/module/window-messagechannel-success.html covers this.
Chris Dumez
Comment 4 2021-01-05 08:48:39 PST
(In reply to Yusuke Suzuki from comment #2) > Currently, MessagePort postMessage w/ WebAssembly.Module is not supported. > > We need to know the notion of the followings. > > 1. This pair of MessagePorts are in the same process so that we can send > SerializedScriptValue without using IPC back-and-force > 2. This pair of MessagePorts are in the different process so that we should > just throw an onmessageerror exception. > > Chris, do you know about MessagePort's remote/not-remote flags? Is it > specified? > > Note: https://bugzilla.mozilla.org/show_bug.cgi?id=1605566 @Yusuke: I don't quite understand the need for "different-process". Our worklets always run in process. The only case where we use message port cross-process is with service workers, which are different from worklets.
Chris Dumez
Comment 5 2021-01-05 09:47:04 PST
(In reply to Chris Dumez from comment #4) > (In reply to Yusuke Suzuki from comment #2) > > Currently, MessagePort postMessage w/ WebAssembly.Module is not supported. > > > > We need to know the notion of the followings. > > > > 1. This pair of MessagePorts are in the same process so that we can send > > SerializedScriptValue without using IPC back-and-force > > 2. This pair of MessagePorts are in the different process so that we should > > just throw an onmessageerror exception. > > > > Chris, do you know about MessagePort's remote/not-remote flags? Is it > > specified? > > > > Note: https://bugzilla.mozilla.org/show_bug.cgi?id=1605566 > > @Yusuke: I don't quite understand the need for "different-process". Our > worklets always run in process. > > The only case where we use message port cross-process is with service > workers, which are different from worklets. Based on the error, it looks like nobody implemented the serialization of modules in SerializedScriptValue? If so, sending the module to an iframe via postMessage would fail too (has this been tested?)
Richard Newman
Comment 6 2021-01-05 09:55:59 PST
I have not tested iframe messaging. Web worker to page seems to work correctly.
Chris Dumez
Comment 7 2021-01-05 09:59:44 PST
(In reply to Richard Newman from comment #6) > I have not tested iframe messaging. > > Web worker to page seems to work correctly. Oh I see. If worker to page works then I would imagine iframe messaging would work too. This must be specific to worklets somehow.
Chris Dumez
Comment 8 2021-01-05 10:05:01 PST
(In reply to Chris Dumez from comment #7) > (In reply to Richard Newman from comment #6) > > I have not tested iframe messaging. > > > > Web worker to page seems to work correctly. > > Oh I see. If worker to page works then I would imagine iframe messaging > would work too. This must be specific to worklets somehow. In SerializedScriptValue: if (m_context != SerializationContext::WorkerPostMessage) { code = SerializationReturnCode::DataCloneError; return true; } When encoding wasm modules. We are likely not using the WorkerPostMessage SerializationContext when messaging audio worklets?
Chris Dumez
Comment 9 2021-01-05 10:08:27 PST
(In reply to Chris Dumez from comment #8) > (In reply to Chris Dumez from comment #7) > > (In reply to Richard Newman from comment #6) > > > I have not tested iframe messaging. > > > > > > Web worker to page seems to work correctly. > > > > Oh I see. If worker to page works then I would imagine iframe messaging > > would work too. This must be specific to worklets somehow. > > In SerializedScriptValue: > > if (m_context != SerializationContext::WorkerPostMessage) { > code = SerializationReturnCode::DataCloneError; > return true; > } > > When encoding wasm modules. We are likely not using the WorkerPostMessage > SerializationContext when messaging audio worklets? MessagePort::postMessage() seems to use the default serialization context, no matter in which context the message port is used: === ExceptionOr<void> MessagePort::postMessage(JSC::JSGlobalObject& state, JSC::JSValue messageValue, PostMessageOptions&& options) { LOG(MessagePorts, "Attempting to post message to port %s (to be received by port %s)", m_identifier.logString().utf8().data(), m_remoteIdentifier.logString().utf8().data()); registerLocalActivity(); Vector<RefPtr<MessagePort>> ports; auto messageData = SerializedScriptValue::create(state, messageValue, WTFMove(options.transfer), ports); ===
Yusuke Suzuki
Comment 10 2021-01-05 12:52:16 PST
(In reply to Chris Dumez from comment #9) > (In reply to Chris Dumez from comment #8) > > (In reply to Chris Dumez from comment #7) > > > (In reply to Richard Newman from comment #6) > > > > I have not tested iframe messaging. > > > > > > > > Web worker to page seems to work correctly. > > > > > > Oh I see. If worker to page works then I would imagine iframe messaging > > > would work too. This must be specific to worklets somehow. > > > > In SerializedScriptValue: > > > > if (m_context != SerializationContext::WorkerPostMessage) { > > code = SerializationReturnCode::DataCloneError; > > return true; > > } > > > > When encoding wasm modules. We are likely not using the WorkerPostMessage > > SerializationContext when messaging audio worklets? > > MessagePort::postMessage() seems to use the default serialization context, > no matter in which context the message port is used: > > === > ExceptionOr<void> MessagePort::postMessage(JSC::JSGlobalObject& state, > JSC::JSValue messageValue, PostMessageOptions&& options) > { > LOG(MessagePorts, "Attempting to post message to port %s (to be received > by port %s)", m_identifier.logString().utf8().data(), > m_remoteIdentifier.logString().utf8().data()); > > registerLocalActivity(); > > Vector<RefPtr<MessagePort>> ports; > auto messageData = SerializedScriptValue::create(state, messageValue, > WTFMove(options.transfer), ports); > === I changed it to WorkerPostMessage serialization context, and it didn't work because MessagePort always serialize (via IPC encoder) SerializedScriptValue and deserialized it even though both ports are used by the workers/worklets in the same process. The problem is that we do not support full serialization of WebAssembly.Module. 1. It becomes super huge size 2. It is really complicated 3. Serialize and deserialize it will double memory size while WebAssembly.Module can be shared between multiple threads safely (it is designed to be a thread-safe). So, we need a notion of "MessageChannel is not used among different processes", and we should avoid using IPC encoder / decoder to serialize SerializedScriptValue for MessagePort. And I think this is what gecko is doing.
Chris Dumez
Comment 11 2021-01-05 17:48:19 PST
(In reply to Yusuke Suzuki from comment #10) > (In reply to Chris Dumez from comment #9) > > (In reply to Chris Dumez from comment #8) > > > (In reply to Chris Dumez from comment #7) > > > > (In reply to Richard Newman from comment #6) > > > > > I have not tested iframe messaging. > > > > > > > > > > Web worker to page seems to work correctly. > > > > > > > > Oh I see. If worker to page works then I would imagine iframe messaging > > > > would work too. This must be specific to worklets somehow. > > > > > > In SerializedScriptValue: > > > > > > if (m_context != SerializationContext::WorkerPostMessage) { > > > code = SerializationReturnCode::DataCloneError; > > > return true; > > > } > > > > > > When encoding wasm modules. We are likely not using the WorkerPostMessage > > > SerializationContext when messaging audio worklets? > > > > MessagePort::postMessage() seems to use the default serialization context, > > no matter in which context the message port is used: > > > > === > > ExceptionOr<void> MessagePort::postMessage(JSC::JSGlobalObject& state, > > JSC::JSValue messageValue, PostMessageOptions&& options) > > { > > LOG(MessagePorts, "Attempting to post message to port %s (to be received > > by port %s)", m_identifier.logString().utf8().data(), > > m_remoteIdentifier.logString().utf8().data()); > > > > registerLocalActivity(); > > > > Vector<RefPtr<MessagePort>> ports; > > auto messageData = SerializedScriptValue::create(state, messageValue, > > WTFMove(options.transfer), ports); > > === > > I changed it to WorkerPostMessage serialization context, and it didn't work > because MessagePort always serialize (via IPC encoder) SerializedScriptValue > and deserialized it even though both ports are used by the workers/worklets > in the same process. > The problem is that we do not support full serialization of > WebAssembly.Module. > > 1. It becomes super huge size > 2. It is really complicated > 3. Serialize and deserialize it will double memory size while > WebAssembly.Module can be shared between multiple threads safely (it is > designed to be a thread-safe). > > So, we need a notion of "MessageChannel is not used among different > processes", and we should avoid using IPC encoder / decoder to serialize > SerializedScriptValue for MessagePort. And I think this is what gecko is > doing. From MessagePort, you can query identifier() and remoteIdentifier(). Each returns a MessagePortIdentifier which is a struct that contains both a processIdentifier and a portIdentifier. It should therefore be trivial to figure out if the local and remote ports are hosted in the same process check comparing identifier().processIdentifier and remoteIdentifier().processIdentifier.
Chris Dumez
Comment 12 2021-01-14 10:31:08 PST
(In reply to Chris Dumez from comment #11) > (In reply to Yusuke Suzuki from comment #10) > > (In reply to Chris Dumez from comment #9) > > > (In reply to Chris Dumez from comment #8) > > > > (In reply to Chris Dumez from comment #7) > > > > > (In reply to Richard Newman from comment #6) > > > > > > I have not tested iframe messaging. > > > > > > > > > > > > Web worker to page seems to work correctly. > > > > > > > > > > Oh I see. If worker to page works then I would imagine iframe messaging > > > > > would work too. This must be specific to worklets somehow. > > > > > > > > In SerializedScriptValue: > > > > > > > > if (m_context != SerializationContext::WorkerPostMessage) { > > > > code = SerializationReturnCode::DataCloneError; > > > > return true; > > > > } > > > > > > > > When encoding wasm modules. We are likely not using the WorkerPostMessage > > > > SerializationContext when messaging audio worklets? > > > > > > MessagePort::postMessage() seems to use the default serialization context, > > > no matter in which context the message port is used: > > > > > > === > > > ExceptionOr<void> MessagePort::postMessage(JSC::JSGlobalObject& state, > > > JSC::JSValue messageValue, PostMessageOptions&& options) > > > { > > > LOG(MessagePorts, "Attempting to post message to port %s (to be received > > > by port %s)", m_identifier.logString().utf8().data(), > > > m_remoteIdentifier.logString().utf8().data()); > > > > > > registerLocalActivity(); > > > > > > Vector<RefPtr<MessagePort>> ports; > > > auto messageData = SerializedScriptValue::create(state, messageValue, > > > WTFMove(options.transfer), ports); > > > === > > > > I changed it to WorkerPostMessage serialization context, and it didn't work > > because MessagePort always serialize (via IPC encoder) SerializedScriptValue > > and deserialized it even though both ports are used by the workers/worklets > > in the same process. > > The problem is that we do not support full serialization of > > WebAssembly.Module. > > > > 1. It becomes super huge size > > 2. It is really complicated > > 3. Serialize and deserialize it will double memory size while > > WebAssembly.Module can be shared between multiple threads safely (it is > > designed to be a thread-safe). > > > > So, we need a notion of "MessageChannel is not used among different > > processes", and we should avoid using IPC encoder / decoder to serialize > > SerializedScriptValue for MessagePort. And I think this is what gecko is > > doing. > > From MessagePort, you can query identifier() and remoteIdentifier(). Each > returns a MessagePortIdentifier which is a struct that contains both a > processIdentifier and a portIdentifier. It should therefore be trivial to > figure out if the local and remote ports are hosted in the same process > check comparing identifier().processIdentifier and > remoteIdentifier().processIdentifier. Indeed, it seems we are going to the NetworkProcess via IPC even though the source and destination processes are identical. This is definitely suboptimal in general and breaking for Wasm modules since those are currently not IPC-encoded.
Chris Dumez
Comment 13 2021-01-14 11:49:31 PST
With the WIP patch at Bug 220627 (to avoid IPC when both MessagePorts are in the same process) and with a change to MessagePort::postMessage() to use SerializationContext::WorkerPostMessage when necessary, LayoutTests/imported/w3c/web-platform-tests/wasm/serialization/module/window-messagechannel-success.html starts to PASS.
Yusuke Suzuki
Comment 14 2021-01-14 12:42:06 PST
(In reply to Chris Dumez from comment #13) > With the WIP patch at Bug 220627 (to avoid IPC when both MessagePorts are in > the same process) and with a change to MessagePort::postMessage() to use > SerializationContext::WorkerPostMessage when necessary, > LayoutTests/imported/w3c/web-platform-tests/wasm/serialization/module/window- > messagechannel-success.html starts to PASS. Perfect!
Chris Dumez
Comment 15 2021-01-28 17:06:09 PST
Created attachment 418682 [details] WIP Patch
Chris Dumez
Comment 16 2021-01-28 17:18:02 PST
Comment on attachment 418682 [details] WIP Patch View in context: https://bugs.webkit.org/attachment.cgi?id=418682&action=review > Source/WebCore/dom/MessagePort.cpp:141 > + // FIXME: At the time of serialization, we don't know if the target is a window, a worker or something else. We will only know at the time of @Yusuke: Not sure how to best deal with this. Passing SerializationContext::WorkerPostMessage unconditionally here is a hack.
Yusuke Suzuki
Comment 17 2021-02-01 02:03:23 PST
Comment on attachment 418682 [details] WIP Patch View in context: https://bugs.webkit.org/attachment.cgi?id=418682&action=review > LayoutTests/imported/w3c/web-platform-tests/wasm/serialization/module/window-messagechannel-success-expected.txt:2 > +PASS postMessaging to a dedicated worker via MessageChannel allows them to instantiate Can we have a test case that is posting wasm module to message-ports tied to the different process and getting an error in deserialization side? Ideally, we should throw an error, and we should report onmessageerror since we failed deserialization. Maybe, we should add a test for checking that error, and after that, we should file a bug for `onmessageerror` handler invocation and implement it. https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessageerror https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/onmessageerror >> Source/WebCore/dom/MessagePort.cpp:141 >> + // FIXME: At the time of serialization, we don't know if the target is a window, a worker or something else. We will only know at the time of > > @Yusuke: Not sure how to best deal with this. Passing SerializationContext::WorkerPostMessage unconditionally here is a hack. This hack looks OK to me! If we attempt to deserialize non-sharable-between-processes objects, then we will get an error at deserialization (not causing catastrophic crash) since our SerializedScriptValue's deserialization is robust enough against that.
letz
Comment 18 2021-08-12 09:39:46 PDT
Created attachment 435429 [details] Test case
letz
Comment 19 2021-08-12 09:42:17 PDT
Note that WebAssembly modules should be serialisable to be used in processorOptions at construction time between an AudioWorkletNote and and AudioWorkletProcessor.
Chris Dumez
Comment 20 2021-08-18 14:10:06 PDT
Comment on attachment 418682 [details] WIP Patch View in context: https://bugs.webkit.org/attachment.cgi?id=418682&action=review >> LayoutTests/imported/w3c/web-platform-tests/wasm/serialization/module/window-messagechannel-success-expected.txt:2 >> +PASS postMessaging to a dedicated worker via MessageChannel allows them to instantiate > > Can we have a test case that is posting wasm module to message-ports tied to the different process and getting an error in deserialization side? > Ideally, we should throw an error, and we should report onmessageerror since we failed deserialization. > Maybe, we should add a test for checking that error, and after that, we should file a bug for `onmessageerror` handler invocation and implement it. > https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessageerror > https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/onmessageerror WebKit currently doesn't support onmessageerror AFAIK
Chris Dumez
Comment 21 2021-08-18 14:39:25 PDT
Created attachment 435798 [details] WIP patch
Chris Dumez
Comment 22 2021-08-18 14:45:03 PDT
Created attachment 435799 [details] WIP patch
Chris Dumez
Comment 23 2021-08-18 16:09:52 PDT
Created attachment 435812 [details] WIP patch
Chris Dumez
Comment 24 2021-08-18 17:18:06 PDT
Created attachment 435820 [details] WIP patch
Chris Dumez
Comment 25 2021-08-19 08:25:04 PDT
Created attachment 435860 [details] WIP patch
Chris Dumez
Comment 26 2021-08-19 08:42:54 PDT
Chris Dumez
Comment 27 2021-08-19 08:59:22 PDT
Chris Dumez
Comment 28 2021-08-19 10:40:38 PDT
Chris Dumez
Comment 29 2021-08-23 08:11:05 PDT
Chris Dumez
Comment 30 2021-08-23 10:21:14 PDT
Ian Kettlewell
Comment 31 2021-12-18 09:59:52 PST
A key detail worth noting: It looks like WebAssembly.Memory objects cannot be sent between workers via postMessage either. An important use for postMessaging WebAssembly modules / memory is to use an Audio Worklet in a way similar to how a non-web dedicated audio thread would work. The Audio Worklet runs a Wasm instance backed by a SharedArrayBuffer that communicates with the main thread via Wasm atomics. That flow makes it *much* easier to for things like game engines to share audio backend code between non-web and web. I think (but haven't verified) the module being un-postMessageable can be worked around by postMessaging the code and re-instantiating the module on the Worklet. However as far as I'm aware the WebAssembly.Memory object being non-postMessagable does not have a workaround. That blocks the use-case I've described here.
letz
Comment 32 2022-01-27 01:01:17 PST
Any news on this issue? Any expected date for a proper fix to appear? Thanks.
Tibor Klajnscek
Comment 33 2022-03-09 04:20:29 PST
+1 for a status update - this is blocking us from using our audio worklet backend which is critical to consistent and crackle-free audio in our products.
Attila Haraszti
Comment 34 2022-04-19 03:16:37 PDT
This is currently a blocker for us, a fix would be highly appreciated.
Tibor Klajnscek
Comment 35 2022-07-22 03:46:39 PDT
This seems to work in the latest Safari dev preview, but I haven't found any release notes mentioning it. Is it just a fluke or has it been fixed? And if so, will it make it into Safari 16?
Ahmad Saleem
Comment 36 2022-09-30 08:15:36 PDT
@Chris - it seems this patch passes all tests and only have "style" issues? Is it something on your radar and "see also" bug is also fixed? Thanks!
Chris Dumez
Comment 37 2022-11-17 11:05:20 PST
EWS
Comment 38 2022-11-18 13:31:52 PST
Committed 256853@main (a9d61c9d9e2e): <https://commits.webkit.org/256853@main> Reviewed commits have been landed. Closing PR #6596 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.