Skip to content

Commit 644910f

Browse files
committed
Add support for a new ReadableStream "transfer" type.
1 parent 2942e89 commit 644910f

File tree

1 file changed

+57
-15
lines changed

1 file changed

+57
-15
lines changed

index.bs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ spec:infra; type:dfn; text:list
1919
spec:html; type:dfn; text:entangle
2020
spec:html; type:dfn; text:message port post message steps
2121
spec:html; type:dfn; text:port message queue
22+
spec:html; type:dfn; text:transferable objects
2223
</pre>
2324

2425
<pre class="anchors">
@@ -571,7 +572,7 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle
571572
callback UnderlyingSourcePullCallback = Promise<undefined> (ReadableStreamController controller);
572573
callback UnderlyingSourceCancelCallback = Promise<undefined> (optional any reason);
573574

574-
enum ReadableStreamType { "bytes" };
575+
enum ReadableStreamType { "bytes", "transfer" };
575576
</xmp>
576577

577578
<dl>
@@ -639,8 +640,7 @@ enum ReadableStreamType { "bytes" };
639640
something more persistently wrong.
640641
</div>
641642

642-
<dt><dfn dict-member for="UnderlyingSource" lt="type"><code>type</code></dfn> (byte streams
643-
only)</dt>
643+
<dt><dfn dict-member for="UnderlyingSource" lt="type"><code>type</code></dfn></dt>
644644
<dd>
645645
<p>Can be set to "<dfn enum-value for="ReadableStreamType">bytes</dfn>" to signal that the
646646
constructed {{ReadableStream}} is a <a>readable byte stream</a>. This ensures that the resulting
@@ -651,6 +651,14 @@ enum ReadableStreamType { "bytes" };
651651
<p>For an example of how to set up a readable byte stream, including using the different
652652
controller interface, see [[#example-rbs-push]].
653653

654+
<p>Can be set to "<dfn enum-value for="ReadableStreamType">transfer</dfn>" to signal that the
655+
constructed {{ReadableStream}} will transfer or serialize chunks before enqueuing them.
656+
This ensures that enqueued chunks are not mutable by the source.
657+
Transferred or serialized chunks may have <dfn>closing steps</dfn> which are executed if
658+
enqueued chunks are dequeued without being provided to the application, for instance when
659+
a {{ReadableStream}} is errored.
660+
</p>
661+
654662
<p>Setting any value other than "{{ReadableStreamType/bytes}}" or undefined will cause the
655663
{{ReadableStream()}} constructor to throw an exception.
656664

@@ -801,7 +809,8 @@ option. If {{UnderlyingSource/type}} is set to undefined (including via omission
801809
1. Perform ? [$SetUpReadableByteStreamControllerFromUnderlyingSource$]([=this=],
802810
|underlyingSource|, |underlyingSourceDict|, |highWaterMark|).
803811
1. Otherwise,
804-
1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=].
812+
1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=] or
813+
is "{{ReadableStreamType/transfer}}".
805814
1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$](|strategy|).
806815
1. Let |highWaterMark| be ? [$ExtractHighWaterMark$](|strategy|, 1).
807816
1. Perform ? [$SetUpReadableStreamDefaultControllerFromUnderlyingSource$]([=this=],
@@ -1523,6 +1532,9 @@ the following table:
15231532
<tr>
15241533
<td><dfn>\[[stream]]</dfn>
15251534
<td class="non-normative">The {{ReadableStream}} instance controlled
1535+
<tr>
1536+
<td><dfn>\[[isTransferring]]</dfn>
1537+
<td class="non-normative">A boolean flag indicating whether to transfer enqueued chunks
15261538
</table>
15271539

15281540
<h4 id="rs-default-controller-prototype">Methods and properties</h4>
@@ -2235,8 +2247,8 @@ create them does not matter.
22352247
objects|transferring=] their [=chunks=]. However, it does introduce a noticeable asymmetry between
22362248
the two branches, and limits the possible [=chunks=] to serializable ones. [[!HTML]]
22372249

2238-
If |stream| is a [=readable byte stream=], then |cloneForBranch2| is ignored and chunks are cloned
2239-
unconditionally.
2250+
If |stream| is a [=readable byte stream=], or if |stream| type is "{{ReadableStreamType/transfer}}",
2251+
then |cloneForBranch2| is ignored and chunks are cloned unconditionally.
22402252

22412253
<p class="note">In this standard ReadableStreamTee is always called with |cloneForBranch2| set to
22422254
false; other specifications pass true via the [=ReadableStream/tee=] wrapper algorithm.
@@ -2247,6 +2259,8 @@ create them does not matter.
22472259
1. Assert: |cloneForBranch2| is a boolean.
22482260
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
22492261
return ? [$ReadableByteStreamTee$](|stream|).
2262+
1. If |stream|.[=ReadableStream/[[controller]]=].[=ReadableStreamDefaultController/[[isTransferring]]=]
2263+
is true, return ? [$ReadableStreamDefaultTee$](|stream|, true).
22502264
1. Return ? [$ReadableStreamDefaultTee$](|stream|, |cloneForBranch2|).
22512265
</div>
22522266

@@ -2955,8 +2969,12 @@ The following abstract operations support the implementation of the
29552969
1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|controller|) is false, return.
29562970
1. Let |stream| be |controller|.[=ReadableStreamDefaultController/[[stream]]=].
29572971
1. If ! [$IsReadableStreamLocked$](|stream|) is true and !
2958-
[$ReadableStreamGetNumReadRequests$](|stream|) > 0, perform !
2959-
[$ReadableStreamFulfillReadRequest$](|stream|, |chunk|, false).
2972+
[$ReadableStreamGetNumReadRequests$](|stream|) > 0, perform the following steps:
2973+
1. Let |internalChunk| be |chunk|.
2974+
1. If |controller|.[=ReadableStreamDefaultController/[[isTransferring]]=] is true, perform the following steps:
2975+
1. Set |internalChunk| to [$StructuredTransferOrSerialize$](|chunk|).
2976+
1. If |internalChunk| is an abrupt completion, return.
2977+
1. ![$ReadableStreamFulfillReadRequest$](|stream|, |internalChunk|, false).
29602978
1. Otherwise,
29612979
1. Let |result| be the result of performing
29622980
|controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=], passing in |chunk|,
@@ -3029,7 +3047,7 @@ The following abstract operations support the implementation of the
30293047
<dfn abstract-op lt="SetUpReadableStreamDefaultController"
30303048
id="set-up-readable-stream-default-controller">SetUpReadableStreamDefaultController(|stream|,
30313049
|controller|, |startAlgorithm|, |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|,
3032-
|sizeAlgorithm|)</dfn> performs the following steps:
3050+
|sizeAlgorithm|, |isTransferring|)</dfn> performs the following steps:
30333051

30343052
1. Assert: |stream|.[=ReadableStream/[[controller]]=] is undefined.
30353053
1. Set |controller|.[=ReadableStreamDefaultController/[[stream]]=] to |stream|.
@@ -3039,8 +3057,9 @@ The following abstract operations support the implementation of the
30393057
|controller|.[=ReadableStreamDefaultController/[[pullAgain]]=], and
30403058
|controller|.[=ReadableStreamDefaultController/[[pulling]]=] to false.
30413059
1. Set |controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=] to
3042-
|sizeAlgorithm| and |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to
3043-
|highWaterMark|.
3060+
|sizeAlgorithm|, |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to
3061+
|highWaterMark| and |controller|.[=ReadableStreamDefaultController/[[isTransferring]]=] to
3062+
|isTransferring|.
30443063
1. Set |controller|.[=ReadableStreamDefaultController/[[pullAlgorithm]]=] to |pullAlgorithm|.
30453064
1. Set |controller|.[=ReadableStreamDefaultController/[[cancelAlgorithm]]=] to |cancelAlgorithm|.
30463065
1. Set |stream|.[=ReadableStream/[[controller]]=] to |controller|.
@@ -3065,6 +3084,9 @@ The following abstract operations support the implementation of the
30653084
1. Let |startAlgorithm| be an algorithm that returns undefined.
30663085
1. Let |pullAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
30673086
1. Let |cancelAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
3087+
1. Let |cancelAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined.
3088+
1. Let |isTransferring| be true if |underlyingSourceDict|["{{UnderlyingSource/pull}}"] is
3089+
"{{ReadableStreamType/transfer}}" and false otherwise.
30683090
1. If |underlyingSourceDict|["{{UnderlyingSource/start}}"] [=map/exists=], then set
30693091
|startAlgorithm| to an algorithm which returns the result of [=invoking=]
30703092
|underlyingSourceDict|["{{UnderlyingSource/start}}"] with argument list
@@ -3078,7 +3100,7 @@ The following abstract operations support the implementation of the
30783100
[=invoking=] |underlyingSourceDict|["{{UnderlyingSource/cancel}}"] with argument list
30793101
«&nbsp;|reason|&nbsp;» and [=callback this value=] |underlyingSource|.
30803102
1. Perform ? [$SetUpReadableStreamDefaultController$](|stream|, |controller|, |startAlgorithm|,
3081-
|pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|).
3103+
|pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isTransferring|).
30823104
</div>
30833105

30843106
<h4 id="rbs-controller-abstract-ops">Byte stream controllers</h4>
@@ -6346,8 +6368,8 @@ In what follows, a <dfn>value-with-size</dfn> is a [=struct=] with the two [=str
63466368
for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
63476369

63486370
<div algorithm>
6349-
<dfn abstract-op lt="DequeueValue" id="dequeue-value">DequeueValue(|container|)</dfn> performs the
6350-
following steps:
6371+
<dfn abstract-op lt="DequeueValue" id="dequeue-value">DequeueValue(|container|)</dfn> performs
6372+
the following steps:
63516373

63526374
1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
63536375
1. Assert: |container|.\[[queue]] is not [=list/is empty|empty=].
@@ -6368,7 +6390,11 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
63686390
1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
63696391
1. If ! [$IsNonNegativeNumber$](|size|) is false, throw a {{RangeError}} exception.
63706392
1. If |size| is +∞, throw a {{RangeError}} exception.
6371-
1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |value| and
6393+
1. Let |enqueuedValue| be |value|.
6394+
1. If |container|.\[[isTransferring]] is true, perform the following steps:
6395+
1. Set |enqueuedValue| to [$StructuredTransferOrSerialize$](|value|).
6396+
1. If |enqueuedValue| is an abrupt completion, return.
6397+
1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and
63726398
[=value-with-size/size=] |size| to |container|.\[[queue]].
63736399
1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] + |size|.
63746400
</div>
@@ -6388,6 +6414,10 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
63886414
performs the following steps:
63896415

63906416
1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots.
6417+
1. If |container|.\[[isTransferring]] is true, perform the following steps until |container|.\[[queue]]
6418+
is [=list/is empty|empty=]:
6419+
1. Let |chunk| be ! [$DequeueValue$]([=this=]).
6420+
1. If |chunk| has [=closing steps=], perform the [=closing steps=] given |chunk|.
63916421
1. Set |container|.\[[queue]] to a new empty [=list=].
63926422
1. Set |container|.\[[queueTotalSize]] to 0.
63936423
</div>
@@ -6596,6 +6626,18 @@ The following abstract operations are a grab-bag of utilities.
65966626
1. Return ? [$StructuredDeserialize$](|serialized|, [=the current Realm=]).
65976627
</div>
65986628

6629+
<div algorithm>
6630+
<dfn abstract-op lt="StructuredTransferOrSerialize">StructuredTransferOrSerialize(|value|)</dfn>
6631+
performs the following steps:
6632+
1. If |value| is [=transferable objects|transferable=], perform the following steps:
6633+
// FIXME: We should probably handle other transferable types like ArrayBuffers.
6634+
1. Let |serialized| be ! [$StructuredSerializeWithTransfer$](|value|, « |value| »).
6635+
1. Let |deserialized| be ! [$StructuredDeserializeWithTransfer$](|serialized|,
6636+
[=the current Realm=]).
6637+
1. return |deserialized|.\[[Deserialized]].
6638+
1. return [$StructuredClone$](|value|).
6639+
</div>
6640+
65996641
<h2 id="other-specs">Using streams in other specifications</h2>
66006642

66016643
Much of this standard concerns itself with the internal machinery of streams. Other specifications

0 commit comments

Comments
 (0)