Creating pools
createPool(options)(tasks) starts worker threads and returns helpers:
call.<task>(args)enqueue a task and return a promise.shutdown(delayMs?)stop workers immediately or after an optional delay.
call.*() always returns a promise.
Inputs may also be native promises; they are resolved on the host before
dispatch. If an input promise rejects, the host call rejects and the worker
task is not executed.
See Promise inputs and awaited outputs.
Batching pattern
Section titled “Batching pattern”call.*() enqueues work and dispatches automatically.
For batches, create all calls first and then await them together.
const jobs = Array.from({ length: 1_000 }, () => call.hello());const results = await Promise.all(jobs);Options
Section titled “Options”createPool({ threads?: number, inliner?: { position?: "first" | "last", batchSize?: number, dispatchThreshold?: number, }, balancer?: { strategy?: | "roundRobin" | "robinRound" | "firstIdle" | "randomLane" | "firstIdleOrRandom" } | "roundRobin" | "robinRound" | "firstIdle" | "randomLane" | "firstIdleOrRandom", worker?: { resolveAfterFinishingAll?: true, timers?: { spinMicroseconds?: number, parkMs?: number, pauseNanoseconds?: number, }, hardTimeoutMs?: number, resourceLimits?: { maxOldGenerationSizeMb?: number, maxYoungGenerationSizeMb?: number, codeRangeSizeMb?: number, stackSizeMb?: number, }, }, payload?: { mode?: "growable" | "fixed", payloadInitialBytes?: number, payloadMaxByteLength?: number, maxPayloadBytes?: number, }, abortSignalCapacity?: number, host?: { stallFreeLoops?: number, maxBackoffMs?: number, }, workerExecArgv?: string[], permission?: "strict" | "unsafe" | PermissionProtocol, dispatcher?: DispatcherSettings, // deprecated alias of host debug?: { extras?: boolean, logMain?: boolean, logHref?: boolean, logImportedUrl?: boolean, }, source?: string,})Deprecated payload aliases are still accepted at the top level:
payloadInitialBytes->payload.payloadInitialBytespayloadMaxBytes->payload.payloadMaxByteLengthbufferMode->payload.modemaxPayloadBytes->payload.maxPayloadBytes
threads
Section titled “threads”Number of worker threads to spawn (default 1). The total lane count is
threads + (inliner ? 1 : 0).
payload
Section titled “payload”Payload transport tuning lives under payload.
Selects shared-buffer transport mode:
"growable": growable shared array buffer mode."fixed": fixed-size shared array buffer mode.
Default is "growable" when SAB growth is available, otherwise "fixed".
payloadMaxByteLength
Section titled “payloadMaxByteLength”Maximum size (in bytes) each payload buffer may grow to. Default is
64 MiB.
payloadInitialBytes
Section titled “payloadInitialBytes”Initial payload buffer size in bytes. Default is 4 MiB in growable mode.
In fixed mode, startup uses the full payloadMaxByteLength.
maxPayloadBytes
Section titled “maxPayloadBytes”Hard cap for dynamic payload encoding. Must be > 0 and
<= payloadMaxByteLength >> 3.
Default is payloadMaxByteLength >> 3 (8 MiB with defaults).
Calls that exceed this cap are rejected with KNT_ERROR_3 before dynamic slot
reservation.
If payload.mode is "fixed" and payload size is under the cap but still does
not fit current capacity, the call is rejected with a controlled encoder error.
Payload limits apply per worker and per direction (request payload + return payload), so each worker allocates two payload buffers with these limits.
abortSignalCapacity
Section titled “abortSignalCapacity”Maximum number of concurrent abort-aware calls the pool can track.
Default is 258, and it applies only when at least one task declares
abortSignal.
Only tasks defined with abortSignal: true or
abortSignal: { hasAborted: true } count against this limit.
const pool = createPool({ threads: 4, abortSignalCapacity: 1024,})({ myAbortableTask });Example
Section titled “Example”Basic example:
import { createPool, isMain, task } from "@vixeny/knitting";
export const add = task<[number, number], number>({ f: async ([a, b]) => a + b,});
const { call, shutdown } = createPool({ threads: 2,})({ add });
if (isMain) { const jobs = [call.add([1, 2]), call.add([3, 4])]; const results = await Promise.all(jobs); console.log(results); await shutdown();}balancer
Section titled “balancer”Controls how calls are routed across lanes (threads, plus optional inliner).
Pass a string or an object with a strategy key.
roundRobin(default): round-robin rotation through all lanes.robinRound: legacy alias ofroundRobin.firstIdle: pick the first idle lane, else fall back to round-robin.randomLane: pick a random lane.firstIdleOrRandom: pick the first idle lane, else random.
When only one thread is spawned and no inliner is enabled, the balancer is bypassed and calls go directly to the single worker.
inliner
Section titled “inliner”Adds an extra lane that runs tasks on the main thread.
position: whether the inline lane appears before ("first") or after ("last") the worker lanes for balancing.batchSize: max tasks processed per event-loop tick (default1when enabled).dispatchThreshold: minimum in-flight calls per invoker before inline lane is eligible (default1).
See Inliner guide for detail.
worker
Section titled “worker”resolveAfterFinishingAll
Section titled “resolveAfterFinishingAll”When set to true, workers wait for all pending promises to settle before
exiting.
timers
Section titled “timers”Idle behavior tuning while workers have no work:
spinMicroseconds: busy-spin budget before parking.parkMs:Atomics.waittimeout while parked.pauseNanoseconds:Atomics.pauseduration while spinning. Set0to disable.
hardTimeoutMs
Section titled “hardTimeoutMs”Hard wall-clock timeout for each task call. On timeout, the pool force-shuts down to stop runaway CPU execution.
resourceLimits
Section titled “resourceLimits”Node.js worker memory/stack limits:
maxOldGenerationSizeMbmaxYoungGenerationSizeMbcodeRangeSizeMbstackSizeMb
workerExecArgv
Section titled “workerExecArgv”Extra Node.js execArgv flags passed to workers, for example
["--expose-gc", "--max-old-space-size=4096"].
When permission is set to "unsafe", inherited Node permission flags
(--allow-fs-read, --allow-fs-write, etc.) are stripped.
permission
Section titled “permission”Runtime permission flag policy for workers.
- Omit
permission: strict defaults plusallowImport: true(web imports allowed). "strict"(default when passing an object): computes conservative defaults."unsafe": disables permission flags and strips inherited Node permission flags.- In object mode,
consoledefaults tofalsein strict mode andtruein unsafe mode.
See Permissions guide for runtime-specific mapping and strict defaults.
Timing note
Section titled “Timing note”Worker timing paths capture a high-resolution performance.now() reference at
module load/startup time. This keeps scheduling and timeout precision stable
without freezing global performance.
Safety hardening defaults
Section titled “Safety hardening defaults”- Startup-only guard layer: safety hooks are installed once before the worker loop starts (no extra checks inside the hot task-processing loop).
- Process termination APIs from task code are blocked:
process.exit,process.kill,process.abort, plusDeno.exitwhen present. - Permission enforcement is delegated to runtime-native mechanisms (Node worker permission flags and Deno worker permissions when available). In-process FS/network/env monkey-patching is not performed.
Host dispatcher backoff and scheduling options.
stallFreeLoops
Section titled “stallFreeLoops”How many notify loops run before backoff starts (default 128).
maxBackoffMs
Section titled “maxBackoffMs”Maximum backoff delay in milliseconds once the dispatcher starts stalling
(default 10).
dispatcher
Section titled “dispatcher”Deprecated alias of host.
extras: extra warnings (for example, accidentalcreatePoolin a worker).logMain: signal logging on the main thread (writes under./log/).logHref: log worker entry module URL.logImportedUrl: log module URLs imported by workers for task discovery.
source
Section titled “source”Override worker entry module URL/path explicitly.
Limits
Section titled “Limits”A single pool supports up to 65,536 tasks (function IDs are stored as
Uint16, range 0..0xFFFF). Passing more tasks throws a RangeError.