Skip to content

Commit 50b355e

Browse files
authored
stream: reject closed only after sink abort settles
Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com> PR-URL: #65727 Fixes: #65726 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 1278496 commit 50b355e

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

lib/internal/webstreams/writablestream.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,13 @@ function writableStreamFinishErroring(stream) {
928928
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
929929
return;
930930
}
931+
// The closed promises are only rejected once the sink's abort algorithm
932+
// settles, so materialize them here: deriving them from the 'errored'
933+
// state within that window would hand out an already rejected promise.
934+
stream[kState].closedPromise ??= PromiseWithResolvers();
935+
const writer = stream[kState].writer;
936+
if (writer !== undefined)
937+
writer[kState].close ??= PromiseWithResolvers();
931938
PromisePrototypeThen(
932939
stream[kState].controller[kAbort](abortRequest.reason),
933940
() => {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
// The writer's closed promise stays pending until the sink's abort()
4+
// algorithm settles, even when it is first observed after the stream has
5+
// already reached the 'errored' state.
6+
7+
const common = require('../common');
8+
const assert = require('assert');
9+
const { setImmediate: immediate } = require('timers/promises');
10+
const { WritableStream } = require('stream/web');
11+
12+
async function main() {
13+
const error = new Error('boom');
14+
const { promise: abortComplete, resolve: finishAbort } = Promise.withResolvers();
15+
const ws = new WritableStream({
16+
abort: common.mustCall((reason) => {
17+
assert.strictEqual(reason, error);
18+
return abortComplete;
19+
}),
20+
});
21+
const writer = ws.getWriter();
22+
const aborted = writer.abort(error);
23+
24+
// Lets the stream finish erroring and call the sink's abort().
25+
await immediate();
26+
27+
let closedSettled = false;
28+
const closed = writer.closed.catch(common.mustCall((reason) => {
29+
closedSettled = true;
30+
assert.strictEqual(reason, error);
31+
}));
32+
33+
await immediate();
34+
assert.strictEqual(closedSettled, false);
35+
36+
finishAbort();
37+
await aborted;
38+
await closed;
39+
assert.strictEqual(closedSettled, true);
40+
}
41+
42+
main().then(common.mustCall());

0 commit comments

Comments
 (0)