Skip to content

Commit

Permalink
lib: prefer iterable weak set than WeakRefs in AbortSignal.any
Browse files Browse the repository at this point in the history
  • Loading branch information
jazelly committed Sep 14, 2024
1 parent d64835f commit 2a7a07f
Showing 1 changed file with 43 additions and 14 deletions.
57 changes: 43 additions & 14 deletions lib/internal/abort_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
// in https://github.com/mysticatea/abort-controller (MIT license)

const {
ArrayPrototypePush,
ObjectAssign,
ObjectDefineProperties,
ObjectDefineProperty,
PromiseResolve,
SafeFinalizationRegistry,
SafeSet,
SafeWeakRef,
SafeWeakSet,
Symbol,
SymbolToStringTag,
WeakRefPrototypeDeref,
WeakSetPrototypeAdd,
WeakSetPrototypeHas,
} = primordials;

const {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
});
}

Expand Down

0 comments on commit 2a7a07f

Please sign in to comment.