Re: [whatwg/streams] Support teeing readable byte streams (#1114)

@MattiasBuelens commented on this pull request.



> +          if (forBranch2 === true) {
+            if (canceled1 === false) {
+              const clonedChunk = CloneArrayBufferView(chunk);
+              ReadableByteStreamControllerEnqueue(branch1._controller, clonedChunk);
+            }
+            if (canceled2 === true) {
+              chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, 0);
+            }
+            ReadableByteStreamControllerRespondWithNewView(branch2._controller, chunk);
+          } else {
+            if (canceled2 === false) {
+              const clonedChunk = CloneArrayBufferView(chunk);
+              ReadableByteStreamControllerEnqueue(branch2._controller, clonedChunk);
+            }
+            if (canceled1 === true) {
+              chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, 0);

> I kind of thought the stream machinery handled the case of returning the buffers when closed for us?

No, it doesn't. If you look at [ReadableStreamClose](https://streams.spec.whatwg.org/commit-snapshots/dd76a17a3738d78708a8dfd8f0508e717d6a1988/#readable-stream-close), it only runs the close steps of read requests from a *default reader*. It doesn't run any close steps of read-into requests from a *BYOB reader*.

And that's because *it cannot*. In order to run those close steps, it must construct a _chunk_ with a transferred version of the original read-into request's buffer. But it doesn't *have* that buffer, since the underlying byte source might have transferred it from `byobRequest.view` to some other API to do the reading. (For example, by passing it to another `byobReader.read(view)` call like in `ReadableByteStreamTee`.)

We can only call these close steps when we have received control back of that buffer, which must happen through either `byobRequest.respond()` (if the byte source didn't transfer it and instead wrote directly into the buffer) or `.respondWithNewView()` (if it did transfer it). 

This also means that *every underlying byte source implementation* must do something like this in its `cancel` algorithm:
```javascript
new ReadableStream({
  type: 'bytes',
  start(controller) {
    this._controller = controller;
  },
  pull(controller) {
    // call controller.enqueue() or controller.byobRequest.respond() somehow...
  },
  cancel() {
    // finish any pending BYOB request (even if we never used it ourselves)
    this._controller.byobRequest?.respond(0);
  }
});
```
Otherwise, the last BYOB read can never resolve to `{ value: emptyChunk, done: true }` after canceling the stream.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/pull/1114#discussion_r601907474

Received on Thursday, 25 March 2021 23:43:28 UTC