-
Notifications
You must be signed in to change notification settings - Fork 296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AbortSignal.any() #1152
Add AbortSignal.any() #1152
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done a quick editorial take. This would probably benefit from review by @smaug----, @jakearchibald, and @domenic.
dom.bs
Outdated
{{AbortSignal}} objects <var>signals</var> or a single {{AbortSignal}} <var>signal</var>, an | ||
optional <var>signalInterface</var>, which must be either {{AbortSignal}} or an interface that | ||
inherits from it, and an optional <var>realm</var>, run these steps: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. Let's always require a list. Folks can use the list syntax from Infra.
And do we want an interface or some kind of creation algorithm? (Also, if you want it to default to AbortSignal you should specify that here using the language from Infra, but please only do that if there are many callers. Otherwise it's not really worth it.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair - changed to requiring a list.
Regarding interface vs. creation algorithm: The main reason it has this form is to ensure the signal is newly created (i.e. not associated with a controller), since that's an assumption in the algorithm. The language ~= that of "create an event". There's another alternative where the exported algorithm just takes an optional realm and delegates to a private algorithm that takes a signal (to also be used by TaskSignal), but that's probably overkill?
I removed the default value since this likely won't have many callers callers (users of follow (i.e. fetch) and the TaskSignal specialization).
Thanks for taking a first pass, @annevk; comments addressed. |
@shaseley can you provide a bit of detail on why the spec needs to make the garbage collection more explicit than other specs? Is it observable in some way? My understanding (please correct me if I'm wrong @annevk) is that spec algorithms should be written as simply as possible, and UAs are invited to implement them in a more complex way that makes them work faster / more efficient as long as the observable behaviour is the same. |
Well, although we don't always do a good job at this, we do need to describe relationships between objects in a way that allows GC to eventually succeed. And I don't think we should accept "agent lifetime" as a way out, although sometimes that is acceptable. I hadn't looked at the GC section here yet, I was hoping @smaug---- could take a look at that. But now that I have:
(I hope that at some point JS/IDL formalize some of these aspects better so we can ground terminology like "weak reference" in terms of a larger web platform memory model.) |
Fair enough. I guess it ensures we don't later break it in a way that prevents this particular kind of GC. |
Yeah and it also helps if we all think a bit about memory usage and leaks. (E.g., why you need to explicitly clone a response to read it twice, as I'm sure you're well aware.) |
Yeah, I wasn't sure if this section actually belonged or not, but there is precedent in the DOM spec here. I included this because GC/memory management of composite signals was a concern in the related issue, so I wanted to include it to hopefully help guide other implementors. FYI: there's one design decision related to the linking algorithm that helps support memory management and is observable: AbortSignal.any() always links the resulting signal to a non-composite signal (timeout or associated with a controller). This basically flattens the signal graph to a depth of 1, meaning intermediate composite signals don't need to be kept alive just to propagate abort. This affects the order events fire, which is covered in tests.
FWIW I was using https://dom.spec.whatwg.org/#garbage-collection as an example, which should probably also get cleaned up then?:
Thanks, I'll have a look at that and clean this up.
|
2af42a9
to
800cfa2
Compare
Ok, I updated the GC section based on @annevk's suggestions. The idea is that source signals can store dependent signals weakly as long as dependent signals are kept alive as outlined in the GC section. Doing so prevents long-lived source signals (e.g. application-level signal) from holding onto composite signals indefinitely. |
dom.bs
Outdated
these steps: | ||
<p>To <dfn export>create a composite abort signal</dfn> from a list of {{AbortSignal}} objects | ||
<var>signals</var>, using <var>signalInterface</var>, which must be either {{AbortSignal}} or an | ||
interface that inherits from it, and an optional <var>realm</var>, run these steps: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it rather useless to say "or an interface that inherits from it"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is here is to allow subclasses to invoke this as an internal constructor, since they may need to specialize the behavior (if this is premature, I can remove and monkeypatch for TaskSignal
). I'm borrowing the language from event creation, trying to keep this locally consistent.
Can you recommend alternative wording? To me, "which must be an {{AbortSignal}}" implies an AbortSignal object and "which must be {{AbortSignal}}" implies subclasses are excluded since it names it specifically. I didn't find anything in infra about inheritance wording, which is one reason I looked for something locally in the DOM spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“object implementing X” is defined by Web IDL and includes objects implementing Y if Y extends X.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But here the parameter is the interface itself and not a platform object, so I don't think that can be used (the definition requires an object)? The interface parameter is used below to create an object that implements it. (That the parameter is an interface and not object is why I don't think the phrasing is useless -- but it would be if it was an object.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, got it, I read that too quickly. That makes sense to me.
dom.bs
Outdated
<li><p>If <var>followingSignal</var> is [=AbortSignal/aborted=], then return. | ||
<li><p>Let <var>resultSignal</var> be a <a for=/>new</a> object implementing | ||
<var>signalInterface</var> using <var>realm</var> if given, otherwise using the default behavior | ||
defined in Web IDL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we say something like "defined in Foo", I'd expect that to be then link to the definition. Especially since this is doing something unusual with realms, the default behavior should be clear too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I copied the language from event creation, which does something similar, but I didn't copy the note w/ related Web IDL issue. I added the same note; does this address the concern?
FYI the reason this takes an optional realm is because new Request()
in the fetch spec (step 29) creates a new signal in a specific realm, but in this spec we just create new signals without specifying a realm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, in general realm-of-creation is a giant mess across all web platform standards. We've had a few attempts at trying to clean that up, but none successful thus far. There's a Web IDL issue open on defining some kind of implicit realm that we could use in most cases, but it hasn't been worked on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reviewing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normatively LGTM, a few suggestions for making things a bit cleaner.
dom.bs
Outdated
|
||
<p>An {{AbortSignal}} object has associated <dfn for=AbortSignal>dependent signals</dfn>, which is a | ||
<a for=/>set</a> of weak references to {{AbortSignal}} objects that are dependent on it for their | ||
[=AbortSignal/aborted=] state. Unless specified otherwise, its value is the empty set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these should be "weak sets" instead of "sets of weak references". The difference is not behaviorally significant, but I think you'd need more verbiage to rigorously manage a set of weak references. E.g., instead of "Append sourceSignal to ..." you'd need "Append a weak reference to sourceSignal to...". And you'd need to dereference and null-check and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good catch, thanks, "weak set" semantics are indeed what I want (I still had Blink/Oilpan in mind where references/collection weakness happens automatically based on type).
Similarly, Mutation Observer's node list should probably be a "weak list". It does add weak references to nodes, but the values aren't null-checked on iteration (I missed this in review, thinking a list of weak references would behave like a weak list).
Some clarification in Infra could be useful here, e.g. define weak sets & lists, weak references, their relationship, etc. I'll file an issue.
This adds IDL and algorithms for AbortSignal.any(). Details: - This implements an optimization that puts all children on non-composite 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 - This makes "signal abort" private. This is done so we can make assumptions about when a signal can no longer abort. The few places that directly signal abort will be converted to use an AbortController (this also exposes an algorithm to "request abort" on a controller for this). - 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 AbortSignal factory APIs specify a realm.
050b2ea
to
67144aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Domenic!
AbortSignal's "follow" algorithm is replaced with a new algorithm for creating a dependent abort signal ([1], [2]), which is implemented as a new AbortSignal constructor. This CL replaces Follow with the new ctor, behind the AbortSignalAny REF. There is a slight compat risk with the change (when the feature ships): 1. fetch: abort is propagated to dependent signals after abort event dispatch for a signal, whereas "follow" propagated abort before (as abort algorithms), which is observable. We could alternatively use weak abort algorithms in AbortSignal.any(), but that would be more complicated and partially limit GC optimizations. 2. cache: The use of "follow" in cache.addAll was not specced and could lead to abort events firing when they shouldn't have. The new behavior preserves the specced behavior (aborting outstanding requests on failure) without dispatching events when abort is triggered internally. Note: the virtual test expectations match the current expectations except for the test modified in fetch/api/abort/general.any.js. [1] whatwg/dom#1152 [2] whatwg/fetch#1646 Bug: 1323391 Low-Coverage-Reason: the uncovered lines in request.cc are covered by WPT tests Change-Id: I5c25048fcf9f8db759e2f0181ac338c8b603b451 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4515276 Reviewed-by: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Commit-Queue: Scott Haseley <shaseley@chromium.org> Reviewed-by: Ben Kelly <wanderview@chromium.org> Cr-Commit-Position: refs/heads/main@{#1144953}
|
||
<p class=note>The [=AbortSignal/abort algorithms=] enable APIs with complex | ||
requirements to react in a reasonable way to {{AbortController/abort()}}. For example, a given API's | ||
[=AbortSignal/abort reason=] might need to be propagated to a cross-thread environment, such as a | ||
service worker. | ||
|
||
<p>An {{AbortSignal}} object has a <dfn for="AbortSignal">dependent</dfn> (a boolean), which is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need this flag? Why can't we rely on source signals
being non-empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference has to do with empty signals and GC eligibility.
const signal = AbortSignal.any([]);
…
const signal2 = AbortSignal.any([signal]);
signal
is an "empty dependent signal", and such signals can never abort. When creating signal2
, the "create a dependent signal" algorithm will not add signal
as a source signal because its sources are empty; if we remove the "dependent" flag, it will. It's a minor difference that won't affect correctness, but it could affect GC eligibility.
[=AbortSignal/source signals=]: | ||
|
||
<ol> | ||
<li><p>Assert: <var>sourceSignal</var> is not [=AbortSignal/aborted=] and not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear to me what ensure that the second part of the assertion holds. How do we know for sure that AbortSignal.any() cannot be called with an AbortSignal parameter that has it's dependent
flag set?
Maybe we should recursively go through source signals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be called with dependent signals. When it encounters one in the outer for-loop, it iterates over the dependent signal's source signals and links those. And by always linking to a dependent's source signals, dependent signals are never added as sources. So this is saying "a dependent signal's source signal cannot be a dependent signal."
The idea is something like this:
const root = someController.signal;
// Non-dependent signal passed, simple case (append it directly):
// root.__dependent_signals = [signal1];
// signal1.__source_signals = [root];
const signal1 = AbortSignal.any([someController.signal]);
// Dependent signal passed, iterate over its source signals and link those:
// root.__dependent_signals = [signal1, signal2];
// signal2.__source_signals = [root]; (note: root is linked, not signal1).
const signal2 = AbortSignal.any([signal1]);
// Same as above:
// root.__dependent_signals = [signal1, signal2, signal3];
// signal3.__source_signals = [root];
const signal3 = AbortSignal.any([signal2]);
This is by design to "flatten" the signal dependency graph by linking directly to signals that can abort directly (timeouts or controller signals), which helps optimize GC since intermediates aren't kept alive to propagate abort. (Note also that new sources are fixed at construction time).
AbortSignal's "follow" algorithm is replaced with a new algorithm for creating a dependent abort signal ([1], [2]), which is implemented as a new AbortSignal constructor. This CL replaces Follow with the new ctor, behind the AbortSignalAny REF. There is a slight compat risk with the change (when the feature ships): 1. fetch: abort is propagated to dependent signals after abort event dispatch for a signal, whereas "follow" propagated abort before (as abort algorithms), which is observable. We could alternatively use weak abort algorithms in AbortSignal.any(), but that would be more complicated and partially limit GC optimizations. 2. cache: The use of "follow" in cache.addAll was not specced and could lead to abort events firing when they shouldn't have. The new behavior preserves the specced behavior (aborting outstanding requests on failure) without dispatching events when abort is triggered internally. Note: the virtual test expectations match the current expectations except for the test modified in fetch/api/abort/general.any.js. [1] whatwg/dom#1152 [2] whatwg/fetch#1646 Bug: 1323391 Low-Coverage-Reason: the uncovered lines in request.cc are covered by WPT tests Change-Id: I5c25048fcf9f8db759e2f0181ac338c8b603b451 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4515276 Reviewed-by: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Commit-Queue: Scott Haseley <shaseley@chromium.org> Reviewed-by: Ben Kelly <wanderview@chromium.org> Cr-Commit-Position: refs/heads/main@{#1144953}
AbortSignal's "follow" algorithm is replaced with a new algorithm for creating a dependent abort signal ([1], [2]), which is implemented as a new AbortSignal constructor. This CL replaces Follow with the new ctor, behind the AbortSignalAny REF. There is a slight compat risk with the change (when the feature ships): 1. fetch: abort is propagated to dependent signals after abort event dispatch for a signal, whereas "follow" propagated abort before (as abort algorithms), which is observable. We could alternatively use weak abort algorithms in AbortSignal.any(), but that would be more complicated and partially limit GC optimizations. 2. cache: The use of "follow" in cache.addAll was not specced and could lead to abort events firing when they shouldn't have. The new behavior preserves the specced behavior (aborting outstanding requests on failure) without dispatching events when abort is triggered internally. Note: the virtual test expectations match the current expectations except for the test modified in fetch/api/abort/general.any.js. [1] whatwg/dom#1152 [2] whatwg/fetch#1646 Bug: 1323391 Low-Coverage-Reason: the uncovered lines in request.cc are covered by WPT tests Change-Id: I5c25048fcf9f8db759e2f0181ac338c8b603b451 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4515276 Reviewed-by: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Commit-Queue: Scott Haseley <shaseley@chromium.org> Reviewed-by: Ben Kelly <wanderview@chromium.org> Cr-Commit-Position: refs/heads/main@{#1144953}
@shaseley thank you so much for driving this to completion! I think it's very nice that we now have this composition method. Reason for the delay in merging: to double check everyone was still comfortable with the approach here given the uncertainty at the end, I reached out to Olli (for Gecko) and Chris (for WebKit) and they both indicated this still seemed good. If folks notice anything that was overlooked here please raise a new issue. Although we often do end up seeing comments in them, closed issues are in principle not monitored and definitely not a good place to write something down you think should be tracked. |
See whatwg/dom#1152 for context. Tests: web-platform-tests/wpt#39951. SW PR: w3c/ServiceWorker#1678.
https://bugs.webkit.org/show_bug.cgi?id=256176 rdar://109056627 Reviewed by Brent Fulgham. Implement AbortSignal.any() as per: - whatwg/dom#1152 The feature is behind a runtime flag, disabled by default. * LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/dom/abort/abort-signal-any.any.worker-expected.txt: * Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml: * Source/WebCore/bindings/js/JSAbortSignalCustom.cpp: (WebCore::JSAbortSignalOwner::isReachableFromOpaqueRoots): * Source/WebCore/dom/AbortSignal.cpp: (WebCore::AbortSignal::any): (WebCore::AbortSignal::addSourceSignal): (WebCore::AbortSignal::addDependentSignal): (WebCore::AbortSignal::signalAbort): * Source/WebCore/dom/AbortSignal.h: * Source/WebCore/dom/AbortSignal.idl: Canonical link: https://commits.webkit.org/264163@main
PR-URL: #47821 Fixes: #47811 Refs: whatwg/dom#1152 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
…nd cache-storage, a=testonly Automatic update from web-platform-tests Don't use AbortSignal::Follow in fetch and cache-storage AbortSignal's "follow" algorithm is replaced with a new algorithm for creating a dependent abort signal ([1], [2]), which is implemented as a new AbortSignal constructor. This CL replaces Follow with the new ctor, behind the AbortSignalAny REF. There is a slight compat risk with the change (when the feature ships): 1. fetch: abort is propagated to dependent signals after abort event dispatch for a signal, whereas "follow" propagated abort before (as abort algorithms), which is observable. We could alternatively use weak abort algorithms in AbortSignal.any(), but that would be more complicated and partially limit GC optimizations. 2. cache: The use of "follow" in cache.addAll was not specced and could lead to abort events firing when they shouldn't have. The new behavior preserves the specced behavior (aborting outstanding requests on failure) without dispatching events when abort is triggered internally. Note: the virtual test expectations match the current expectations except for the test modified in fetch/api/abort/general.any.js. [1] whatwg/dom#1152 [2] whatwg/fetch#1646 Bug: 1323391 Low-Coverage-Reason: the uncovered lines in request.cc are covered by WPT tests Change-Id: I5c25048fcf9f8db759e2f0181ac338c8b603b451 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4515276 Reviewed-by: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Commit-Queue: Scott Haseley <shaseley@chromium.org> Reviewed-by: Ben Kelly <wanderview@chromium.org> Cr-Commit-Position: refs/heads/main@{#1144953} -- wpt-commits: 29c729d6b60e74383e8e8222f06bb7c3680b2022 wpt-pr: 39951
PR-URL: nodejs#47821 Fixes: nodejs#47811 Refs: whatwg/dom#1152 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
…nd cache-storage, a=testonly Automatic update from web-platform-tests Don't use AbortSignal::Follow in fetch and cache-storage AbortSignal's "follow" algorithm is replaced with a new algorithm for creating a dependent abort signal ([1], [2]), which is implemented as a new AbortSignal constructor. This CL replaces Follow with the new ctor, behind the AbortSignalAny REF. There is a slight compat risk with the change (when the feature ships): 1. fetch: abort is propagated to dependent signals after abort event dispatch for a signal, whereas "follow" propagated abort before (as abort algorithms), which is observable. We could alternatively use weak abort algorithms in AbortSignal.any(), but that would be more complicated and partially limit GC optimizations. 2. cache: The use of "follow" in cache.addAll was not specced and could lead to abort events firing when they shouldn't have. The new behavior preserves the specced behavior (aborting outstanding requests on failure) without dispatching events when abort is triggered internally. Note: the virtual test expectations match the current expectations except for the test modified in fetch/api/abort/general.any.js. [1] whatwg/dom#1152 [2] whatwg/fetch#1646 Bug: 1323391 Low-Coverage-Reason: the uncovered lines in request.cc are covered by WPT tests Change-Id: I5c25048fcf9f8db759e2f0181ac338c8b603b451 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4515276 Reviewed-by: Nidhi Jaju <nidhijaju@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Commit-Queue: Scott Haseley <shaseley@chromium.org> Reviewed-by: Ben Kelly <wanderview@chromium.org> Cr-Commit-Position: refs/heads/main@{#1144953} -- wpt-commits: 29c729d6b60e74383e8e8222f06bb7c3680b2022 wpt-pr: 39951
PR-URL: #47821 Fixes: #47811 Refs: whatwg/dom#1152 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: nodejs#47821 Fixes: nodejs#47811 Refs: whatwg/dom#1152 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
PR-URL: #47821 Backport-PR-URL: #48800 Fixes: #47811 Refs: whatwg/dom#1152 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Hi DOM folks,
I've got a working implementation of AbortSignal.any() with tentative WPT tests, so I wanted to send out a draft spec for thoughts/consideration. It's probably not ready as-is because it would break other specs (see below), but this is how I thought the end state might look.
PR Description
This adds IDL and algorithms for AbortSignal.any().
Implementation details:
This implements an optimization that puts all children on non-composite signals (i.e. those associated with a controller or timeout). This allows "intermediate" nodes (e.g. B in A follows B follows C) to be garbage collected if they are only being kept alive to propagate aborts.
This removes the follow algorithm, so callsites will need to be updated.
This makes "signal abort" private. This is done so we can make assumptions about when a signal can no longer abort. The few places that directly signal abort will be converted to use an AbortController (this also exposes an algorithm to "request abort" on a controller for this).
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 AbortSignal factory APIs specify a realm.
Dependent / Follow-up work
As-is, this PR changes how other specs would interact with abort signals and controllers. The related work is to:
Implementation Notes
There's an implementation of this in Chromium behind a flag in Canary (M112). The basic implementation was straightforward, but there was a bit of work to get the memory management implemented (what's described in the Garbage Collection section in the draft PR). Composite signals can't be GCed if they have abort algorithms (or abort event listeners) and there's a source signal that can still signal abort, but we weren't removing abort algorithms once they were "done", which prevented GC. I fixed this in Chromium, but we should probably also update specs too.
PR Questions
(See WHATWG Working Mode: Changes for more details.)
Additional Info
Preview | Diff