@@ -85,17 +85,16 @@ function lazyMessageChannel() { |
85 | 85 | } |
86 | 86 |
|
87 | 87 | const 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; |
96 | 95 | } |
| 96 | + sourceSignal[kDependantSignals].delete(dependantSignalRef); |
97 | 97 | }); |
98 | | -}); |
99 | 98 |
|
100 | 99 | const gcPersistentSignals = new SafeSet(); |
101 | 100 |
|
@@ -117,6 +116,8 @@ const kCloneData = Symbol('kCloneData'); |
117 | 116 | const kTimeout = Symbol('kTimeout'); |
118 | 117 | const kMakeTransferable = Symbol('kMakeTransferable'); |
119 | 118 | const kComposite = Symbol('kComposite'); |
| 119 | +const kFollowing = Symbol('kFollowing'); |
| 120 | +const kResultSignalWeakRef = Symbol('kResultSignalWeakRef'); |
120 | 121 | const kSourceSignals = Symbol('kSourceSignals'); |
121 | 122 | const kDependantSignals = Symbol('kDependantSignals'); |
122 | 123 |
|
@@ -136,6 +137,60 @@ function validateThisAbortSignal(obj) { |
136 | 137 | throw new ERR_INVALID_THIS('AbortSignal'); |
137 | 138 | } |
138 | 139 |
|
| 140 | +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 | + |
139 | 194 | // Because the AbortSignal timeout cannot be canceled, we don't want the |
140 | 195 | // presence of the timer alone to keep the AbortSignal from being garbage |
141 | 196 | // collected if it otherwise no longer accessible. We also don't want the |
@@ -148,6 +203,7 @@ function setWeakAbortSignalTimeout(weakRef, delay) { |
148 | 203 | const timeout = setTimeout(() => { |
149 | 204 | const signal = weakRef.deref(); |
150 | 205 | if (signal !== undefined) { |
| 206 | + clearTimeoutRegistry.unregister(signal); |
151 | 207 | gcPersistentSignals.delete(signal); |
152 | 208 | abortSignal( |
153 | 209 | signal, |
@@ -198,6 +254,7 @@ class AbortSignal extends EventTarget { |
198 | 254 | */ |
199 | 255 | get aborted() { |
200 | 256 | validateThisAbortSignal(this); |
| 257 | + refreshCompositeSignal(this); |
201 | 258 | return !!this[kAborted]; |
202 | 259 | } |
203 | 260 |
|
@@ -206,11 +263,13 @@ class AbortSignal extends EventTarget { |
206 | 263 | */ |
207 | 264 | get reason() { |
208 | 265 | validateThisAbortSignal(this); |
| 266 | + refreshCompositeSignal(this); |
209 | 267 | return this[kReason]; |
210 | 268 | } |
211 | 269 |
|
212 | 270 | throwIfAborted() { |
213 | 271 | validateThisAbortSignal(this); |
| 272 | + refreshCompositeSignal(this); |
214 | 273 | if (this[kAborted]) { |
215 | 274 | throw this[kReason]; |
216 | 275 | } |
@@ -241,7 +300,8 @@ class AbortSignal extends EventTarget { |
241 | 300 | signal[kTimeout] = true; |
242 | 301 | clearTimeoutRegistry.register( |
243 | 302 | signal, |
244 | | - setWeakAbortSignalTimeout(new SafeWeakRef(signal), delay)); |
| 303 | + setWeakAbortSignalTimeout(new SafeWeakRef(signal), delay), |
| 304 | + signal); |
245 | 305 | return signal; |
246 | 306 | } |
247 | 307 |
|
@@ -260,7 +320,6 @@ class AbortSignal extends EventTarget { |
260 | 320 | return resultSignal; |
261 | 321 | } |
262 | 322 |
|
263 | | - const resultSignalWeakRef = new SafeWeakRef(resultSignal); |
264 | 323 | resultSignal[kSourceSignals] = new SafeSet(); |
265 | 324 |
|
266 | 325 | // Track if we have any timeout signals |
@@ -283,51 +342,51 @@ class AbortSignal extends EventTarget { |
283 | 342 | return resultSignal; |
284 | 343 | } |
285 | 344 |
|
286 | | - signal[kDependantSignals] ??= new SafeSet(); |
287 | 345 | if (!signal[kComposite]) { |
288 | 346 | const signalWeakRef = new SafeWeakRef(signal); |
289 | 347 | resultSignal[kSourceSignals].add(signalWeakRef); |
290 | | - signal[kDependantSignals].add(resultSignalWeakRef); |
291 | | - dependantSignalsCleanupRegistry.register(resultSignal, signalWeakRef); |
292 | | - sourceSignalsCleanupRegistry.register(signal, { |
293 | | - sourceSignalRef: signalWeakRef, |
294 | | - composedSignalRef: resultSignalWeakRef, |
295 | | - }); |
296 | 348 | } else if (!signal[kSourceSignals]) { |
297 | 349 | continue; |
298 | 350 | } else { |
| 351 | + refreshCompositeSignal(signal); |
| 352 | + if (signal.aborted) { |
| 353 | + abortSignal(resultSignal, signal.reason); |
| 354 | + return resultSignal; |
| 355 | + } |
299 | 356 | for (const sourceSignalWeakRef of signal[kSourceSignals]) { |
300 | 357 | const sourceSignal = sourceSignalWeakRef.deref(); |
301 | 358 | if (!sourceSignal) { |
302 | 359 | continue; |
303 | 360 | } |
304 | | - assert(!sourceSignal.aborted); |
305 | 361 | assert(!sourceSignal[kComposite]); |
306 | 362 |
|
| 363 | + if (sourceSignal.aborted) { |
| 364 | + abortSignal(resultSignal, sourceSignal.reason); |
| 365 | + return resultSignal; |
| 366 | + } |
| 367 | + |
307 | 368 | if (resultSignal[kSourceSignals].has(sourceSignalWeakRef)) { |
308 | 369 | continue; |
309 | 370 | } |
310 | 371 | resultSignal[kSourceSignals].add(sourceSignalWeakRef); |
311 | | - sourceSignal[kDependantSignals].add(resultSignalWeakRef); |
312 | | - dependantSignalsCleanupRegistry.register(resultSignal, sourceSignalWeakRef); |
313 | | - sourceSignalsCleanupRegistry.register(signal, { |
314 | | - sourceSignalRef: sourceSignalWeakRef, |
315 | | - composedSignalRef: resultSignalWeakRef, |
316 | | - }); |
317 | 372 | } |
318 | 373 | } |
319 | 374 | } |
320 | 375 |
|
321 | | - // If we have any timeout signals, add the composite signal to gcPersistentSignals |
322 | 376 | if (hasTimeoutSignals && resultSignal[kSourceSignals].size > 0) { |
323 | | - gcPersistentSignals.add(resultSignal); |
| 377 | + resultSignal[kTimeout] = true; |
324 | 378 | } |
325 | 379 |
|
326 | 380 | return resultSignal; |
327 | 381 | } |
328 | 382 |
|
329 | 383 | [kNewListener](size, type, listener, once, capture, passive, weak) { |
330 | 384 | super[kNewListener](size, type, listener, once, capture, passive, weak); |
| 385 | + |
| 386 | + if (this[kComposite] && type === 'abort' && !this.aborted && size === 1) { |
| 387 | + followCompositeSignal(this); |
| 388 | + } |
| 389 | + |
331 | 390 | const isTimeoutOrNonEmptyCompositeSignal = this[kTimeout] || (this[kComposite] && this[kSourceSignals]?.size); |
332 | 391 | if (isTimeoutOrNonEmptyCompositeSignal && |
333 | 392 | type === 'abort' && |
|