Skip to content

Commit c4ebc02

Browse files
committed
Add writable stream owning type
1 parent 2ed1d35 commit c4ebc02

File tree

1 file changed

+54
-18
lines changed

1 file changed

+54
-18
lines changed

index.bs

+54-18
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,26 @@ The following abstract operations operate on {{ReadableStream}} instances at a h
21552155
the operations below, the JavaScript-modifiable reader, writer, and stream APIs (i.e. methods
21562156
on the appropriate prototypes) must not be used. Instead, the streams must be manipulated
21572157
directly.
2158+
<p class="note">
2159+
{{WritableStream}} "{{WritableStreamType/owning}}" and {{ReadableStream}} "{{ReadableStreamType/owning}}"
2160+
has some impact on the piping operation:
2161+
- Piping a not owning {{ReadableStream}} to a not owning {{WritableStream}} will enqueue
2162+
the exact samne JavaScript values.
2163+
- Piping an owning {{ReadableStream}} to an owning {{WritableStream}} is similar to the above case,
2164+
as ownership will be transferred from the {{ReadableStream}} to the {{WritableStream}}.
2165+
In case of error during the piping operation, the user Agent is responsible to call the
2166+
necessary [=closing steps=] of the JavaScript values that were dequeud from the
2167+
{{ReadableStream}} but not successfully enqueued in the {{WritableStream}}.
2168+
- Piping an owning {{ReadableStream}} to a not owning {{WritableStream}} will enqueue
2169+
the serialized/transfered JavaScript values.
2170+
In case of error during the piping operation, the user Agent is responsible to call the
2171+
necessary [=closing steps=] of the JavaScript values that were dequeud from the
2172+
{{ReadableStream}} but not successfully enqueued in the {{WritableStream}}.
2173+
Note that [=closing steps=] of the enqueued JavaScript values in the {{WritableStream}}
2174+
will not be called automatically. It is up to the application to handle this.
2175+
- Piping a not owning {{ReadableStream}} to an owning {{WritableStream}} will trigger
2176+
serialization of the {{ReadableStream}} chunks when enqueued in {{WritableStream}}.
2177+
Piping may fail if the serialization fails, say in case of transferable-only JavaScript values.
21582178
* <strong>Backpressure must be enforced:</strong>
21592179
* While [$WritableStreamDefaultWriterGetDesiredSize$](|writer|) is ≤ 0 or is null, the user
21602180
agent must not read from |reader|.
@@ -4004,13 +4024,14 @@ dictionary UnderlyingSink {
40044024
UnderlyingSinkWriteCallback write;
40054025
UnderlyingSinkCloseCallback close;
40064026
UnderlyingSinkAbortCallback abort;
4007-
any type;
4027+
WritableStreamType type;
40084028
};
40094029

40104030
callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller);
40114031
callback UnderlyingSinkWriteCallback = Promise<undefined> (any chunk, WritableStreamDefaultController controller);
40124032
callback UnderlyingSinkCloseCallback = Promise<undefined> ();
40134033
callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
4034+
enum WritableStreamType { "owning" };
40144035
</xmp>
40154036

40164037
<dl>
@@ -4096,8 +4117,15 @@ callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
40964117

40974118
<dt><dfn dict-member for="UnderlyingSink">type</dfn></dt>
40984119
<dd>
4099-
<p>This property is reserved for future use, so any attempts to supply a value will throw an
4100-
exception.
4120+
<p>Can be set to "<dfn enum-value for="WritaleStreamType">owning</dfn>" to signal that the
4121+
constructed {{WritableStream}} will own chunks (via transfer or serialization) before enqueuing them.
4122+
This ensures that enqueued chunks are not mutable by the source.
4123+
Transferred or serialized chunks may have [=closing steps=]] which are executed if
4124+
enqueued chunks are dequeued without being provided to the application, for instance when
4125+
a {{WritableStream}} is errored.
4126+
4127+
<p>Setting any value other than "{{WritableStreamType/owning}}" or undefined will cause the
4128+
{{WritableStream()}} constructor to throw an exception.
41014129
</dl>
41024130

41034131
The <code>controller</code> argument passed to {{UnderlyingSink/start|start()}} and
@@ -4166,10 +4194,6 @@ as seen for example in [[#example-ws-no-backpressure]].
41664194
<p class="note">We cannot declare the |underlyingSink| argument as having the {{UnderlyingSink}}
41674195
type directly, because doing so would lose the reference to the original object. We need to
41684196
retain the object so we can [=invoke=] the various methods on it.
4169-
1. If |underlyingSinkDict|["{{UnderlyingSink/type}}"] [=map/exists=], throw a {{RangeError}}
4170-
exception.
4171-
<p class="note">This is to allow us to add new potential types in the future, without
4172-
backward-compatibility concerns.
41734197
1. Perform ! [$InitializeWritableStream$]([=this=]).
41744198
1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$](|strategy|).
41754199
1. Let |highWaterMark| be ? [$ExtractHighWaterMark$](|strategy|, 1).
@@ -4264,7 +4288,7 @@ interface WritableStreamDefaultWriter {
42644288
Promise<undefined> abort(optional any reason);
42654289
Promise<undefined> close();
42664290
undefined releaseLock();
4267-
Promise<undefined> write(optional any chunk);
4291+
Promise<undefined> write(optional any chunk, optional StructuredSerializeOptions options = { });
42684292
};
42694293
</xmp>
42704294

@@ -4349,19 +4373,23 @@ following table:
43494373
lock on the writer for the duration of the write; the lock instead simply prevents other
43504374
[=producers=] from writing in an interleaved manner.
43514375

4352-
<dt><code>await <var ignore>writer</var>.{{WritableStreamDefaultWriter/write(chunk)|write}}(<var ignore>chunk</var>)</code>
4376+
<dt><code>await <var ignore>writer</var>.{{WritableStreamDefaultWriter/write(chunk, options)|write}}(<var ignore>chunk</var>, <var ignore>options</var>)</code>
43534377
<dd>
43544378
<p>Writes the given [=chunk=] to the writable stream, by waiting until any previous writes have
43554379
finished successfully, and then sending the [=chunk=] to the [=underlying sink=]'s
43564380
{{UnderlyingSink/write|write()}} method. It will return a promise that fulfills with undefined
43574381
upon a successful write, or rejects if the write fails or stream becomes errored before the
43584382
writing process is initiated.
4383+
<p>If the writable stream has its type set to "{{WritableStreamType/owning}}", the given [=chunk=]
4384+
is serialized with [=options=], which ensures that mutating [=chunk=] will have no impact on what
4385+
is written by the writable stream.
43594386

43604387
<p>Note that what "success" means is up to the [=underlying sink=]; it might indicate simply that
43614388
the [=chunk=] has been accepted, and not necessarily that it is safely saved to its ultimate
43624389
destination.
43634390

4364-
<p id="write-mutable-chunks">If <var ignore>chunk</var> is mutable, [=producers=] are advised to
4391+
<p id="write-mutable-chunks">If <var ignore>chunk</var> is mutable and the writable stream type is
4392+
not "{{WritableStreamType/owning}}", [=producers=] are advised to
43654393
avoid mutating it after passing it to {{WritableStreamDefaultWriter/write()}}, until after the
43664394
promise returned by {{WritableStreamDefaultWriter/write()}} settles. This ensures that the
43674395
[=underlying sink=] receives and processes the same value that was passed in.
@@ -4429,12 +4457,13 @@ following table:
44294457
</div>
44304458

44314459
<div algorithm>
4432-
The <dfn id="default-writer-write" method for="WritableStreamDefaultWriter">write(|chunk|)</dfn>
4460+
The <dfn id="default-writer-write" method for="WritableStreamDefaultWriter">write(|chunk|, |options|)</dfn>
44334461
method steps are:
44344462

4463+
1. Let |transferList| be |options|["transfer"].
44354464
1. If [=this=].[=WritableStreamDefaultWriter/[[stream]]=] is undefined, return [=a promise rejected
44364465
with=] a {{TypeError}} exception.
4437-
1. Return ! [$WritableStreamDefaultWriterWrite$]([=this=], |chunk|).
4466+
1. Return ! [$WritableStreamDefaultWriterWrite$]([=this=], |chunk|, |transferList|).
44384467
</div>
44394468

44404469
<h3 id="ws-default-controller-class">The {{WritableStreamDefaultController}} class</h3>
@@ -4505,6 +4534,10 @@ the following table:
45054534
<td><dfn>\[[writeAlgorithm]]</dfn>
45064535
<td class="non-normative">A promise-returning algorithm, taking one argument (the [=chunk=] to
45074536
write), which writes data to the [=underlying sink=]
4537+
<tr>
4538+
<td><dfn>\[[isOwning]]</dfn>
4539+
<td class="non-normative">A boolean flag indicating whether to take ownership of enqueued chunks
4540+
via transfer or serialization.
45084541
</table>
45094542

45104543
The <dfn>close sentinel</dfn> is a unique value enqueued into
@@ -5069,7 +5102,7 @@ The following abstract operations support the implementation and manipulation of
50695102

50705103
<div algorithm>
50715104
<dfn abstract-op lt="WritableStreamDefaultWriterWrite"
5072-
id="writable-stream-default-writer-write">WritableStreamDefaultWriterWrite(|writer|, |chunk|)</dfn>
5105+
id="writable-stream-default-writer-write">WritableStreamDefaultWriterWrite(|writer|, |chunk|, |transferList|)</dfn>
50735106
performs the following steps:
50745107

50755108
1. Let |stream| be |writer|.[=WritableStreamDefaultWriter/[[stream]]=].
@@ -5088,7 +5121,7 @@ The following abstract operations support the implementation and manipulation of
50885121
|stream|.[=WritableStream/[[storedError]]=].
50895122
1. Assert: |state| is "`writable`".
50905123
1. Let |promise| be ! [$WritableStreamAddWriteRequest$](|stream|).
5091-
1. Perform ! [$WritableStreamDefaultControllerWrite$](|controller|, |chunk|, |chunkSize|).
5124+
1. Perform ! [$WritableStreamDefaultControllerWrite$](|controller|, |chunk|, |chunkSize|, |transferList|).
50925125
1. Return |promise|.
50935126
</div>
50945127

@@ -5102,7 +5135,7 @@ The following abstract operations support the implementation of the
51025135
<dfn abstract-op lt="SetUpWritableStreamDefaultController"
51035136
id="set-up-writable-stream-default-controller">SetUpWritableStreamDefaultController(|stream|,
51045137
|controller|, |startAlgorithm|, |writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|,
5105-
|highWaterMark|, |sizeAlgorithm|)</dfn> performs the following steps:
5138+
|highWaterMark|, |sizeAlgorithm|, |isOwning|)</dfn> performs the following steps:
51065139

51075140
1. Assert: |stream| [=implements=] {{WritableStream}}.
51085141
1. Assert: |stream|.[=WritableStream/[[controller]]=] is undefined.
@@ -5113,6 +5146,7 @@ The following abstract operations support the implementation of the
51135146
1. Set |controller|.[=WritableStreamDefaultController/[[started]]=] to false.
51145147
1. Set |controller|.[=WritableStreamDefaultController/[[strategySizeAlgorithm]]=] to
51155148
|sizeAlgorithm|.
5149+
1. Set |controller|.[=WritableStreamDefaultController/[[isOwning]]=] to |isOwning|.
51165150
1. Set |controller|.[=WritableStreamDefaultController/[[strategyHWM]]=] to |highWaterMark|.
51175151
1. Set |controller|.[=WritableStreamDefaultController/[[writeAlgorithm]]=] to |writeAlgorithm|.
51185152
1. Set |controller|.[=WritableStreamDefaultController/[[closeAlgorithm]]=] to |closeAlgorithm|.
@@ -5142,6 +5176,8 @@ The following abstract operations support the implementation of the
51425176
1. Let |writeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
51435177
1. Let |closeAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
51445178
1. Let |abortAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
5179+
1. Let |isOwning| be true if |underlyingSinkDict|["{{UnderlyingSink/type}}"] is
5180+
"{{WritableStreamType/owning}}" and false otherwise.
51455181
1. If |underlyingSinkDict|["{{UnderlyingSink/start}}"] [=map/exists=], then set |startAlgorithm| to
51465182
an algorithm which returns the result of [=invoking=]
51475183
|underlyingSinkDict|["{{UnderlyingSink/start}}"] with argument list «&nbsp;|controller|&nbsp;»
@@ -5159,7 +5195,7 @@ The following abstract operations support the implementation of the
51595195
|underlyingSinkDict|["{{UnderlyingSink/abort}}"] with argument list «&nbsp;|reason|&nbsp;» and
51605196
[=callback this value=] |underlyingSink|.
51615197
1. Perform ? [$SetUpWritableStreamDefaultController$](|stream|, |controller|, |startAlgorithm|,
5162-
|writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|).
5198+
|writeAlgorithm|, |closeAlgorithm|, |abortAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isOwning|).
51635199
</div>
51645200

51655201
<div algorithm>
@@ -5313,9 +5349,9 @@ The following abstract operations support the implementation of the
53135349
<div algorithm>
53145350
<dfn abstract-op lt="WritableStreamDefaultControllerWrite"
53155351
id="writable-stream-default-controller-write">WritableStreamDefaultControllerWrite(|controller|,
5316-
|chunk|, |chunkSize|)</dfn> performs the following steps:
5352+
|chunk|, |chunkSize|, |transferList|)</dfn> performs the following steps:
53175353

5318-
1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, undefined).
5354+
1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, |transferList|).
53195355
1. If |enqueueResult| is an abrupt completion,
53205356
1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$](|controller|,
53215357
|enqueueResult|.\[[Value]]).

0 commit comments

Comments
 (0)