-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support transferable streams (postMessage) #1053
Conversation
This is ready for review. @domenic and @MattiasBuelens PTAL. There's a minor issue that I haven't figured out how to link I need to check I don't have any gaps in my tests and port them to wpt, but I don't expect that to take long. |
Tip for easier reviewing: The changes are under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good! 🙂
From your comment on #276, I understand that we will not yet support the "double-transfer" case in this first version. Correct?
There is no reference implementation of this functionality as jsdom does not support transferrable objects, and so it wouldn't be testable.
That is indeed unfortunate.
For remote-web-streams, I wrote a mock implementation of MessageChannel
. It doesn't actually do any (de)serialization or send stuff between realms, it's just enough so I can run my tests. But I guess that wouldn't really work for a project like jsdom... 😅
1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$](|controller|, |value|). | ||
1. If |backpressurePromise| is not undefined, | ||
1. [=Resolve=] |backpressurePromise| with undefined. | ||
1. Set |backpressurePromise| to undefined. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. Set |backpressurePromise| to undefined. | |
1. Set |backpressurePromise| to undefined. | |
1. Disentangle |port|. |
The readable's cancelAlgorithm disentangles its port when it sends the "error"
message. Thus, the writable side should do the same when it receives that message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's my understanding that we only need to disentangle the port on one side. Although from the implementers' perspective it might be better to explicitly disentangle on both sides. Let me think about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm leaning towards "no" now, since in platonic spec-land it is redundant to disentangle from both sides, and in practice implementations need to implement MessagePort close()
in such a way that both sides get closed after all queued messages have been delivered.
Correct. It's unfortunate, but I said there, it's very complex to support.
We could polyfill MessageChannel and MessagePort I suppose, but we'd need quite a high level of fidelity and at that point it might be easier just to implement it in jsdom. Anyway, I'd prefer not to block on that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly-editorial feedback. It's great to see this coming together!
1. Perform ! [$InitializeReadableStream$](|stream|). | ||
1. Let |controller| be a [=new=] {{ReadableStreamDefaultController}}. | ||
1. Let |backpressurePromise| be [=a new promise=]. | ||
1. Add a handler for |port|'s {{MessagePort/message}} event with the following steps: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Add a handler" is not well-defined. I've run into this problem before; you need something like https://wicg.github.io/kv-storage/#add-a-simple-event-listener. Maybe we can upstream that into DOM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels odd having to route the event via JavaScript in the standard when in Blink's implementation we just have blink::NativeEventListener. And it's not like it's some weird edge-case either: it's used in loads of places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. To be clear I don't suggest you change anything here, but I'd like to get us better primitives.
1. Set |backpressurePromise| to [=a new promise=]. | ||
1. Otherwise, if |type| is `"close"`, | ||
1. Perform ! [$ReadableStreamDefaultControllerClose$](|controller|). | ||
1. Disentangle |port|. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, it's weird that this is not a defined term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I was confused when I didn't find a definition.
The Streams Standard change whatwg/streams#1053 adds the ability to transfer streams to a different realm using postMessage(). Add tests for this feature.
I've uploaded the tests at web-platform-tests/wpt#24546. I've left it as a draft PR until we are ready to land this, but please feel free to review anyway. |
<dfn abstract-op lt="PackAndPostMessage">PackAndPostMessage(|port|, |type|, |value|)</dfn> performs | ||
the following steps: | ||
|
||
1. Let |message| be [$OrdinaryObjectCreate$](null). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. Let |message| be [$OrdinaryObjectCreate$](null). | |
1. Let |message| be ! [$OrdinaryObjectCreate$](null). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OrdinaryObjectCreate does not return a completion record. This is non-obvious from the definition https://tc39.es/ecma262/2020/#sec-ordinaryobjectcreate but clear from how it is used elsewhere: https://tc39.es/ecma262/2020/#sec-createiterresultobject.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything returns a completion record: https://tc39.es/ecma262/#sec-implicit-completion-values
1. Perform ! [$CreateDataProperty$](|message|, "`type`", |type|). | ||
1. Perform ! [$CreateDataProperty$](|message|, "`value`", |value|). | ||
1. Let |targetPort| be the port with which |port| is entangled, if any; otherwise let it be null. | ||
1. Let |options| be «[ "`transfer`" → « » ]». |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO for me to make these linkable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM; very nice.
We should work on getting a second implementer signoff, especially on the remaining open questions in #276.
1. If ! [$IsReadableStreamLocked$](|readable|) is true, throw a "{{DataCloneError}}" | ||
{{DOMException}}. | ||
1. If ! [$IsWritableStreamLocked$](|writable|) is true, throw a "{{DataCloneError}}" | ||
{{DOMException}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding these checks here, as opposed to delegating to transferring the readable and writable sides independently, avoids ending up with a "half-transferred" transform stream. That's nice :). I wonder if we should <p class="note">
that? In either case we should be sure we have a test for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the tests to test this feature (which is missing from Blink's implementation 😳).
I don't feel I need to call attention to the feature with a note, as it is just a matter of polish.
Can I say that Google Chrome is interested even though I'm the one who wrote the change? 😄 @jorendorff and @youennf could I hear your views on this? |
Yes, you certainly can :) |
Google Chrome is interested! |
3762b86
to
8dd6ff6
Compare
@MattiasBuelens Github thinks I have unresolved comments from you, but I can't tell which they are. Are you okay with landing this, or do you think it needs more work? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. 👍
Perhaps it's because this review comment has a "suggested change" in it that wasn't committed? Anyway, you can ignore that.
Make readable, writable, and transform streams transferable via postMessage(stream, [stream]). The streams themselves must be transferred, but the chunks written or read from the streams are cloned, not transferred. Support for transferring chunks will require API changes and is expected to be added in a future update. There is no reference implementation of this functionality as jsdom does not support transferrable objects, and so it wouldn't be testable. Closes whatwg#276.
Co-authored-by: Mattias Buelens <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Mattias Buelens <[email protected]>
8dd6ff6
to
a5d56a9
Compare
The Streams Standard change whatwg/streams#1053 adds the ability to transfer streams to a different realm using postMessage(). Add tests for this feature.
…stonly Automatic update from web-platform-tests Add tests for transferable streams (#24546) The Streams Standard change whatwg/streams#1053 adds the ability to transfer streams to a different realm using postMessage(). Add tests for this feature. -- wpt-commits: f2eb8b9fa0d0d11429ce4952dbb9dd796c18e0da wpt-pr: 24546
…stonly Automatic update from web-platform-tests Add tests for transferable streams (#24546) The Streams Standard change whatwg/streams#1053 adds the ability to transfer streams to a different realm using postMessage(). Add tests for this feature. -- wpt-commits: f2eb8b9fa0d0d11429ce4952dbb9dd796c18e0da wpt-pr: 24546
Make readable, writable, and transform streams transferable via
postMessage(stream, [stream]).
The streams themselves must be transferred, but the chunks written or
read from the streams are cloned, not transferred. Support for
transferring chunks will require API changes and is expected to be added
in a future update.
There is no reference implementation of this functionality as jsdom does
not support transferable objects, and so it wouldn't be testable.
Closes #276.
(See WHATWG Working Mode: Changes for more details.)
Preview | Diff