Skip to content

Commit fd55d3c

Browse files
geeksilva97aduh95
authored andcommitted
lib: clean up persisted signals when they are settled
PR-URL: #56001 Refs: #55328Fixes: #55328 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Jason Zhang <[email protected]> Reviewed-By: Juan José Arboleda <[email protected]>
1 parent 84f98e0 commit fd55d3c

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

‎lib/internal/abort_controller.js‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,21 @@ const dependantSignalsCleanupRegistry = new SafeFinalizationRegistry((signalWeak
9696
}
9797
});
9898
});
99+
99100
constgcPersistentSignals=newSafeSet();
100101

102+
constsourceSignalsCleanupRegistry=newSafeFinalizationRegistry(({ sourceSignalRef, composedSignalRef })=>{
103+
constcomposedSignal=composedSignalRef.deref();
104+
if(composedSignal!==undefined){
105+
composedSignal[kSourceSignals].delete(sourceSignalRef);
106+
107+
if(composedSignal[kSourceSignals].size===0){
108+
// This signal will no longer abort. There's no need to keep it in the gcPersistentSignals set.
109+
gcPersistentSignals.delete(composedSignal);
110+
}
111+
}
112+
});
113+
101114
constkAborted=Symbol('kAborted');
102115
constkReason=Symbol('kReason');
103116
constkCloneData=Symbol('kCloneData');
@@ -260,6 +273,10 @@ class AbortSignal extends EventTarget{
260273
resultSignal[kSourceSignals].add(signalWeakRef);
261274
signal[kDependantSignals].add(resultSignalWeakRef);
262275
dependantSignalsCleanupRegistry.register(resultSignal,signalWeakRef);
276+
sourceSignalsCleanupRegistry.register(signal,{
277+
sourceSignalRef: signalWeakRef,
278+
composedSignalRef: resultSignalWeakRef,
279+
});
263280
}elseif(!signal[kSourceSignals]){
264281
continue;
265282
}else{
@@ -277,6 +294,10 @@ class AbortSignal extends EventTarget{
277294
resultSignal[kSourceSignals].add(sourceSignalWeakRef);
278295
sourceSignal[kDependantSignals].add(resultSignalWeakRef);
279296
dependantSignalsCleanupRegistry.register(resultSignal,sourceSignalWeakRef);
297+
sourceSignalsCleanupRegistry.register(signal,{
298+
sourceSignalRef: sourceSignalWeakRef,
299+
composedSignalRef: resultSignalWeakRef,
300+
});
280301
}
281302
}
282303
}
@@ -436,6 +457,7 @@ class AbortController{
436457
*/
437458
getsignal(){
438459
this.#signal ??=newAbortSignal(kDontThrowSymbol);
460+
439461
returnthis.#signal;
440462
}
441463

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

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

67+
functionrunWithOrphanListeners(limit,done){
68+
letcomposedSignalRef;
69+
constcomposedSignalRefs=[];
70+
consthandler=()=>{};
71+
72+
functionrun(iteration){
73+
constac=newAbortController();
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=newWeakRef(AbortSignal.any([ac.signal]));
87+
composedSignalRef.deref().addEventListener('abort',handler);
88+
89+
constotherComposedSignalRef=newWeakRef(AbortSignal.any([composedSignalRef.deref()]));
90+
otherComposedSignalRef.deref().addEventListener('abort',handler);
91+
92+
composedSignalRefs.push(composedSignalRef,otherComposedSignalRef);
93+
94+
setImmediate(()=>{
95+
run(iteration+1);
96+
});
97+
}
98+
99+
run(1);
100+
}
101+
67102
constlimit=10_000;
68103

69104
describe('when there is a long-lived signal',()=>{
@@ -120,3 +155,23 @@ it('drops settled dependant signals when signal is composite', (t, done) =>{
120155
});
121156
});
122157
});
158+
159+
it('drops settled signals even when there are listeners',(t,done)=>{
160+
runWithOrphanListeners(limit,(signalRefs)=>{
161+
setImmediate(()=>{
162+
global.gc();
163+
setImmediate(()=>{
164+
global.gc();// One more call needed to clean up the deeper composed signals
165+
setImmediate(()=>{
166+
global.gc();// One more call needed to clean up the deeper composed signals
167+
168+
constunGCedSignals=[...signalRefs].filter((ref)=>ref.deref());
169+
170+
t.assert.strictEqual(unGCedSignals.length,0);
171+
172+
done();
173+
});
174+
});
175+
});
176+
});
177+
});

0 commit comments

Comments
(0)