Re: [whatwg/streams] Discussion: How should streams handle Explicit Resource Management (Issue #1307)

Le's focus specifically on `ReadableStream` ... and just go through some of the scenarios to tease out the details (much of the following is obvious, I'm detailing it to make the conversation more concrete)

If the `ReadableStream` is not locked, then Async-disposal being the equivalent of `await readable.cancel()` makes sense... but o course, if the `ReadableStream` is locked, `readable.cancel()` fails. So the following would necessarily fail because the reader is not defined as being disposed.

```js
{
  await using rs = new ReadableStream();
  const reader = rs.getReader();
}
```

If we define reader as being sync disposable such that it releases the lock, then the following should work just fine, releasing the lock then async canceling the stream...

```js
{
  await using rs = new ReadableStream();
  using reader = rs.getReader();

  // equivalent to
  //    reader.releaseLock()
  //    await rs.cancel();
}
```

If the stream is locked to a reader, then I do think it is worth considering whether `async using reader` would be equivalent to `await reader.cancel()`:

```js
{
  const rs = new ReadableStream();
  await using reader = rs.getReader();

  // equivalent to
  await reader.cancel();
}
```

This would make sense especially given the idea that the `Reader` is essentially viewed as taking ownership over the stream. But this can be a bit tricky, of course.

In the pipeTo/pipeThrough scenario, the readable stream remains locked so `await readable.cancel()` would be expected to fail, so the following case would end with an error if `async using readable` assumes `await readable.cancel()`

```js
let dest;
{
  await using readable = new ReadableStream();
  dest = readable.pipeThrough(new MyTransformStream());

  // async dispose fails because readable is locked by the pipe
}
```

But what if the destination is disposable? This should work fine, I think? The `dest` should be disposed of first, which should end up releasing the lock on `readable`, allowing it to be canceled?

```js
{
  await using readable = new ReadableStream();
  await using dest = readable.pipeThrough(new MyTransformStream());
}
```

For tee, yeah... that's a bit wonky. Not sure what to do here.

```js
{
  await using readable = new ReadableStream();
  const [b1, b2] = readable.tee();

  // Async dispose fails because readable is locked
}
```

```js
{
  await using readable = new ReadableStream();
  await using [b1, b2] = readable.tee();

  // Async dispose of the branches works fine... but
  // Async dispose of readable still fails since canceling the branches does not release the lock on readable
}
```

Perhaps if we redefined it so that if all branches are canceled/closed the lock on the original stream is released?

Another interesting question, which may actually be a question for the TC-39 proposal authors because I'm not sure if this would even be possible... If the block exists because an exception is thrown, should the pending exception be passed into the `readable.cancel(...)` call when disposing the stream? I don't think this is currently possible with the current spec tho...

That is, for instance:

```js
{
  async using readable = new ReadableStream();
  throw new Error('boom');

  // Should the async dispose be equivalent to `await readable.cancel(error)` where `error` is the error that is thrown
}
```

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/issues/1307#issuecomment-1969256958
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/streams/issues/1307/1969256958@github.com>

Received on Wednesday, 28 February 2024 15:41:02 UTC