lib: defer AbortSignal.any() following · nodejs/node@ada3ce8
@@ -85,17 +85,16 @@ function lazyMessageChannel() {
8585}
86868787const clearTimeoutRegistry = new SafeFinalizationRegistry(clearTimeout);
88-const dependantSignalsCleanupRegistry = new SafeFinalizationRegistry((signalWeakRef) => {
89-const signal = signalWeakRef.deref();
90-if (signal === undefined) {
91-return;
92-}
93-signal[kDependantSignals].forEach((ref) => {
94-if (ref.deref() === undefined) {
95-signal[kDependantSignals].delete(ref);
88+const dependantSignalsCleanupRegistry = new SafeFinalizationRegistry(
89+({ sourceSignalRef, dependantSignalRef, sourceSignalsCleanupToken }) => {
90+sourceSignalsCleanupRegistry.unregister(sourceSignalsCleanupToken);
91+92+const sourceSignal = sourceSignalRef.deref();
93+if (sourceSignal === undefined) {
94+return;
9695}
96+sourceSignal[kDependantSignals].delete(dependantSignalRef);
9797});
98-});
999810099const gcPersistentSignals = new SafeSet();
101100@@ -117,6 +116,8 @@ const kCloneData = Symbol('kCloneData');
117116const kTimeout = Symbol('kTimeout');
118117const kMakeTransferable = Symbol('kMakeTransferable');
119118const kComposite = Symbol('kComposite');
119+const kFollowing = Symbol('kFollowing');
120+const kResultSignalWeakRef = Symbol('kResultSignalWeakRef');
120121const kSourceSignals = Symbol('kSourceSignals');
121122const kDependantSignals = Symbol('kDependantSignals');
122123@@ -136,6 +137,60 @@ function validateThisAbortSignal(obj) {
136137throw new ERR_INVALID_THIS('AbortSignal');
137138}
138139140+function refreshCompositeSignal(signal) {
141+if (!signal[kComposite] || signal[kAborted] || !signal[kSourceSignals]?.size) {
142+return;
143+}
144+145+for (const sourceSignalWeakRef of signal[kSourceSignals]) {
146+const sourceSignal = sourceSignalWeakRef.deref();
147+if (sourceSignal === undefined) {
148+signal[kSourceSignals].delete(sourceSignalWeakRef);
149+continue;
150+}
151+152+if (sourceSignal.aborted) {
153+abortSignal(signal, sourceSignal.reason);
154+return;
155+}
156+}
157+}
158+159+function followCompositeSignal(signal) {
160+if (signal[kFollowing] || signal[kAborted] || !signal[kSourceSignals]?.size) {
161+return;
162+}
163+164+const resultSignalWeakRef = signal[kResultSignalWeakRef] ??= new SafeWeakRef(signal);
165+166+for (const sourceSignalWeakRef of signal[kSourceSignals]) {
167+const sourceSignal = sourceSignalWeakRef.deref();
168+if (sourceSignal === undefined) {
169+signal[kSourceSignals].delete(sourceSignalWeakRef);
170+continue;
171+}
172+173+if (sourceSignal.aborted) {
174+abortSignal(signal, sourceSignal.reason);
175+return;
176+}
177+178+sourceSignal[kDependantSignals] ??= new SafeSet();
179+sourceSignal[kDependantSignals].add(resultSignalWeakRef);
180+dependantSignalsCleanupRegistry.register(signal, {
181+sourceSignalRef: sourceSignalWeakRef,
182+dependantSignalRef: resultSignalWeakRef,
183+sourceSignalsCleanupToken: sourceSignalWeakRef,
184+});
185+sourceSignalsCleanupRegistry.register(sourceSignal, {
186+sourceSignalRef: sourceSignalWeakRef,
187+composedSignalRef: resultSignalWeakRef,
188+}, sourceSignalWeakRef);
189+}
190+191+signal[kFollowing] = true;
192+}
193+139194// Because the AbortSignal timeout cannot be canceled, we don't want the
140195// presence of the timer alone to keep the AbortSignal from being garbage
141196// collected if it otherwise no longer accessible. We also don't want the
@@ -148,6 +203,7 @@ function setWeakAbortSignalTimeout(weakRef, delay) {
148203const timeout = setTimeout(() => {
149204const signal = weakRef.deref();
150205if (signal !== undefined) {
206+clearTimeoutRegistry.unregister(signal);
151207gcPersistentSignals.delete(signal);
152208abortSignal(
153209signal,
@@ -198,6 +254,7 @@ class AbortSignal extends EventTarget {
198254 */
199255get aborted() {
200256validateThisAbortSignal(this);
257+refreshCompositeSignal(this);
201258return !!this[kAborted];
202259}
203260@@ -206,11 +263,13 @@ class AbortSignal extends EventTarget {
206263 */
207264get reason() {
208265validateThisAbortSignal(this);
266+refreshCompositeSignal(this);
209267return this[kReason];
210268}
211269212270throwIfAborted() {
213271validateThisAbortSignal(this);
272+refreshCompositeSignal(this);
214273if (this[kAborted]) {
215274throw this[kReason];
216275}
@@ -241,7 +300,8 @@ class AbortSignal extends EventTarget {
241300signal[kTimeout] = true;
242301clearTimeoutRegistry.register(
243302signal,
244-setWeakAbortSignalTimeout(new SafeWeakRef(signal), delay));
303+setWeakAbortSignalTimeout(new SafeWeakRef(signal), delay),
304+signal);
245305return signal;
246306}
247307@@ -260,7 +320,6 @@ class AbortSignal extends EventTarget {
260320return resultSignal;
261321}
262322263-const resultSignalWeakRef = new SafeWeakRef(resultSignal);
264323resultSignal[kSourceSignals] = new SafeSet();
265324266325// Track if we have any timeout signals
@@ -283,51 +342,51 @@ class AbortSignal extends EventTarget {
283342return resultSignal;
284343}
285344286-signal[kDependantSignals] ??= new SafeSet();
287345if (!signal[kComposite]) {
288346const signalWeakRef = new SafeWeakRef(signal);
289347resultSignal[kSourceSignals].add(signalWeakRef);
290-signal[kDependantSignals].add(resultSignalWeakRef);
291-dependantSignalsCleanupRegistry.register(resultSignal, signalWeakRef);
292-sourceSignalsCleanupRegistry.register(signal, {
293-sourceSignalRef: signalWeakRef,
294-composedSignalRef: resultSignalWeakRef,
295-});
296348} else if (!signal[kSourceSignals]) {
297349continue;
298350} else {
351+refreshCompositeSignal(signal);
352+if (signal.aborted) {
353+abortSignal(resultSignal, signal.reason);
354+return resultSignal;
355+}
299356for (const sourceSignalWeakRef of signal[kSourceSignals]) {
300357const sourceSignal = sourceSignalWeakRef.deref();
301358if (!sourceSignal) {
302359continue;
303360}
304-assert(!sourceSignal.aborted);
305361assert(!sourceSignal[kComposite]);
306362363+if (sourceSignal.aborted) {
364+abortSignal(resultSignal, sourceSignal.reason);
365+return resultSignal;
366+}
367+307368if (resultSignal[kSourceSignals].has(sourceSignalWeakRef)) {
308369continue;
309370}
310371resultSignal[kSourceSignals].add(sourceSignalWeakRef);
311-sourceSignal[kDependantSignals].add(resultSignalWeakRef);
312-dependantSignalsCleanupRegistry.register(resultSignal, sourceSignalWeakRef);
313-sourceSignalsCleanupRegistry.register(signal, {
314-sourceSignalRef: sourceSignalWeakRef,
315-composedSignalRef: resultSignalWeakRef,
316-});
317372}
318373}
319374}
320375321-// If we have any timeout signals, add the composite signal to gcPersistentSignals
322376if (hasTimeoutSignals && resultSignal[kSourceSignals].size > 0) {
323-gcPersistentSignals.add(resultSignal);
377+resultSignal[kTimeout] = true;
324378}
325379326380return resultSignal;
327381}
328382329383[kNewListener](size, type, listener, once, capture, passive, weak) {
330384super[kNewListener](size, type, listener, once, capture, passive, weak);
385+386+if (this[kComposite] && type === 'abort' && !this.aborted && size === 1) {
387+followCompositeSignal(this);
388+}
389+331390const isTimeoutOrNonEmptyCompositeSignal = this[kTimeout] || (this[kComposite] && this[kSourceSignals]?.size);
332391if (isTimeoutOrNonEmptyCompositeSignal &&
333392type === 'abort' &&