Skip to content
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

AbortSignal.any() assertion failure #1293

Closed
vinhill opened this issue Jun 20, 2024 · 4 comments · Fixed by #1295
Closed

AbortSignal.any() assertion failure #1293

vinhill opened this issue Jun 20, 2024 · 4 comments · Fixed by #1295

Comments

@vinhill
Copy link
Contributor

vinhill commented Jun 20, 2024

What is the issue with the DOM Standard?

In Gecko Bug 1903676, a test case was found where the assertion in create a dependent abort signal step 4.2.1. does not hold. I believe this is a spec issue.

Here is the example

let a = new AbortController();
let b = AbortSignal.any([a.signal]);
a.signal.addEventListener("abort", () => {
  AbortSignal.any([b]);
  console.log(b.aborted, a.signal.aborted);  // false, true
})
a.abort();

During the abort event of a, we create a dependent signal from b, which is not yet aborted. If b is not aborted and dependent on a, the spec expects a to be not aborted too and asserts this. The problem is that signal abort fires the event before aborting the dependent signals, steps 5 and 6 of signal abort could be switched to resolve this issue.

@shaseley

@vinhill
Copy link
Contributor Author

vinhill commented Jun 20, 2024

I can get a PR up for this, the wpt "Abort events for AbortSignal.any() signals fire in the right order (using AbortController)" probably would require tweaking. The abort algorithm was introduced in #1152

@shaseley
Copy link
Contributor

Thanks, yes that's indeed a problem. I confirmed we hit the same assert in Chromium.

Re: moving the steps around, I think step 6 in "signal abort" might need to go above step 3 though, since running the abort algorithms can invoke JS, which could also call AbortSignal.any() and hit that assert (i.e the problem is running any JS between setting the source's abort reason and dependents')?

If we wanted to maintain the same order, we'd need to change "create a dependent abort signal" to check for the aborted state of the dependent signals' source signals, but then you could get cases where AbortSignal.any([b]) returns an aborted signal even though b isn't aborted yet. I guess you could also propagate the aborted state separately first, but that's a lot more complexity than it's probably worth.

@shaseley
Copy link
Contributor

On second thought, I don't think reordering would work. You could still have situations where the assert fails, e.g. by modifying the test case as follows:

<script>

let a = new AbortController();
let b = AbortSignal.any([a.signal]);
let c = AbortSignal.any([b]);
let d;
b.addEventListener("abort", () => {
  d = AbortSignal.any([c]);
  console.log(b.aborted, a.signal.aborted, c.aborted, d.aborted);
})
a.abort();
console.log(b.aborted, a.signal.aborted, c.aborted, d.aborted);

</script>

Maybe it wouldn't be too bad to split out propagating the abort reason and running abort algorithms/firing events so that all dependents have their aborted state atomically updated.

shaseley added a commit to shaseley/dom that referenced this issue Jul 15, 2024
The assert in 4.2.1 of "create a dependent abort signal" fails when
creating a dependent signal while dispatching abort events or running
abort algorithms if abort had not yet been propagated to one of the
sources.

This fix splits "signal abort" into two phases: first, set the abort
reason on the signal being aborted and all of its unaborted dependents;
next, run the abort algorithms and dispatch events for the signal and
those same dependents. Note that:
 1. Dependent signals do not themselves have dependent signals, which
    means it's unnecessary to recursively call "signal abort"
 2. This approach retains the existing event dispatch order, while
    ensuring the abort state is synced before any JS runs

This fixes whatwg#1293.
@shaseley
Copy link
Contributor

Potential fix: #1295

annevk pushed a commit that referenced this issue Aug 21, 2024
The assert in 4.2.1 of "create a dependent abort signal" fails when
creating a dependent signal while dispatching abort events or running
abort algorithms if abort had not yet been propagated to one of the
sources.

This fix splits "signal abort" into two phases: first, set the abort
reason on the signal being aborted and all of its unaborted dependents;
next, run the abort algorithms and dispatch events for the signal and
those same dependents. Note that:
 1. Dependent signals do not themselves have dependent signals, which
    means it's unnecessary to recursively call "signal abort"
 2. This approach retains the existing event dispatch order, while
    ensuring the abort state is synced before any JS runs

This fixes #1293.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants