lib: fix AbortSignal.any() abort propagation · nodejs/node@7ae0855 · GitHub
Skip to content

Commit 7ae0855

Browse files
inoway46aduh95
authored andcommitted
lib: fix AbortSignal.any() abort propagation
Follow composite signal sources at construction so abort state is propagated even without listeners, before sources can be collected. Keep the existing weak-reference finalization and timeout cleanup. Cover source collection before each state accessor, including nested composites, and cleanup of unreachable listener-less dependents. Fixes: #65995 Refs: #62363 Assisted-by: Codex Signed-off-by: inoway46 <inoueyuya416@gmail.com> PR-URL: #66014 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Xuguang Mei <meixuguang@gmail.com> Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 33775d2 commit 7ae0855

3 files changed

Lines changed: 64 additions & 5 deletions

File tree

‎lib/internal/abort_controller.js‎

Lines changed: 4 additions & 0 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Flags: --expose-gc
2+
3+
import '../common/index.mjs';
4+
import { gcUntil } from '../common/gc.js';
5+
import assert from 'node:assert/strict';
6+
import { it } from 'node:test';
7+
8+
for (const nested of [false, true]) {
9+
for (const accessor of ['aborted', 'reason', 'throwIfAborted']) {
10+
it(`preserves ${accessor} after source GC (nested: ${nested})`, async () => {
11+
let controller = new AbortController();
12+
const sourceRef = new WeakRef(controller.signal);
13+
let signal = AbortSignal.any([controller.signal]);
14+
if (nested) signal = AbortSignal.any([signal]);
15+
const reason = { message: 'stop' };
16+
17+
controller.abort(reason);
18+
controller = null;
19+
20+
// Do not observe the composite or attach a listener before source GC.
21+
await gcUntil('source signal is collected', () => sourceRef.deref() === undefined);
22+
23+
// Exercise each entry point before any other accessor can refresh state.
24+
if (accessor === 'aborted') assert.strictEqual(signal.aborted, true);
25+
if (accessor === 'reason') assert.strictEqual(signal.reason, reason);
26+
assert.throws(() => signal.throwIfAborted(), (err) => err === reason);
27+
assert.strictEqual(signal.aborted, true);
28+
assert.strictEqual(signal.reason, reason);
29+
});
30+
}
31+
}

‎test/parallel/test-abortsignal-drop-settled-signals.mjs‎

Lines changed: 29 additions & 5 deletions

0 commit comments

Comments
 (0)