Skip to content

Commit d0bd912

Browse files
authored
Define WritableStreamDefaultController's signal and abortReason
This allows an underlying sink to signal abort to the streams machinery even when it has a pending write or close operation. Fixes #1015.
1 parent c5ca883 commit d0bd912

File tree

5 files changed

+58
-2
lines changed

5 files changed

+58
-2
lines changed

index.bs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4041,6 +4041,8 @@ The Web IDL definition for the {{WritableStreamDefaultController}} class is give
40414041
<xmp class="idl">
40424042
[Exposed=(Window,Worker,Worklet)]
40434043
interface WritableStreamDefaultController {
4044+
readonly attribute any abortReason;
4045+
readonly attribute AbortSignal signal;
40444046
undefined error(optional any e);
40454047
};
40464048
</xmp>
@@ -4060,6 +4062,9 @@ the following table:
40604062
<td><dfn>\[[abortAlgorithm]]</dfn>
40614063
<td class="non-normative">A promise-returning algorithm, taking one argument (the abort reason),
40624064
which communicates a requested abort to the [=underlying sink=]
4065+
<tr>
4066+
<td><dfn>\[[abortReason]]</dfn>
4067+
<td class="non-normative">The argument given to [$WritableStreamAbort$], or undefined.
40634068
<tr>
40644069
<td><dfn>\[[closeAlgorithm]]</dfn>
40654070
<td class="non-normative">A promise-returning algorithm which communicates a requested close to
@@ -4071,6 +4076,10 @@ the following table:
40714076
<td><dfn>\[[queueTotalSize]]</dfn>
40724077
<td class="non-normative">The total size of all the chunks stored in
40734078
[=WritableStreamDefaultController/[[queue]]=] (see [[#queue-with-sizes]])
4079+
<tr>
4080+
<td><dfn>\[[signal]]</dfn>
4081+
<td class="non-normative">An {{AbortSignal}} that can be used to abort the pending write or
4082+
close operation when the stream is [=abort a writable stream|aborted=].
40744083
<tr>
40754084
<td><dfn>\[[started]]</dfn>
40764085
<td class="non-normative">A boolean flag indicating whether the [=underlying sink=] has finished
@@ -4097,9 +4106,13 @@ The <dfn>close sentinel</dfn> is a unique value enqueued into
40974106
[=WritableStreamDefaultController/[[queue]]=], in lieu of a [=chunk=], to signal that the stream is
40984107
closed. It is only used internally, and is never exposed to web developers.
40994108

4100-
<h4 id="ws-default-controller-prototype">Methods</h4>
4109+
<h4 id="ws-default-controller-prototype">Methods and properties</h4>
41014110

41024111
<dl class="domintro non-normative">
4112+
<dt><code><var ignore>controller</var>.{{WritableStreamDefaultController/signal}}</code>
4113+
<dd>
4114+
<p>An AbortSignal that can be used to abort the pending write or close operation when the stream is
4115+
[=abort a writable stream|aborted=].
41034116
<dt><code><var ignore>controller</var>.{{WritableStreamDefaultController/error()|error}}(<var ignore>e</var>)</code>
41044117
<dd>
41054118
<p>Closes the controlled writable stream, making all future interactions with it fail with the
@@ -4111,6 +4124,20 @@ closed. It is only used internally, and is never exposed to web developers.
41114124
sink=].
41124125
</dl>
41134126

4127+
<div algorithm>
4128+
The <dfn id="ws-default-controller-abort-reason" attribute
4129+
for="WritableStreamDefaultController">abortReason</dfn> getter steps are:
4130+
4131+
1. Return [=this=].[=WritableStreamDefaultController/[[abortReason]]=].
4132+
</div>
4133+
4134+
<div algorithm>
4135+
The <dfn id="ws-default-controller-signal" attribute
4136+
for="WritableStreamDefaultController">signal</dfn> getter steps are:
4137+
4138+
1. Return [=this=].[=WritableStreamDefaultController/[[signal]]=].
4139+
</div>
4140+
41144141
<div algorithm>
41154142
The <dfn id="ws-default-controller-error" method
41164143
for="WritableStreamDefaultController">error(|e|)</dfn> method steps are:
@@ -4252,8 +4279,19 @@ The following abstract operations operate on {{WritableStream}} instances at a h
42524279
id="writable-stream-abort">WritableStreamAbort(|stream|, |reason|)</dfn> performs the following
42534280
steps:
42544281

4282+
1. If |stream|.[=WritableStream/[[state]]=] is "`closed`" or "`errored`", return
4283+
[=a promise resolved with=] undefined.
4284+
1. Set
4285+
|stream|.[=WritableStream/[[controller]]=].[=WritableStreamDefaultController/[[abortReason]]=]
4286+
to |reason|.
4287+
1. [=Signal abort=] on
4288+
|stream|.[=WritableStream/[[controller]]=].[=WritableStreamDefaultController/[[signal]]=].
42554289
1. Let |state| be |stream|.[=WritableStream/[[state]]=].
42564290
1. If |state| is "`closed`" or "`errored`", return [=a promise resolved with=] undefined.
4291+
4292+
<p class="note">We re-check the state because [=signaling abort=] runs author code and that might
4293+
have changed the state.
4294+
42574295
1. If |stream|.[=WritableStream/[[pendingAbortRequest]]=] is not undefined, return
42584296
|stream|.[=WritableStream/[[pendingAbortRequest]]=]'s [=pending abort request/promise=].
42594297
1. Assert: |state| is "`writable`" or "`erroring`".
@@ -4677,6 +4715,8 @@ The following abstract operations support the implementation of the
46774715
1. Set |controller|.[=WritableStreamDefaultController/[[stream]]=] to |stream|.
46784716
1. Set |stream|.[=WritableStream/[[controller]]=] to |controller|.
46794717
1. Perform ! [$ResetQueue$](|controller|).
4718+
1. Set |controller|.[=WritableStreamDefaultController/[[abortReason]]=] to undefined.
4719+
1. Set |controller|.[=WritableStreamDefaultController/[[signal]]=] to a new {{AbortSignal}}.
46804720
1. Set |controller|.[=WritableStreamDefaultController/[[started]]=] to false.
46814721
1. Set |controller|.[=WritableStreamDefaultController/[[strategySizeAlgorithm]]=] to
46824722
|sizeAlgorithm|.

reference-implementation/lib/WritableStreamDefaultController-impl.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ const { AbortSteps, ErrorSteps } = require('./abstract-ops/internal-methods.js')
55
const { ResetQueue } = require('./abstract-ops/queue-with-sizes.js');
66

77
exports.implementation = class WritableStreamDefaultControllerImpl {
8+
get abortReason() {
9+
return this._abortReason;
10+
}
11+
get signal() {
12+
return this._abortController.signal;
13+
}
14+
815
error(e) {
916
const state = this._stream._state;
1017

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
[Exposed=(Window,Worker,Worklet)]
22
interface WritableStreamDefaultController {
3+
readonly attribute any abortReason;
4+
readonly attribute AbortSignal signal;
35
void error(optional any e);
46
};

reference-implementation/lib/abstract-ops/writable-streams.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ function IsWritableStreamLocked(stream) {
104104
}
105105

106106
function WritableStreamAbort(stream, reason) {
107+
if (stream._state === 'closed' || stream._state === 'errored') {
108+
return promiseResolvedWith(undefined);
109+
}
110+
stream._controller._abortReason = reason;
111+
stream._controller._abortController.abort();
107112
const state = stream._state;
108113
if (state === 'closed' || state === 'errored') {
109114
return promiseResolvedWith(undefined);
@@ -537,6 +542,8 @@ function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm
537542
controller._queueTotalSize = undefined;
538543
ResetQueue(controller);
539544

545+
controller._abortReason = undefined;
546+
controller._abortController = new AbortController();
540547
controller._started = false;
541548

542549
controller._strategySizeAlgorithm = sizeAlgorithm;

0 commit comments

Comments
 (0)