Skip to content

Commit

Permalink
AbortSignal: Propagate aborted state to dependent signals before firi…
Browse files Browse the repository at this point in the history
…ng events

The implementation and spec of dependent signals assumes and asserts that a dependent signal is aborted if any of its sources have been
aborted. But this property does not hold during the abort process, since
intermediate states of abort propagation can be observed. For example,
calling AbortSignal.any([signal]) in an "abort" event listener for one
of signal's sources is a way to observe intermediate state, since the
"abort" event fires before the source signal's dependents are updated.

To fix this, this CL decouples setting the abort state and reacting to
abort event:
 1. Mark the source signal as aborted
 2. Propagate the aborted state to any dependent signals
 3. Run abort steps (run algorithms, fire events) for the source signal
 4. Run abort steps for each of the dependent signals

PR: whatwg/dom#1295

Change-Id: I65a97eb46b01a0071d661e945f64c90e33954088
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5664649
Commit-Queue: Scott Haseley <[email protected]>
Reviewed-by: Joey Arhar <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1344364}
  • Loading branch information
shaseley authored and chromium-wpt-export-bot committed Aug 20, 2024
1 parent c74037d commit 07a9d09
Showing 1 changed file with 39 additions and 0 deletions.
39 changes: 39 additions & 0 deletions dom/abort/resources/abort-signal-any-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,43 @@ function abortSignalAnyTests(signalInterface, controllerInterface) {
controller.abort();
assert_equals(result, "01234");
}, `Abort events for ${desc} signals fire in the right order ${suffix}`);

test(t => {
const controller = new controllerInterface();
const signal1 = signalInterface.any([controller.signal]);
const signal2 = signalInterface.any([signal1]);
let eventFired = false;

controller.signal.addEventListener('abort', () => {
const signal3 = signalInterface.any([signal2]);
assert_true(controller.signal.aborted);
assert_true(signal1.aborted);
assert_true(signal2.aborted);
assert_true(signal3.aborted);
eventFired = true;
});

controller.abort();
assert_true(eventFired, "event fired");
}, `Dependent signals for ${desc} are marked aborted before abort events fire ${suffix}`);

test(t => {
const controller1 = new controllerInterface();
const controller2 = new controllerInterface();
const signal = signalInterface.any([controller1.signal, controller2.signal]);
let count = 0;

controller1.signal.addEventListener('abort', () => {
controller2.abort("reason 2");
});

signal.addEventListener('abort', () => {
count++;
});

controller1.abort("reason 1");
assert_equals(count, 1);
assert_true(signal.aborted);
assert_equals(signal.reason, "reason 1");
}, `Dependent signals for ${desc} are aborted correctly for reentrant aborts ${suffix}`);
}

0 comments on commit 07a9d09

Please sign in to comment.