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
JavaScript
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;