Skip to content

Commit 6c02df2

Browse files
authored
Add AbortSignal.any()
- This implements an optimization that puts all children on non-dependent signals (i.e., those associated with a controller). This allows "intermediate" nodes (e.g., B in A follows B follows C) to be garbage collected if they are being kept alive to propagate aborts. - This removes the follow algorithm, so callsites will need to be updated. - The "create a composite abort signal" algorithm takes an interface so that TaskSignal.any() can easily hook into it, but create a TaskSignal. - Some algorithms that invoke "follow" create an AbortSignal in a particular realm. This enables doing that, but borrows some language from elsewhere in the spec w.r.t. doing the default thing. Neither of the other two static members specify a realm. Follow-up PRs: - whatwg/fetch#1646 - w3c/ServiceWorker#1678 - whatwg/streams#1277 This also sets the stage to make AbortSignal's "signal abort" fully internal. #1194 tracks the remainder. Tests: web-platform-tests/wpt#37434 and web-platform-tests/wpt#39785. Fixes #920.
1 parent fb0bf26 commit 6c02df2

File tree

1 file changed

+129
-38
lines changed

1 file changed

+129
-38
lines changed

dom.bs

Lines changed: 129 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type: interface
3030
urlPrefix: https://tc39.es/ecma262/#; spec: ECMASCRIPT
3131
text: Construct; url: sec-construct; type: abstract-op
3232
type: dfn
33+
text: current realm; url: current-realm
3334
text: realm; url: realm
3435
text: surrounding agent; url: surrounding-agent
3536
urlPrefix: https://w3c.github.io/hr-time/#; spec: HR-TIME
@@ -1768,6 +1769,7 @@ interface AbortController {
17681769
<p>An {{AbortController}} object has an associated <dfn for=AbortController>signal</dfn> (an
17691770
{{AbortSignal}} object).
17701771

1772+
<div algorithm>
17711773
<p>The
17721774
<dfn constructor for=AbortController lt="AbortController()"><code>new AbortController()</code></dfn>
17731775
constructor steps are:
@@ -1777,13 +1779,22 @@ constructor steps are:
17771779

17781780
<li><p>Set <a>this</a>'s <a for=AbortController>signal</a> to <var>signal</var>.
17791781
</ol>
1782+
</div>
17801783

17811784
<p>The <dfn attribute for=AbortController><code>signal</code></dfn> getter steps are to return
17821785
<a>this</a>'s <a for=AbortController>signal</a>.
17831786

1787+
<div algorithm>
17841788
<p>The <dfn method for=AbortController><code>abort(<var>reason</var>)</code></dfn> method steps are
1785-
to <a for=AbortSignal>signal abort</a> on <a>this</a>'s <a for=AbortController>signal</a> with
1786-
<var>reason</var> if it is given.
1789+
to <a for=AbortController>signal abort</a> on <a>this</a> with <var>reason</var> if it is given.
1790+
</div>
1791+
1792+
<div algorithm>
1793+
<p>To <dfn export for=AbortController>signal abort</dfn> on an {{AbortController}}
1794+
<var>controller</var> with an optional <var>reason</var>, <a for=AbortSignal>signal abort</a> on
1795+
<var>controller</var>'s <a for=AbortController>signal</a> with <var>reason</var> if it is given.
1796+
</div>
1797+
17871798

17881799
<h3 id=interface-AbortSignal>Interface {{AbortSignal}}</h3>
17891800

@@ -1792,6 +1803,7 @@ to <a for=AbortSignal>signal abort</a> on <a>this</a>'s <a for=AbortController>s
17921803
interface AbortSignal : EventTarget {
17931804
[NewObject] static AbortSignal abort(optional any reason);
17941805
[Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
1806+
[NewObject] static AbortSignal _any(sequence&lt;AbortSignal> signals);
17951807

17961808
readonly attribute boolean aborted;
17971809
readonly attribute any reason;
@@ -1805,6 +1817,11 @@ interface AbortSignal : EventTarget {
18051817
<dd>Returns an {{AbortSignal}} instance whose <a for=AbortSignal>abort reason</a> is set to
18061818
<var>reason</var> if not undefined; otherwise to an "{{AbortError!!exception}}" {{DOMException}}.
18071819

1820+
<dt><code>AbortSignal . <a method for=AbortSignal lt=any(signals)>any</a>(<var>signals</var>)</code>
1821+
<dd>Returns an {{AbortSignal}} instance which will be aborted once any of <var>signals</var> is
1822+
aborted. Its <a for=AbortSignal>abort reason</a> will be set to whichever one of <var>signals</var>
1823+
caused it to be aborted.
1824+
18081825
<dt><code>AbortSignal . <a method for=AbortSignal lt=timeout(milliseconds)>timeout</a>(<var>milliseconds</var>)</code>
18091826
<dd>Returns an {{AbortSignal}} instance which will be aborted in <var>milliseconds</var>
18101827
milliseconds. Its <a for=AbortSignal>abort reason</a> will be set to a
@@ -1821,35 +1838,32 @@ interface AbortSignal : EventTarget {
18211838
{{AbortController}} has signaled to abort; otherwise, does nothing.
18221839
</dl>
18231840

1824-
<p>An {{AbortSignal}} object has an associated <dfn export for=AbortSignal>abort reason</dfn>, which is a
1825-
JavaScript value. It is undefined unless specified otherwise.
1826-
1827-
<p>An {{AbortSignal}} object is <dfn export for="AbortSignal">aborted</dfn> when its
1828-
[=AbortSignal/abort reason=] is not undefined.
1829-
1830-
<p>An {{AbortSignal}} object has associated <dfn for=AbortSignal>abort algorithms</dfn>, which is a
1831-
<a for=/>set</a> of algorithms which are to be executed when it is [=AbortSignal/aborted=]. Unless
1832-
specified otherwise, its value is the empty set.
1833-
1834-
<p>To <dfn export for=AbortSignal>add</dfn> an algorithm <var>algorithm</var> to an {{AbortSignal}}
1835-
object <var>signal</var>, run these steps:
1836-
1837-
<ol>
1838-
<li><p>If <var>signal</var> is [=AbortSignal/aborted=], then return.
1839-
1840-
<li><p><a for=set>Append</a> <var>algorithm</var> to <var>signal</var>'s
1841-
<a for=AbortSignal>abort algorithms</a>.
1842-
</ol>
1841+
<p>An {{AbortSignal}} object has an associated <dfn export for=AbortSignal>abort reason</dfn> (a
1842+
JavaScript value), which is initially undefined.
18431843

1844-
<p>To <dfn export for=AbortSignal>remove</dfn> an algorithm <var>algorithm</var> from an
1845-
{{AbortSignal}} <var>signal</var>, <a for=set>remove</a> <var>algorithm</var> from
1846-
<var>signal</var>'s <a for=AbortSignal>abort algorithms</a>.
1844+
<p>An {{AbortSignal}} object has associated <dfn for=AbortSignal>abort algorithms</dfn>, (a
1845+
<a for=/>set</a> of algorithms which are to be executed when it is [=AbortSignal/aborted=]),
1846+
which is initially empty.
18471847

18481848
<p class=note>The [=AbortSignal/abort algorithms=] enable APIs with complex
18491849
requirements to react in a reasonable way to {{AbortController/abort()}}. For example, a given API's
18501850
[=AbortSignal/abort reason=] might need to be propagated to a cross-thread environment, such as a
18511851
service worker.
18521852

1853+
<p>An {{AbortSignal}} object has a <dfn for="AbortSignal">dependent</dfn> (a boolean), which is
1854+
initially false.
1855+
1856+
<p>An {{AbortSignal}} object has associated <dfn for=AbortSignal>source signals</dfn> (a weak
1857+
<a for=/>set</a> of {{AbortSignal}} objects that the object is dependent on for its
1858+
[=AbortSignal/aborted=] state), which is initially empty.
1859+
1860+
<p>An {{AbortSignal}} object has associated <dfn for=AbortSignal>dependent signals</dfn> (a weak
1861+
<a for=/>set</a> of {{AbortSignal}} objects that are dependent on the object for their
1862+
[=AbortSignal/aborted=] state), which is initially empty.
1863+
1864+
<hr>
1865+
1866+
<div algorithm>
18531867
<p>The static <dfn method for=AbortSignal><code>abort(<var>reason</var>)</code></dfn> method steps
18541868
are:
18551869

@@ -1861,7 +1875,9 @@ are:
18611875

18621876
<li>Return <var>signal</var>.
18631877
</ol>
1878+
</div>
18641879

1880+
<div algorithm>
18651881
<p>The static <dfn method for=AbortSignal><code>timeout(<var>milliseconds</var>)</code></dfn> method
18661882
steps are:
18671883

@@ -1886,6 +1902,13 @@ steps are:
18861902

18871903
<li><p>Return <var>signal</var>.
18881904
</ol>
1905+
</div>
1906+
1907+
<div algorithm>
1908+
<p>The static <dfn method for=AbortSignal><code>any(<var>signals</var>)</code></dfn> method
1909+
steps are to return the result of <a>creating a dependent abort signal</a> from <var>signals</var>
1910+
using {{AbortSignal}} and the <a>current realm</a>.
1911+
</div>
18891912

18901913
<p>The <dfn attribute for=AbortSignal>aborted</dfn> getter steps are to return true if <a>this</a>
18911914
is [=AbortSignal/aborted=]; otherwise false.
@@ -1924,46 +1947,114 @@ is [=AbortSignal/aborted=]; otherwise false.
19241947
<dfn event for=AbortSignal><code>abort</code></dfn>.
19251948

19261949
<p class=note>Changes to an {{AbortSignal}} object represent the wishes of the corresponding
1927-
{{AbortController}} object, but an API observing the {{AbortSignal}} object can chose to ignore
1950+
{{AbortController}} object, but an API observing the {{AbortSignal}} object can choose to ignore
19281951
them. For instance, if the operation has already completed.
19291952

1953+
<hr>
1954+
1955+
<p>An {{AbortSignal}} object is <dfn export for="AbortSignal">aborted</dfn> when its
1956+
[=AbortSignal/abort reason=] is not undefined.
1957+
1958+
<div algorithm>
1959+
<p>To <dfn export for=AbortSignal>add</dfn> an algorithm <var>algorithm</var> to an {{AbortSignal}}
1960+
object <var>signal</var>:
1961+
1962+
<ol>
1963+
<li><p>If <var>signal</var> is [=AbortSignal/aborted=], then return.
1964+
1965+
<li><p><a for=set>Append</a> <var>algorithm</var> to <var>signal</var>'s
1966+
<a for=AbortSignal>abort algorithms</a>.
1967+
</ol>
1968+
</div>
1969+
1970+
<div algorithm>
1971+
<p>To <dfn export for=AbortSignal>remove</dfn> an algorithm <var>algorithm</var> from an
1972+
{{AbortSignal}} <var>signal</var>, <a for=set>remove</a> <var>algorithm</var> from
1973+
<var>signal</var>'s <a for=AbortSignal>abort algorithms</a>.
1974+
</div>
1975+
1976+
<div algorithm>
19301977
<p>To <dfn export for=AbortSignal>signal abort</dfn>, given an {{AbortSignal}} object
1931-
<var>signal</var> and an optional <var>reason</var>, run these steps:
1978+
<var>signal</var> and an optional <var>reason</var>:
19321979

19331980
<ol>
19341981
<li><p>If <var>signal</var> is [=AbortSignal/aborted=], then return.
19351982

19361983
<li><p>Set <var>signal</var>'s [=AbortSignal/abort reason=] to <var>reason</var> if it is given;
19371984
otherwise to a new "{{AbortError!!exception}}" {{DOMException}}.
19381985

1939-
<li><p><a for=set>For each</a> <var>algorithm</var> in <var>signal</var>'s
1986+
<li><p><a for=set>For each</a> <var>algorithm</var> of <var>signal</var>'s
19401987
[=AbortSignal/abort algorithms=]: run <var>algorithm</var>.
19411988

19421989
<li><p><a for=set>Empty</a> <var>signal</var>'s <a for=AbortSignal>abort algorithms</a>.
19431990

19441991
<li><p>[=Fire an event=] named {{AbortSignal/abort}} at <var>signal</var>.
1992+
1993+
<li><p><a for=set>For each</a> <var>dependentSignal</var> of <var>signal</var>'s
1994+
[=AbortSignal/dependent signals=], [=AbortSignal/signal abort=] on <var>dependentSignal</var> with
1995+
<var>signal</var>'s [=AbortSignal/abort reason=].
19451996
</ol>
1997+
</div>
19461998

1947-
<p>A <var>followingSignal</var> (an {{AbortSignal}}) is made to
1948-
<dfn export for=AbortSignal>follow</dfn> a <var>parentSignal</var> (an {{AbortSignal}}) by running
1949-
these steps:
1999+
<div algorithm>
2000+
<p>To <dfn export>create a dependent abort signal</dfn> from a list of {{AbortSignal}} objects
2001+
<var>signals</var>, using <var>signalInterface</var>, which must be either {{AbortSignal}} or an
2002+
interface that inherits from it, and a <var>realm</var>:
19502003

19512004
<ol>
1952-
<li><p>If <var>followingSignal</var> is [=AbortSignal/aborted=], then return.
2005+
<li><p>Let <var>resultSignal</var> be a <a for=/>new</a> object implementing
2006+
<var>signalInterface</var> using <var>realm</var>.
19532007

1954-
<li><p>If <var>parentSignal</var> is [=AbortSignal/aborted=], then
1955-
<a for=AbortSignal>signal abort</a> on <var>followingSignal</var> with <var>parentSignal</var>'s
1956-
[=AbortSignal/abort reason=].
2008+
<li><p><a for=list>For each</a> <var>signal</var> of <var>signals</var>: if <var>signal</var> is
2009+
[=AbortSignal/aborted=], then set <var>resultSignal</var>'s [=AbortSignal/abort reason=] to
2010+
<var>signal</var>'s [=AbortSignal/abort reason=] and return <var>resultSignal</var>.
2011+
2012+
<li><p>Set <var>resultSignal</var>'s [=AbortSignal/dependent=] to true.
19572013

19582014
<li>
1959-
<p>Otherwise, <a for=AbortSignal lt=add>add the following abort steps</a> to
1960-
<var>parentSignal</var>:
2015+
<p><a for=list>For each</a> <var>signal</var> of <var>signals</var>:
19612016

19622017
<ol>
1963-
<li><p><a for=AbortSignal>Signal abort</a> on <var>followingSignal</var> with
1964-
<var>parentSignal</var>'s [=AbortSignal/abort reason=].
2018+
<li>
2019+
<p>If <var>signal</var>'s [=AbortSignal/dependent=] is false, then:
2020+
2021+
<ol>
2022+
<li><p><a for=set>Append</a> <var>signal</var> to <var>resultSignal</var>'s
2023+
[=AbortSignal/source signals=].
2024+
2025+
<li><p><a for=set>Append</a> <var>resultSignal</var> to <var>signal</var>'s
2026+
[=AbortSignal/dependent signals=].
2027+
</ol>
2028+
2029+
<li>
2030+
<p>Otherwise, <a for=list>for each</a> <var>sourceSignal</var> of <var>signal</var>'s
2031+
[=AbortSignal/source signals=]:
2032+
2033+
<ol>
2034+
<li><p>Assert: <var>sourceSignal</var> is not [=AbortSignal/aborted=] and not
2035+
[=AbortSignal/dependent=].
2036+
2037+
<li><p>If <var>resultSignal</var>'s [=AbortSignal/source signals=] <a for=set>contains</a>
2038+
<var>sourceSignal</var>, then <a for=iteration>continue</a>.
2039+
2040+
<li><p><a for=set>Append</a> <var>sourceSignal</var> to <var>resultSignal</var>'s
2041+
[=AbortSignal/source signals=].
2042+
2043+
<li><p><a for=set>Append</a> <var>resultSignal</var> to <var>sourceSignal</var>'s
2044+
[=AbortSignal/dependent signals=].
2045+
</ol>
19652046
</ol>
2047+
2048+
<li><p>Return <var>resultSignal</var>.
19662049
</ol>
2050+
</div>
2051+
2052+
2053+
<h4 id=abort-signal-garbage-collection>Garbage collection</h4>
2054+
2055+
<p>A non-[=AbortSignal/aborted=] [=AbortSignal/dependent=] {{AbortSignal}} object must not be
2056+
garbage collected while its [=AbortSignal/source signals=] is non-empty and it has registered event
2057+
listeners for its {{AbortSignal/abort}} event or its [=AbortSignal/abort algorithms=] is non-empty.
19672058

19682059

19692060
<h3 id=abortcontroller-api-integration>Using {{AbortController}} and {{AbortSignal}} objects in

0 commit comments

Comments
 (0)