@@ -259,14 +259,30 @@ class AbortSignal extends EventTarget {
259259 if ( ! signalsArray . length ) {
260260 return resultSignal ;
261261 }
262+
262263 const resultSignalWeakRef = new SafeWeakRef ( resultSignal ) ;
263264 resultSignal [ kSourceSignals ] = new SafeSet ( ) ;
265+
266+ // Track if we have any timeout signals
267+ let hasTimeoutSignals = false ;
268+
264269 for ( let i = 0 ; i < signalsArray . length ; i ++ ) {
265270 const signal = signalsArray [ i ] ;
271+
272+ // Check if this is a timeout signal
273+ if ( signal [ kTimeout ] ) {
274+ hasTimeoutSignals = true ;
275+
276+ // Add the timeout signal to gcPersistentSignals to keep it alive
277+ // This is what the kNewListener method would do when adding abort listeners
278+ gcPersistentSignals . add ( signal ) ;
279+ }
280+
266281 if ( signal . aborted ) {
267282 abortSignal ( resultSignal , signal . reason ) ;
268283 return resultSignal ;
269284 }
285+
270286 signal [ kDependantSignals ] ??= new SafeSet ( ) ;
271287 if ( ! signal [ kComposite ] ) {
272288 const signalWeakRef = new SafeWeakRef ( signal ) ;
@@ -301,6 +317,12 @@ class AbortSignal extends EventTarget {
301317 }
302318 }
303319 }
320+
321+ // If we have any timeout signals, add the composite signal to gcPersistentSignals
322+ if ( hasTimeoutSignals && resultSignal [ kSourceSignals ] . size > 0 ) {
323+ gcPersistentSignals . add ( resultSignal ) ;
324+ }
325+
304326 return resultSignal ;
305327 }
306328
@@ -416,8 +438,10 @@ function abortSignal(signal, reason) {
416438 // otherwise to a new "AbortError" DOMException.
417439 signal [ kAborted ] = true ;
418440 signal [ kReason ] = reason ;
441+
419442 // 3. Let dependentSignalsToAbort be a new list.
420443 const dependentSignalsToAbort = ObjectSetPrototypeOf ( [ ] , null ) ;
444+
421445 // 4. For each dependentSignal of signal's dependent signals:
422446 signal [ kDependantSignals ] ?. forEach ( ( s ) => {
423447 const dependentSignal = s . deref ( ) ;
@@ -433,12 +457,27 @@ function abortSignal(signal, reason) {
433457
434458 // 5. Run the abort steps for signal
435459 runAbort ( signal ) ;
460+
436461 // 6. For each dependentSignal of dependentSignalsToAbort,
437462 // run the abort steps for dependentSignal.
438463 for ( let i = 0 ; i < dependentSignalsToAbort . length ; i ++ ) {
439464 const dependentSignal = dependentSignalsToAbort [ i ] ;
440465 runAbort ( dependentSignal ) ;
441466 }
467+
468+ // Clean up the signal from gcPersistentSignals
469+ gcPersistentSignals . delete ( signal ) ;
470+
471+ // If this is a composite signal, also remove all of its source signals from gcPersistentSignals
472+ // when they get dereferenced from the signal's kSourceSignals set
473+ if ( signal [ kComposite ] && signal [ kSourceSignals ] ) {
474+ signal [ kSourceSignals ] . forEach ( ( sourceWeakRef ) => {
475+ const sourceSignal = sourceWeakRef . deref ( ) ;
476+ if ( sourceSignal ) {
477+ gcPersistentSignals . delete ( sourceSignal ) ;
478+ }
479+ } ) ;
480+ }
442481}
443482
444483// To run the abort steps for an AbortSignal signal
0 commit comments