UNPKG

@jonahsnider/benchmark

Version:

A Node.js benchmarking library with support for multithreading and TurboFan optimization isolation.

76 lines 2.98 kB
import assert from 'node:assert/strict'; import { once } from 'node:events'; import { Worker } from 'node:worker_threads'; import { Suite } from './suite.js'; import { ThreadWorker } from './types/index.js'; import { compatibleImport } from './utils.js'; const WORKER_PATH = new URL('thread-worker.js', import.meta.url); /** * Runs a {@link (Suite:class)} in a separate thread. */ export class Thread { static async init(suiteFilepath) { const suite = await compatibleImport(suiteFilepath); assert.ok(suite instanceof Suite, new TypeError(`Expected "${suiteFilepath}" to export a Suite instance`)); return new Thread(suite, suiteFilepath); } name; filepath; #worker; #workerOptions; constructor(suite, suitePath) { this.name = suite.name; this.filepath = suitePath; const workerData = { suitePath, }; this.#workerOptions = { workerData, }; this.#worker = this.#createWorker(); this.#worker.on('exit', this.#onExit.bind(this)); this.#worker.unref(); } async run(abortSignal) { const runMessage = { kind: ThreadWorker.Message.Kind.Run }; // Worker must be run before an abort signal is sent this.#worker.postMessage(runMessage); const onAbortListener = this.#onAbort.bind(this); abortSignal?.addEventListener('abort', onAbortListener, { once: true }); const message = once(this.#worker, 'message'); try { const [response] = (await message); switch (response.kind) { case ThreadWorker.Response.Kind.Results: { return response.results; } case ThreadWorker.Response.Kind.Error: { // Note: Structured clone algorithm has weird behavior with Error instances - see https://github.com/nodejs/help/issues/1558#issuecomment-431142715 and https://github.com/nodejs/node/issues/26692#issuecomment-658010376 throw response.error; } // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check default: { throw new RangeError('Unknown response kind'); } } } finally { abortSignal?.removeEventListener('abort', onAbortListener); } } #createWorker() { return new Worker(WORKER_PATH, this.#workerOptions); } async #onExit(code) { // This prevents the main thread from hanging when the worker is not `unref`'d await this.#worker.terminate(); // Create a new worker this.#worker = this.#createWorker(); throw new Error(`Worker exited with code ${code}`); } #onAbort() { const abortMessage = { kind: ThreadWorker.Message.Kind.Abort }; this.#worker.postMessage(abortMessage); } } //# sourceMappingURL=thread.js.map