Skip to content

Commit 2854260

Browse files
authored
Merge pull request #7501 from cloudflare/jasnell/ts-streams-pipe-dest-erroring-pin
Pin that a pipe waits for an erroring destination's in-flight write
2 parents 31ff41b + 7b7aa5d commit 2854260

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

‎src/tests/streams/piping/AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ a deliberate defect pin, not a hole).
2424
| 4 | ws.close() queued BEFORE pipeTo | pipe locks both ends, waits, cancels source with 'This destination writable stream is closed.', then RESOLVES (see the TODO(conform) in the test) | pipe REJECTS IMMEDIATELY 'Destination closed before the pipe completed', cancels source with the same error (preventCancel suppresses), locks never observed held | `pipeToJsToJsCloseQueuedDestination`(+`PreventCancel`) |
2525
| 5 | pipeTo brand check on a broken `this` | THROWS synchronously (before the capture_async_api_throws wrapper; the WPT general.any seed); a real stream with a bad destination REJECTS | both reject (spec) | `brandChecks` |
2626
| 6 | destination hwm 0 (never desires) | pipe writes an available chunk anyway — ignores desiredSize (the WPT 'dest never desires chunks' seed family) | never writes (spec) | `sourceErroredAfterChunkHwmZero` |
27-
| 7 | dest controller error()s while the pipe waits on a read | HALF-PROPAGATES: cancels the source with the error but FULFILLS the pipe promise | rejects the pipe and cancels the source with the error (spec) | `destControllerErrorsMidPipe` |
27+
| 7 | dest controller error()s while the pipe waits on a read | HALF-PROPAGATES: cancels the source with the error but FULFILLS the pipe promise | rejects the pipe and cancels the source with the error (spec) | `destControllerErrorsMidPipe`, `destErroringWaitsForInFlightWrite` |
2828
| 8 | FixedLengthStream length violations via pipe | overflow: pipe NEVER SETTLES (bounded); underflow: never settles | overflow: rejects RangeError; underflow: never settles (parity of nonconformance) | `fixedLengthStreamPipeOverflow`/`Underflow` |
2929
| 9 | already-closed source → already-closed dest | rejects TypeError (a C++ deviation: the spec's ordered shutdown conditions give closing-forward priority) | FULFILLS (spec; WPT multiple-propagation 'closed readable to closed writable' pins the fulfillment) | `closedSourceToClosedDest` |
3030
| 10 | SharedArrayBuffer-backed views into CompressionStream | copies the shared bytes; round-trips | write path REJECTS TypeError 'The provided value is not of type (ArrayBuffer or ArrayBufferView)' (spec: BufferSource without [AllowShared]) — while its identity stream ACCEPTS the same views | `sabViewThroughCompressionRoundTrip` |
@@ -77,7 +77,7 @@ the source FIRST, then releasing the write (`pipeStopsPullingWhenDestStalls`).
7777
| `pipe-matrix.js` | migrated pipe-streams-test.js wholesale (35): pipeThrough + pipeTo across JS↔native in all directions, prevent* combos, pre-aborted and mid-read AbortSignals, tee'd pipes, queued-destination close (ledger #1-#4, #13) |
7878
| `api-surface.js` | brand checks (ledger #5), option getter order, throwing getters, invalid signal, locked pipeThrough endpoints |
7979
| `error-propagation.js` | forward matrix (starts-errored × prevent* × truthy), hwm-0 dest (ledger #6), custom-error preservation (migrated from streams-error-edge-cases-test.js) |
80-
| `close-propagation.js` | the WPT-disabled backward territory, bounded: external close/abort on piped dest, write-throw backward propagation, idle dest-controller error (ledger #7) |
80+
| `close-propagation.js` | the WPT-disabled backward territory, bounded: external close/abort on piped dest, write-throw backward propagation, idle dest-controller error and one with a write in flight (ledger #7) |
8181
| `flow-control.js` | backpressure chain (migrated from streams-backpressure-test.js), stalled-dest read-ahead bound |
8282
| `shutdown-backlog.js` | shutdown with chunks buffered in the source: abort, source error, invalid identity chunk; what is written and what stays readable (ledger #11, #12) |
8383
| `shutdown-pending-read.js` | abort while the pipe waits on a read: a chunk arriving during the shutdown's wait, in the abort's turn, or after an idle abort (ledger #14, #15) |

‎src/tests/streams/piping/close-propagation.js‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,50 @@ export const destControllerErrorsMidPipe = {
164164
}
165165
},
166166
};
167+
168+
// The destination's controller errors while a write is in flight: the
169+
// destination is erroring until the write settles, and only then errored.
170+
// Backward propagation waits for errored (spec), so the source is not
171+
// cancelled while the write is outstanding. DIVERGENCE (ledger #7): C++
172+
// then cancels the source with the error but FULFILLS the pipe promise.
173+
export const destErroringWaitsForInFlightWrite = {
174+
async test() {
175+
const derr = new Error('dest-err');
176+
let cancelArg = 'not-called';
177+
const rs = new ReadableStream({
178+
start(c) {
179+
c.enqueue('a');
180+
},
181+
cancel(r) {
182+
cancelArg = r;
183+
},
184+
});
185+
let wc;
186+
let releaseWrite;
187+
const writeStarted = Promise.withResolvers();
188+
const ws = new WritableStream({
189+
start(c) {
190+
wc = c;
191+
},
192+
write() {
193+
writeStarted.resolve();
194+
return new Promise((r) => (releaseWrite = r));
195+
},
196+
});
197+
const pipeP = rs.pipeTo(ws);
198+
await writeStarted.promise;
199+
wc.error(derr);
200+
const early = await outcomeOf(pipeP, 50);
201+
strictEqual(early.state, 'pending');
202+
strictEqual(cancelArg, 'not-called');
203+
releaseWrite();
204+
const outcome = await outcomeOf(pipeP);
205+
if (usingTsImpl) {
206+
strictEqual(outcome.state, 'rejected');
207+
strictEqual(outcome.reason, derr);
208+
} else {
209+
strictEqual(outcome.state, 'fulfilled');
210+
}
211+
strictEqual(cancelArg, derr);
212+
},
213+
};

‎src/tests/streams/piping/main.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export {
7070
destWriteThrowsMidPipe,
7171
destWriteThrowsMidPipePreventCancel,
7272
destControllerErrorsMidPipe,
73+
destErroringWaitsForInFlightWrite,
7374
} from 'close-propagation';
7475

7576
export {

0 commit comments

Comments
 (0)