Skip to content

Commit 643efe1

Browse files
committed
test: test clean up settled signals
1 parent 86f7b92 commit 643efe1

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

lib/internal/abort_controller.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ const dependantSignalsCleanupRegistry = new SafeFinalizationRegistry((signalWeak
9999
const gcPersistentSignals = new SafeSet();
100100

101101
const finalizer = new SafeFinalizationRegistry(({ sourceSignalRef, composedSignalRef }) => {
102-
// TODO: remove ref from source signal
103-
// TODO: remove composed signal from gcPersistentSignals
104102
const composedSignal = composedSignalRef.deref();
105103
if (composedSignal !== undefined) {
106104
composedSignal[kSourceSignals].delete(sourceSignalRef);
107-
gcPersistentSignals.delete(composedSignal);
105+
106+
if (composedSignal[kSourceSignals].size === 0) {
107+
// This signal will no longer abort. There's no need to keep it in the gcPersistentSignals set.
108+
gcPersistentSignals.delete(composedSignal);
109+
}
108110
}
109111

110-
// TODO: remove ref from dependant signal
111112
const sourceSignal = sourceSignalRef.deref();
112113
if (sourceSignal !== undefined) {
113114
sourceSignal[kDependantSignals].delete(composedSignalRef);

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,38 @@ function runShortLivedSourceSignal(limit, done) {
6464
run(1);
6565
};
6666

67+
function runWithOrphanListeners(limit, done) {
68+
let composedSignalRef;
69+
const composedSignalRefs = [];
70+
const handler = () => { };
71+
72+
function run(iteration) {
73+
const ac = new AbortController();
74+
if (iteration > limit) {
75+
setImmediate(() => {
76+
global.gc();
77+
setImmediate(() => {
78+
global.gc();
79+
80+
done(composedSignalRefs);
81+
});
82+
});
83+
return;
84+
}
85+
86+
composedSignalRef = new WeakRef(AbortSignal.any([ac.signal]));
87+
composedSignalRef.deref().addEventListener('abort', handler);
88+
89+
composedSignalRefs.push(composedSignalRef);
90+
91+
setImmediate(() => {
92+
run(iteration + 1);
93+
});
94+
}
95+
96+
run(1);
97+
}
98+
6799
const limit = 10_000;
68100

69101
describe('when there is a long-lived signal', () => {
@@ -120,3 +152,17 @@ it('drops settled dependant signals when signal is composite', (t, done) => {
120152
});
121153
});
122154
});
155+
156+
it('drops settled signals even when there are listeners', (t, done) => {
157+
runWithOrphanListeners(limit, (signalRefs) => {
158+
setImmediate(() => {
159+
global.gc();
160+
161+
const unGCedSignals = [...signalRefs].filter((ref) => ref.deref());
162+
163+
t.assert.strictEqual(unGCedSignals.length, 0);
164+
165+
done();
166+
});
167+
});
168+
});

0 commit comments

Comments
 (0)