From 445b30dac77300bc6cd63834d0513306df0836b1 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:32:02 +0300 Subject: [PATCH 1/2] lib: improve perf of `AbortSignal` creation --- lib/internal/abort_controller.js | 75 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index b10fb35a1a6e4f..2d9aabb2c35d3e 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -6,7 +6,6 @@ const { ObjectAssign, ObjectDefineProperties, - ObjectSetPrototypeOf, ObjectDefineProperty, PromiseResolve, SafeFinalizationRegistry, @@ -69,6 +68,8 @@ const { let _MessageChannel; let markTransferMode; +const kDontThrowSymbol = Symbol('kDontThrowSymbol'); + // Loading the MessageChannel and markTransferable have to be done lazily // because otherwise we'll end up with a require cycle that ends up with // an incomplete initialization of abort_controller. @@ -137,8 +138,35 @@ function setWeakAbortSignalTimeout(weakRef, delay) { } class AbortSignal extends EventTarget { - constructor() { - throw new ERR_ILLEGAL_CONSTRUCTOR(); + + /** + * @param {symbol | undefined} dontThrowSymbol + * @param {{ + * aborted? : boolean, + * reason? : any, + * transferable? : boolean, + * composite? : boolean, + * }} [init] + * @private + */ + constructor(dontThrowSymbol = undefined, init = kEmptyObject) { + if (dontThrowSymbol !== kDontThrowSymbol) { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + super(); + + const { + aborted = false, + reason = undefined, + transferable = false, + composite = false, + } = init; + this[kAborted] = aborted; + this[kReason] = reason; + this[kComposite] = composite; + if (transferable) { + lazyMarkTransferMode(this, false, true); + } } /** @@ -176,7 +204,7 @@ class AbortSignal extends EventTarget { */ static abort( reason = new DOMException('This operation was aborted', 'AbortError')) { - return createAbortSignal({ aborted: true, reason }); + return new AbortSignal(kDontThrowSymbol, { aborted: true, reason }); } /** @@ -185,7 +213,7 @@ class AbortSignal extends EventTarget { */ static timeout(delay) { validateUint32(delay, 'delay', false); - const signal = createAbortSignal(); + const signal = new AbortSignal(kDontThrowSymbol); signal[kTimeout] = true; clearTimeoutRegistry.register( signal, @@ -199,7 +227,7 @@ class AbortSignal extends EventTarget { */ static any(signals) { validateAbortSignalArray(signals, 'signals'); - const resultSignal = createAbortSignal({ composite: true }); + const resultSignal = new AbortSignal(kDontThrowSymbol, { composite: true }); if (!signals.length) { return resultSignal; } @@ -319,7 +347,7 @@ class AbortSignal extends EventTarget { } function ClonedAbortSignal() { - return createAbortSignal({ transferable: true }); + return new AbortSignal(kDontThrowSymbol, { transferable: true }); } ClonedAbortSignal.prototype[kDeserialize] = () => {}; @@ -337,33 +365,6 @@ ObjectDefineProperty(AbortSignal.prototype, SymbolToStringTag, { defineEventHandler(AbortSignal.prototype, 'abort'); -/** - * @param {{ - * aborted? : boolean, - * reason? : any, - * transferable? : boolean, - * composite? : boolean, - * }} [init] - * @returns {AbortSignal} - */ -function createAbortSignal(init = kEmptyObject) { - const { - aborted = false, - reason = undefined, - transferable = false, - composite = false, - } = init; - const signal = new EventTarget(); - ObjectSetPrototypeOf(signal, AbortSignal.prototype); - signal[kAborted] = aborted; - signal[kReason] = reason; - signal[kComposite] = composite; - if (transferable) { - lazyMarkTransferMode(signal, false, true); - } - return signal; -} - function abortSignal(signal, reason) { if (signal[kAborted]) return; signal[kAborted] = true; @@ -385,7 +386,7 @@ class AbortController { * @type {AbortSignal} */ get signal() { - this.#signal ??= createAbortSignal(); + this.#signal ??= new AbortSignal(kDontThrowSymbol); return this.#signal; } @@ -393,7 +394,7 @@ class AbortController { * @param {any} [reason] */ abort(reason = new DOMException('This operation was aborted', 'AbortError')) { - abortSignal(this.#signal ??= createAbortSignal(), reason); + abortSignal(this.#signal ??= new AbortSignal(kDontThrowSymbol), reason); } [customInspectSymbol](depth, options) { @@ -404,7 +405,7 @@ class AbortController { static [kMakeTransferable]() { const controller = new AbortController(); - controller.#signal = createAbortSignal({ transferable: true }); + controller.#signal = new AbortSignal(kDontThrowSymbol, { transferable: true }); return controller; } } From 4eaacb47a4892b88be1fb28833de6da60200fa49 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:49:56 +0300 Subject: [PATCH 2/2] benchmark: add AbortSignal.abort benchmarks --- .../abort-signal-static-abort.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 benchmark/abort_controller/abort-signal-static-abort.js diff --git a/benchmark/abort_controller/abort-signal-static-abort.js b/benchmark/abort_controller/abort-signal-static-abort.js new file mode 100644 index 00000000000000..2b1c884a260780 --- /dev/null +++ b/benchmark/abort_controller/abort-signal-static-abort.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + n: [5e6], + kind: ['default-reason', 'same-reason'], +}); + +function main({ n, kind }) { + switch (kind) { + case 'default-reason': + bench.start(); + for (let i = 0; i < n; ++i) + AbortSignal.abort(); + bench.end(n); + break; + case 'same-reason': { + const reason = new Error('same reason'); + + bench.start(); + for (let i = 0; i < n; ++i) + AbortSignal.abort(reason); + bench.end(n); + break; + } + default: + throw new Error('Invalid kind'); + } +}