UNPKG

multithreading

Version:

The missing standard library for multithreading in JavaScript (Works in the browser, Node.js, Deno, Bun).

91 lines (90 loc) 2.71 kB
import { getCallerLocation } from "./caller_location.js"; import { patchDynamicImports } from "./patch_import.js"; import { WorkerPool } from "./pool.js"; import { checkMoveArgs } from "./check_move_args.js"; export * from "./sync/mod.js"; export { SharedJsonBuffer } from "./json_buffer.js"; let globalPool = null; let globalConfig = { maxWorkers: navigator.hardwareConcurrency || 4 }; let functionIdCounter = 0; const globalFunctionRegistry = new Map(); export function initRuntime(config) { if (globalPool) throw new Error("Runtime already initialized"); globalConfig = { ...globalConfig, ...config }; } function getPool() { if (!globalPool) { globalPool = new WorkerPool(globalConfig.maxWorkers); } return globalPool; } /** * A branded type that ensures the array has been explicitly marked * by the move() function. */ const moveTag = Symbol("Thread.move"); export function move(...args) { checkMoveArgs(args); return Object.defineProperty(args, moveTag, { enumerable: false, configurable: false, writable: false, value: true, }); } export function drop(resource) { resource[Symbol.dispose](); } export function spawn(arg1, arg2) { const pool = getPool(); const { resolve, reject, promise } = Promise.withResolvers(); let args = []; let fn; // Argument parsing if (arg1 && Object.prototype.hasOwnProperty.call(arg1, moveTag)) { args = arg1; fn = arg2; } else { fn = arg1; } const stringified = fn.toString(); let meta = globalFunctionRegistry.get(stringified); if (!meta) { const id = functionIdCounter++; const callerLocation = getCallerLocation(); const code = patchDynamicImports("export default " + stringified, callerLocation.filePath); meta = [id, code]; globalFunctionRegistry.set(stringified, meta); } // Task submission (async () => { try { const task = [ meta[0], meta[1], args, ]; const val = await pool.submit(task); resolve({ ok: true, value: val }); } catch (err) { resolve({ ok: false, error: err }); } })(); return { join: () => promise, abort: () => reject(new Error("Task aborted")), }; } export function shutdown() { if (globalPool) { globalPool.terminate(); globalPool = null; } } const isWorker = typeof globalThis.WorkerGlobalScope !== "undefined" && self instanceof globalThis.WorkerGlobalScope; export const isMainThread = !isWorker; export const isWorkerThread = isWorker;