From 86f7b923f507d4a2574baa40b06f45fd070f2a12 Mon Sep 17 00:00:00 2001 From: Edy Silva Date: Tue, 26 Nov 2024 13:05:11 -0300 Subject: [PATCH] lib: settle signals when controller's signal is GCed --- lib/internal/abort_controller.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index b812f588c23e99..4da7ecc7b2fe11 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -95,8 +95,25 @@ const dependantSignalsCleanupRegistry = new SafeFinalizationRegistry((signalWeak } }); }); + const gcPersistentSignals = new SafeSet(); +const finalizer = new SafeFinalizationRegistry(({ sourceSignalRef, composedSignalRef }) => { + // TODO: remove ref from source signal + // TODO: remove composed signal from gcPersistentSignals + const composedSignal = composedSignalRef.deref(); + if (composedSignal !== undefined) { + composedSignal[kSourceSignals].delete(sourceSignalRef); + gcPersistentSignals.delete(composedSignal); + } + + // TODO: remove ref from dependant signal + const sourceSignal = sourceSignalRef.deref(); + if (sourceSignal !== undefined) { + sourceSignal[kDependantSignals].delete(composedSignalRef); + } +}); + const kAborted = Symbol('kAborted'); const kReason = Symbol('kReason'); const kCloneData = Symbol('kCloneData'); @@ -258,6 +275,9 @@ class AbortSignal extends EventTarget { resultSignal[kSourceSignals].add(signalWeakRef); signal[kDependantSignals].add(resultSignalWeakRef); dependantSignalsCleanupRegistry.register(resultSignal, signalWeakRef); + // when the source signal - coming from the controller - is gced, we need to remove it from the dependant + // signals of the composite signal + finalizer.register(signal, { sourceSignalRef: signalWeakRef, composedSignalRef: resultSignalWeakRef}); } else if (!signal[kSourceSignals]) { continue; } else { @@ -293,6 +313,7 @@ class AbortSignal extends EventTarget { // listener, then we don't want it to be gc'd while the listener // is attached and the timer still hasn't fired. So, we retain a // strong ref that is held for as long as the listener is registered. + gcPersistentSignals.add(this); } } @@ -434,6 +455,7 @@ class AbortController { */ get signal() { this.#signal ??= new AbortSignal(kDontThrowSymbol); + return this.#signal; }