whatwg / streams

Streams Standard
https://streams.spec.whatwg.org/
Other
1.35k stars 160 forks source link

Should cancelation error from UnderlyingSource be transferred too? #1259

Open saschanaz opened 1 year ago

saschanaz commented 1 year ago
b = new ReadableStream({
    cancel() {
      throw new Error("Hello");
    }
})
transferred = structuredClone(b, { transfer: [b] })

// This resolves as it never gets the error message back, with its port immediately closed
transferred.cancel();

Couldn't find a relevant discussion, but WPT explicitly tests it to not wait for the underlying source cancel. What was the reason behind it?

I don't have a good argument against the current behavior, just out of curiosity.

ricea commented 1 year ago

The short answer is that it's because transferable streams are implemented using a pipe, and pipes propagate cancellation without waiting.

Elaborating a bit, what we have is

original ---pipe---> writable <==CRIT==> transferred

(where "CRIT" means cross-realm identity transform). The internal pipe is invisible to developers, except to the extent that it dictates the semantics.

When transferred is cancelled, this is propagated back by erroring writable. Erroring is a synchronous operation, so there's no opportunity to wait.

When the pipe sees the writable is errored, it propagates it back by cancelling the original stream. Because writable is already errored, the results of cancellation cannot be propagated back up the pipe.

See also https://github.com/whatwg/streams/issues/976.