-
Notifications
You must be signed in to change notification settings - Fork 162
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
Various fixes for readable byte streams #1123
Conversation
9ceda3c
to
7fdb8a5
Compare
I also noticed that ReadableByteStreamControllerRespondWithNewView can still throw an error after we've already updated the first pull-into descriptor's buffer to the new view's buffer. Specifically, ReadableByteStreamControllerRespondInternal may still throw a I think we will need to turn these checks into asserts inside |
Hmm, we should probably also tackle #1001 while we're at it. It's often not enough to check if |
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 think we will need to turn these checks into asserts inside RespondInternal(), and throw errors in Respond() and RespondWithNewView()?
That does sound nicer. It's possible our original reasoning was that in error states like these, there are no guarantees, but we might as well make it better.
reference-implementation/lib/ReadableByteStreamController-impl.js
Outdated
Show resolved
Hide resolved
I'm working on a fix for the Chrome crash that Mattias found, which I will land before the rest of these changes. |
This should be allowed: controller.close(); controller.byobRequest.respondWithNewView(transferredView);
…mControllerRespondInternal
…RespondWithNewView()
I am interested in this on behalf of Chrome. Do you want me to file an issue for Chrome? |
Sure, go ahead. 🙂 Who do we need to ping for interest from other browsers? Do we need to file bugs for other browsers? AFAIK readable byte streams are only implemented in Chrome at the moment, other browsers haven't implemented it yet. |
IMO it's OK to treat bugfixes in readable byte streams as falling under the general precedent that other browsers have already expressed interest in readable byte streams; we don't need to block on them reviewing the fixes. (Although they're welcome to comment!) It's kind of a judgment call what's a bugfix vs. a normative change that would rise to the level of needing confirmation, but IMO these are all fine. |
…fixes, a=testonly Automatic update from web-platform-tests Streams: tests for readable byte stream fixes Follows whatwg/streams#1123. -- wpt-commits: 7b29ee36cc22bdad06b4f98df73358ca959fe0a7 wpt-pr: 28557
…fixes, a=testonly Automatic update from web-platform-tests Streams: tests for readable byte stream fixes Follows whatwg/streams#1123. -- wpt-commits: 7b29ee36cc22bdad06b4f98df73358ca959fe0a7 wpt-pr: 28557
…fixes, a=testonly Automatic update from web-platform-tests Streams: tests for readable byte stream fixes Follows whatwg/streams#1123. -- wpt-commits: 7b29ee36cc22bdad06b4f98df73358ca959fe0a7 wpt-pr: 28557
Previously, if you cancel the stream while a BYOB read is pending, the stream has to wait for the underlying byte source to call respond(0) before it can return the BYOB request's buffer to the caller. This makes underlying byte sources difficult to write in a robust way. After this change, the contract changes so that canceling a stream while a BYOB read is pending will always lead to the stream not giving you back your buffer. This means that cancel() can immediately resolve all pending BYOB reads with the classic { done: true, value: undefined }, without having to wait for the underlying byte source. This solves the problem, and would make it easier to implement an underlying byte source. To make this work, an additional change was required: when the stream is canceled, any pending BYOB request is now immediately invalidated, so the underlying byte source doesn't erroneously think that it still needs to provide a response. (This aligns with the behavior of controller.enqueue(), which throws if the stream is already closed.) See #1114 (comment) and #1123 (comment) for some discussion and background.
Previously, if you cancel the stream while a BYOB read is pending, the stream has to wait for the underlying byte source to call respond(0) before it can return the BYOB request's buffer to the caller. This makes underlying byte sources difficult to write in a robust way. After this change, the contract changes so that canceling a stream while a BYOB read is pending will always lead to the stream not giving you back your buffer. This means that cancel() can immediately resolve all pending BYOB reads with the classic { done: true, value: undefined }, without having to wait for the underlying byte source. This solves the problem, and would make it easier to implement an underlying byte source. To make this work, an additional change was required: when the stream is canceled, any pending BYOB request is now immediately invalidated, so the underlying byte source doesn't erroneously think that it still needs to provide a response. (This aligns with the behavior of controller.enqueue(), which throws if the stream is already closed.) See whatwg#1114 (comment) and whatwg#1123 (comment) for some discussion and background.
FYI since I couldn't find a Chromium issue for this spec change yet, I've filed one myself: https://crbug.com/1223565 |
Thanks! I dropped the ball there. |
This CL is based on spec changes made in whatwg/streams#1123. Bug: 1223565 Change-Id: I7235817f6f18e161d721212971c042624df059d2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216570 Commit-Queue: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Adam Rice <ricea@chromium.org> Cr-Commit-Position: refs/heads/main@{#930929}
Based on changes made in whatwg/streams#1123, we should check that the buffer is actually "transferable" in enqueue(chunk) and respondWithNewView(newView). However, the TypeError exceptions resulting from this check were not properly propagated in the implementation until now. This CL fixes this issue to prevent crashes while dealing with readable byte streams with non-transferable buffers. Bug: 1274019 Change-Id: I20e61fa7c2724a1ac958dbf40199d095fe320707 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3327856 Reviewed-by: Yutaka Hirano <yhirano@chromium.org> Commit-Queue: Nidhi Jaju <nidhijaju@chromium.org> Cr-Commit-Position: refs/heads/main@{#950863}
This CL is based on spec changes made in whatwg/streams#1123. Bug: 1223565 Change-Id: I7235817f6f18e161d721212971c042624df059d2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216570 Commit-Queue: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Adam Rice <ricea@chromium.org> Cr-Commit-Position: refs/heads/main@{#930929} NOKEYCHECK=True GitOrigin-RevId: 3280161d249fef949e4901799827ad81f5df082c
Based on changes made in whatwg/streams#1123, we should check that the buffer is actually "transferable" in enqueue(chunk) and respondWithNewView(newView). However, the TypeError exceptions resulting from this check were not properly propagated in the implementation until now. This CL fixes this issue to prevent crashes while dealing with readable byte streams with non-transferable buffers. Bug: 1274019 Change-Id: I20e61fa7c2724a1ac958dbf40199d095fe320707 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3327856 Reviewed-by: Yutaka Hirano <yhirano@chromium.org> Commit-Queue: Nidhi Jaju <nidhijaju@chromium.org> Cr-Commit-Position: refs/heads/main@{#950863} NOKEYCHECK=True GitOrigin-RevId: 958bd007b37f4b16daf4802b79d5e87070370a9f
This started as a few fixes that we encountered while working on #1114, but it ended up being... a lot of fixes. 😅
.respondWithNewView(newView)
can now be called with anewView
that is smaller than the BYOB request's view. This aligns it with.respond(bytesWritten)
, which also allowsbytesWritten <= byobRequest.view.byteLength
..respondWithNewView()
must now be called with an empty view when the stream is closed. This aligns it with.respond(bytesWritten)
, which requiresbytesWritten
to be 0 when closed..respondWithNewView(newView)
must now be called with a view whoseview.buffer.byteLength
matches that of the BYOB request. Ideally, we would like to assert that the new view's buffer is the "transferred version" of the BYOB request's buffer, but that's not possible yet with the tools currently provided by the ECMAScript specification..enqueue(chunk)
and.respondWithNewView(newView)
now check that the given view's buffer is actually transferable. Previously, we only checked whether the buffer is not yet detached, but this is insufficient: aWebAssembly.Memory
's buffer is never transferable. We also make sure to not transfer the given buffer until after we've checked all other preconditions, so the buffer is still intact if these methods were to throw an error..enqueue()
and.respond()
now check that the BYOB request's view has not been transferred, since otherwise it's not possible to copy bytes into its buffer and/or transfer the buffer when committing. This could crash Chrome, but has already been fixed in https://crbug.com/1200302. The reproduction case (see below) is now covered by a WPT test..enqueue()
,.respond()
and.respondWithNewView()
immediately invalidate the BYOB request. Previously, we only did this if we actually filled the first pull-into descriptor, which doesn't always happen. (For example: if the pull-into descriptor's element size is 4, but we only have filled 1 or 2 bytes.)Original description
While working on #1114, we found that
byobRequest.respondWithNewView(newView)
didn't support all the same use cases as a regular.respond(bytesWritten)
call. In particular:newView
was required to be the same length as the BYOB request's view. This would have required the underlying byte source to always fill the entire view. On the other hand,.respond()
allows responding with fewer bytes up to the view's length, which is the desired behavior..respond()
can only be called withbytesWritten
set to 0. To match this,.respondWithNewView()
should only be called with an empty view in the closed state.We now also require the view passed to
.respondWithNewView(newView)
to have the same capacity as the BYOB request's original buffer. Ideally, we would like to assert that the new view's buffer is the "transferred version" of the BYOB request's buffer, but that's not possible yet with the tools currently provided by the ECMAScript specification.I also found cases where the stream would try to transfer an already transferred buffer. For example, this code crashes the tab in Chrome 92 (error code:
STATUS_ACCESS_VIOLATION
):I fixed this by requiring the BYOB request's buffer to not be detached when calling
byteStreamController.enqueue()
or.close()
.@ricea
)(See WHATWG Working Mode: Changes for more details.)
Preview | Diff