-
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
Reject pending reads when releasing reader #1168
Reject pending reads when releasing reader #1168
Conversation
I've fixed Piping was trickier, because there's interaction with "shutdown with an action". Specifically, when
This works perfectly, except for this one test: promise_test(t => {
const rs = recordingReadableStream();
const ws = recordingWritableStream();
const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }),
'pipeTo must reject with the same error');
t.step_timeout(() => ws.controller.error(error1), 10);
return pipePromise.then(() => {
assert_array_equals(rs.eventsWithoutPulls, []);
assert_array_equals(ws.events, []);
});
}, 'Errors must be propagated backward: becomes errored after piping; preventCancel = true'); Because I see two solutions:
|
Perhaps a dumb question, but maybe the tests indicate that we should allow releasing the lock even with pending read requests? I'm not sure that restriction is super well-motivated, and if we've found at least one higher-level combinator (namely pipeTo) where doing so is arduous, there might be more. Otherwise, solving #1103 would make more sense... |
That's also a possibility. I wouldn't want to leave the So basically |
Interesting. I think I like that idea. And we could add Does this semantic of rejecting the read() promises work well even for the piping case? |
4d08211
to
4fb181c
Compare
It works perfectly for Of course we'll need to update some other tests that expect However we still need to figure out what to do with the pending pull-into descriptors when calling |
4fb181c
to
ce7e128
Compare
cc66569
to
8735c80
Compare
I started working on dealing with pending pull-into descriptors after calling When calling
When the underlying byte source eventually calls
For now, I've only updated the reference implementation. If we're okay with the approach, I can look into updating the spec text. |
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.
Generally looks good, with just a few questions!
I slightly lean TypeError? We use it for a lot of similar situations in this spec, and now that abort reasons are a thing, we don't use AbortError DOMException directly at all. |
be00afc
to
17cae42
Compare
Fair enough. In #1103 we had an alternative API proposal: If desired, users could always propagate their abort reason manually: const abortController = new AbortController();
const { signal } = abortController;
const reader = readable.getReader();
signal.addEventListener("abort", () => {
// Cannot propagate `signal.reason` to `releaseLock()`
reader.releaseLock();
});
const read = reader.read().catch((e) => {
// `e` will be a new `TypeError` instead of `signal.reason` here.
// A user could propagate it manually though, if necessary:
if (signal.aborted) {
throw signal.reason;
}
throw e;
}).then(/* ... */);
abortController.abort(new Error("my custom abort reason")); Although I don't think users will commonly want to use an |
The first pull-into descriptor is retained so the underlying byte source can continue filling it. This descriptor becomes "detached" from the corresponding read request, such that when the BYOB request receives a response, it *always* puts all of its bytes into the queue rather than fulfilling a read request. Other pull-into descriptors are discarded immediately, since the underlying byte source cannot fill or have filled those yet.
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
I think this is a good direction. Although it adds some complexity, the new behaviour is easier to work with.
This spec change also affects default readers. We no longer throw a So technically speaking, we merged this PR a bit too early. 😅 We should have asked for their interest first. As discussed on Matrix, I'll mention this when filing spec bugs for all browsers, and ask them to let us know if they have any objections. |
Looked through the bugs and I think you may have forgotten Chrome? |
I filed #1287273 with Chrome. It's mentioned in the OP. |
Sorry, let me emphasize:
Was referring to that last part specifically. |
@nidhijaju and @ricea have already expressed interest on behalf of Chrome in #1168 (review) and #1168 (review), so I didn't deem it necessary to ask again in the Chrome issue. Unless I misunderstood something? Of course, if they still have any objections or remarks, then they are also welcome to discuss them here. 🙂 |
…sing reader, a=testonly Automatic update from web-platform-tests Streams: reject pending reads when releasing reader Follows whatwg/streams#1168. -- wpt-commits: 99d74f9529e16ec0722ef11136ab29b9e80fff26 wpt-pr: 32072
…ONTBUILD This implements the changes from whatwg/streams#1168. Differential Revision: https://phabricator.services.mozilla.com/D137382
…sing reader, a=testonly Automatic update from web-platform-tests Streams: reject pending reads when releasing reader Follows whatwg/streams#1168. -- wpt-commits: 99d74f9529e16ec0722ef11136ab29b9e80fff26 wpt-pr: 32072
…ONTBUILD This implements the changes from whatwg/streams#1168. Differential Revision: https://phabricator.services.mozilla.com/D137382
Previously, calling releaseLock() on ReadableStreamDefaultReader or ReadableStreamBYOBReader while there are pending read requests would throw a TypeError. The specification has been changed[1] to allow this case, and to reject such pending read requests with a TypeError instead. [1] whatwg/streams#1168 Bug: 1287273 Change-Id: Id4013571212e20b0d6ecccdcf68cd6d3927d38b2
Previously, calling releaseLock() on ReadableStreamDefaultReader or ReadableStreamBYOBReader while there are pending read requests would throw a TypeError. The specification has been changed[1] to allow this case, and to reject such pending read requests with a TypeError instead. [1] whatwg/streams#1168 Bug: 1287273 Change-Id: Id4013571212e20b0d6ecccdcf68cd6d3927d38b2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3750760 Commit-Queue: Nidhi Jaju <[email protected]> Reviewed-by: Adam Rice <[email protected]> Cr-Commit-Position: refs/heads/main@{#1023012}
Previously, calling releaseLock() on ReadableStreamDefaultReader or ReadableStreamBYOBReader while there are pending read requests would throw a TypeError. The specification has been changed[1] to allow this case, and to reject such pending read requests with a TypeError instead. [1] whatwg/streams#1168 Bug: 1287273 Change-Id: Id4013571212e20b0d6ecccdcf68cd6d3927d38b2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3750760 Commit-Queue: Nidhi Jaju <[email protected]> Reviewed-by: Adam Rice <[email protected]> Cr-Commit-Position: refs/heads/main@{#1023012} NOKEYCHECK=True GitOrigin-RevId: 1e39b24fb910a075f4d337e6bced80f117dfbd7a
Currently,
ReadableStream(Default|BYOB)Reader.releaseLock()
throws an error if there are still pending read requests (spec):However, in #1103 we realized that it would be more useful if we allowed releasing a reader while there are still pending reads, and to reject those reads instead. The user can then acquire a new reader to receive those chunks.
This PR implements that proposed change:
reader.releaseLock()
no longer throws an error when there are still pending reads. Instead, it immediately rejects all pending reads with aTypeError
.controller.byobRequest
. Instead, we mark this descriptor as "detached", such that when we invalidate this BYOB request (as a result ofcontroller.enqueue()
orbyobRequest.respond()
), the bytes from this descriptor are pushed into the stream's queue and used to fulfill read requests from a future reader.This also allows expressing
pipeTo()
's behavior more accurately. Currently, it "cheats" by callingReadableStreamReaderGenericRelease
directly without checking if[[readRequests]]
is empty. (And yes, there's a test that relies on this happening.) With the proposed changes, we can safely release the reader when the pipe finishes (even if there's a pending read), and be sure that any unread chunks can be read by a future reader or pipe.To do:
TypeError
or aAbortError
DOMException
?TypeError
, see commentOriginally, this PR proposed to only add some extra asserts to ensure that
[[readRequests]]
is empty before releasing the lock. However, as shown in thepipeTo
case (above), this is not sufficient. For context, the original description of that proposal is attached below:Original description
When using
ReadableStream(Default|BYOB)Reader.releaseLock()
, we throw an error if there are still pending read requests (spec):However, when we use the abstract op
ReadableStreamReaderGenericRelease
directly, we do not assert this. Some places in the spec often have an extra assert specifically for this (e.g. async iterator's return steps orReadableByteStreamTee
), but other places fail to perform this check (e.g. async iterator's next steps orReadableStreamPipeTo
).I added this missing check, but now the following WPT tests start failing as a result:
The async iterator test fails because we release the lock in the middle of the read request's close steps, but
ReadableStreamClose
only clears the list of read requests after calling all the close steps. The fix is quite simple: empty the list before calling the close steps or error steps. I'll push a commit for that on this PR.The pipe tests fail because the pipe loop actually starts a read request, but we don't wait for that read to complete before releasing the lock. I think it's possible you could lose a chunk this way, but I haven't yet figured out how. I'll keep trying though. 😛 I think we can solve this one by tracking both
currentWrite
andcurrentRead
, but I'm not sure about the details yet.Other implementations have not yet implemented readable byte streams. As such the editors are treating this as a bugfix to readable byte streams, that falls under their previously-expressed general interest in the feature. (Although, explicit review from other engines would be welcome.)This is incorrect; see Reject pending reads when releasing reader #1168 (comment)(See WHATWG Working Mode: Changes for more details.)
Preview | Diff