From 2a7a07ffd37b9ffa86fd943de0b8b4427ae9328a Mon Sep 17 00:00:00 2001 From: jazelly Date: Fri, 6 Sep 2024 21:00:20 +0930 Subject: [PATCH] lib: prefer iterable weak set than WeakRefs in AbortSignal.any --- lib/internal/abort_controller.js | 57 ++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index ce05571c067398..29aee9e9263a3e 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -4,6 +4,7 @@ // in https://github.com/mysticatea/abort-controller (MIT license) const { + ArrayPrototypePush, ObjectAssign, ObjectDefineProperties, ObjectDefineProperty, @@ -11,8 +12,12 @@ const { SafeFinalizationRegistry, SafeSet, SafeWeakRef, + SafeWeakSet, Symbol, SymbolToStringTag, + WeakRefPrototypeDeref, + WeakSetPrototypeAdd, + WeakSetPrototypeHas, } = primordials; const { @@ -100,6 +105,33 @@ const kComposite = Symbol('kComposite'); const kSourceSignals = Symbol('kSourceSignals'); const kDependantSignals = Symbol('kDependantSignals'); +// Since WeakSet is not iterable, we must use another iterable +// data structure to make it "iterable". +class IterableWeakSet { + #weakSet = new SafeWeakSet(); + #weakRefs = []; + + add(value) { + if (!this.has(value)) { + WeakSetPrototypeAdd(this.#weakSet, value); + ArrayPrototypePush(this.#weakRefs, new SafeWeakRef(value)); + } + } + + has(value) { + return WeakSetPrototypeHas(this.#weakSet, value); + } + + getIterable() { + const iterable = []; + for (let i = 0; i < this.#weakRefs.length; i++) { + const item = WeakRefPrototypeDeref(this.#weakRefs[i]); + ArrayPrototypePush(iterable, item); + } + return iterable; + } +} + function customInspect(self, obj, depth, options) { if (depth < 0) return self; @@ -238,34 +270,32 @@ class AbortSignal extends EventTarget { if (!signalsArray.length) { return resultSignal; } - const resultSignalWeakRef = new SafeWeakRef(resultSignal); - resultSignal[kSourceSignals] = new SafeSet(); + resultSignal[kSourceSignals] = new IterableWeakSet(); for (let i = 0; i < signalsArray.length; i++) { const signal = signalsArray[i]; if (signal.aborted) { abortSignal(resultSignal, signal.reason); return resultSignal; } - signal[kDependantSignals] ??= new SafeSet(); + signal[kDependantSignals] ??= new IterableWeakSet(); if (!signal[kComposite]) { - resultSignal[kSourceSignals].add(new SafeWeakRef(signal)); - signal[kDependantSignals].add(resultSignalWeakRef); + resultSignal[kSourceSignals].add(signal); + signal[kDependantSignals].add(resultSignal); } else if (!signal[kSourceSignals]) { continue; } else { - for (const sourceSignal of signal[kSourceSignals]) { - const sourceSignalRef = sourceSignal.deref(); - if (!sourceSignalRef) { + for (const sourceSignal of signal[kSourceSignals].getIterable()) { + if (!sourceSignal) { continue; } - assert(!sourceSignalRef.aborted); - assert(!sourceSignalRef[kComposite]); + assert(!sourceSignal.aborted); + assert(!sourceSignal[kComposite]); if (resultSignal[kSourceSignals].has(sourceSignal)) { continue; } resultSignal[kSourceSignals].add(sourceSignal); - sourceSignalRef[kDependantSignals].add(resultSignalWeakRef); + sourceSignal[kDependantSignals].add(resultSignal); } } } @@ -380,9 +410,8 @@ function abortSignal(signal, reason) { [kTrustEvent]: true, }); signal.dispatchEvent(event); - signal[kDependantSignals]?.forEach((s) => { - const signalRef = s.deref(); - if (signalRef) abortSignal(signalRef, reason); + signal[kDependantSignals]?.getIterable()?.forEach((s) => { + if (s) abortSignal(s, reason); }); }