PrivateworkersPrivateinitPrivateinitializationPrivatereadyPrivatesettleSettles readyPromise: no argument resolves, an error rejects.
Private ReadonlyinRejectors of the calls in flight on each worker. A terminated worker never
replies, and Comlink settles a call only from the reply, so eviction and
dispose() reject these instead of leaving the callers parked forever.
Private ReadonlydispatchWorker-side in-flight accounting for the worker.* perf counters only.
Private ReadonlycodecLazy, one-worker-first blosc codec warm-up (see worker-pool/codec-warmup.ts).
PrivateinitPrivatependingPrivategetGet the configured worker count, capped by hardware concurrency
Initialize worker pool (lazy initialization)
PrivatelogOne-time backend summary (replaces the per-worker WASM/TS lines). Workers each load WASM independently but share one build, so the pool is either all-WASM or all-fallback in practice; report a mixed result honestly if that invariant ever breaks.
PrivatescheduleRespawn a slot whose worker missed its init deadline, after a backoff and
at most WORKER_INIT_MAX_ATTEMPTS attempts in all. A deadline miss
does not prove the worker broken (see WorkerInitTimeoutError); every
other init failure is permanent and is not retried.
The respawn runs outside the attempt it came from, so initialize() and
the usable-worker gate settle exactly as before. A respawned worker is
published like any other (generation-checked), which also revives a pool
whose every slot missed: whenUsable() takes a live worker over the
attempt's sticky rejection.
PrivatewhenResolve as soon as the pool has ONE usable worker, instead of all of them.
The hot path's contract. initialize() keeps the all-settled one, so
anything that genuinely wants a full pool (or wants to observe the final
worker count) should keep awaiting that.
PrivateinitializeRace api.initialize() against (1) a hard init timeout and (2) the
worker's own onerror/onmessageerror events.
The pool's permanent attachWorkerErrorHandlers evicts a failed
worker via handleWorkerFailure, but during pool init the worker
isn't in this.workers yet — so handleWorkerFailure finds
nothing to evict and the dangling Comlink initialize() promise
never settles. (Repro: route-block the worker script in a
Playwright test; the page hangs at boot.)
We attach a short-lived listener that rejects the init promise when the worker fails before it joins the pool. The init timeout is the belt-and-suspenders fallback — even if no error event fires (e.g. a network stall that never resolves), we eventually reject and let the caller fall back to the main thread.
OptionalwasmPath: stringPrivateattachInstall onerror and onmessageerror handlers on a freshly-created
worker so a crash inside the worker (uncaught throw, OOM during WASM
init, unserializable Comlink message) surfaces as a logged failure
and is removed from the active pool, rather than escaping to the
browser's window.onerror. The eviction rejects the calls still in
flight on it (see handleWorkerFailure).
PrivatehandleRemove a failed worker from the pool and terminate it, rejecting every
call still in flight on it with WorkerUnavailableError (the
terminated worker will never answer them; runWithTimeout re-dispatches
such a call once onto a surviving worker). If this drops the pool to zero
workers, log a clear error so the surrounding application can decide
whether to fall back to the main-thread implementation or surface a
failure dialog.
Race a worker-routed Promise against a timeout. On timeout, log
the failure, evict the responsible worker (if known) via
handleWorkerFailure, and reject with
WorkerTimeoutError. A timeoutMs of 0 disables the
timeout — callers that don't need it can still use this helper as
a thin pass-through to keep the call-site uniform.
worker may be omitted when the caller cannot identify which
worker handled the call; the timeout still fires, but the pool isn't
pruned.
Optionalworker: WorkerPrivatepickPick the kind-appropriate timeout from config. Projection AND
decode share workerProjectionTimeoutMs (both are long-running
CPU-bound calls — a dedicated decode knob can be added later if
telemetry shows a need).
Run a worker call through withTimeout on a least-busy
worker. The single production entry point for any Comlink-routed call
that needs a hang-detection guard — direct await against a worker
Remote has none, and a worker that is stuck rather than dead is never
evicted, so nothing would settle the call.
On timeout the responsible worker is evicted via
handleWorkerFailure and the caller receives a
WorkerTimeoutError. The geometry loaders deliberately do NOT
run the projection in-process on a timeout (see
isWorkerInfrastructureError) — the failure is recorded and the node
retried against a fresh worker instead of blocking the UI thread. The
OTHER calls that eviction abandons were never answered, so they are
re-dispatched once onto a surviving worker (see
dispatchWithRedispatch); only with no worker left do they reject
with WorkerUnavailableError, the in-process fallback's trigger.
Optionalsignal: AbortSignalPrivateacquireSelect the least-busy worker AND mark its slot busy in one synchronous step after the usability await. Marking later (after the caller resumes) let every concurrent dispatch read the same worker as idle.
PrivatedispatchStart fn on an already-acquired worker and release its slot when the
WORKER'S OWN (timeout-guarded) promise settles — not when the caller's
abort race settles. An aborted caller walks away, but the worker keeps
running the abandoned task; freeing the slot early made least-busy
routing send the next dispatch to a busy worker while others idled. The
release path swallows the rejection (the caller observes it through the
returned promise, or has already moved on after an abort).
PrivatedispatchdispatchTracked, plus ONE re-dispatch of a call that its worker's eviction abandoned (a crash, or ANOTHER call's timeout on that worker) while live workers remain. No kernel answered such a call — it was queued behind the failure, or running on a worker that died — so it runs again on a surviving worker, under a fresh timeout, instead of rejecting with WorkerUnavailableError into the callers' in-process fallback, which would move the same heavy work onto the UI thread.
Never re-dispatched: a call that timed out itself (it rejects with
WorkerTimeoutError), a call whose caller aborted, a call already
re-dispatched once (a second eviction rejects, so a payload that kills
workers cannot walk the pool), and a call abandoned by dispose() or by
the last worker's eviction (nothing is left to run it; that rejection is
the infrastructure failure the fallback exists for).
Sound because runWithTimeout callers structured-clone their inputs; a
transferred payload is detached by the first post, which is why
runDecode does not re-dispatch.
Optionalsignal: AbortSignalPrivatesettlecall raced against the eviction of worker: settles with call, or
rejects with WorkerUnavailableError once the worker is evicted or
the pool disposed while the call is still in flight.
PrivaterejectReject every call in flight on worker and forget the worker.
Run a chunk-decode task on the least-busy WARM worker (see
CodecWarmup.isWarm; none warm rejects, and the codec decodes on the main
thread instead), timeout-guarded but NOT
raced against any dataset abort signal: a decode is a few milliseconds
of work whose result the L0 cache keeps, and abandoning it on a dataset
switch would only make the codec fall back to decoding it on the main
thread. Used by the blosc codec dispatcher (worker-pool/codec-dispatch.ts).
Idle workers whose codec is warm (the codec dispatcher spreads batches over these).
True when a worker's blosc codec is warm, so a chunk decode may be
offloaded. Otherwise warms ONE worker (the others follow once it finished,
reading the codec chunk from the HTTP cache) and returns false: decode on
the main thread meanwhile. Never warms under ?mainThreadCodecs, and a
warm-up never counts as a query. Called by the codec dispatcher for each
chunk above its offload floor, so a store with no such chunk never makes a
worker download its codec.
Get the number of active workers
Get pool statistics for monitoring
Convenience accessor for the aggregate "in-flight task" count.
Exposed in __luxarDebug.workers.queueDepth for live diagnostics
of prefetch backpressure / dataset-switch task accumulation.
The number of worker tasks currently in flight across the
pool (sum of activeQueries per worker). Zero when the pool is
idle.
Check if worker pool is initialized
Whether this pool is still completing its initial worker handshakes.
Clean up all worker resources.
Also terminates workers still pending init (via pendingWorkers)
and bumps initGeneration so any factories still in flight detect
the dispose and self-terminate when they resolve.
"At least ONE worker is usable" gate, settled as soon as the first worker is published — rejected only when the attempt ends with none.
Distinct from initPromise, which keeps its all-settled meaning. The hot path awaits THIS one: a 1024-splat first LOD rung has no use for workers 2..N, and waiting for them cost 2.23 s of a measured 8.0 s first paint (workers become ready ~160 ms apart as each compiles its own copy of the WASM module).
Mirrors
initPromise's deliberate "stays rejected" contract — it is NOT nulled on failure, so a poisoned pool keeps failing fast instead of re-running the init guard per call.dispose()and the'pool-empty'branch of handleWorkerFailure are the only resets.