Expand description
Single-owner VM event loop.
rquickjs’s schedular wake queue holds a SINGLE AtomicWaker slot,
re-registered by whichever future last polled the schedular. Any
short-lived async_with! that awaits inside its closure polls the
schedular, steals that slot, and dies with it — after which every
external completion of a schedular task (a backend response resolving
an awaited page.evaluate, an mpsc send into a ctx.spawn pump)
wakes a dead task and the VM never resumes. AsyncRuntime::drive()
is no fix: it contends for the same single slot, is only woken by
new spawns (never by wakes of existing tasks), and crashes under
multi-thread load (SIGSEGV in its lock-release path).
The fix is the architecture every production QuickJS embedding
converges on (LLRT, txiki.js, quickjs-libc, Deno’s isolate loop):
exactly ONE never-completing future owns the VM and its schedular;
everything else sends messages. spawn_vm_loop starts that future
(a single persistent async_with per session VM); VmHandle::with
submits a closure as a job which the loop ctx.spawns onto the
schedular, so jobs run concurrently with each other and with the
loop’s own recv — an execute parked on a host await never blocks a
route/exposeFunction/WS dispatch job arriving behind it. Because the
loop future never completes, every wake — schedular queue slot,
spawner listen, channel recv — always targets a live task.
Structs§
- VmHandle
- Cloneable submission handle to a session’s VM event loop.
- VmShutdown
- Signals the VM event loop to finish. Dropping it (with the session)
makes the loop break out of
recv, so the loop future completes normally and releases itsAsyncContexton its own task — never abort the loop task: tearing theWithFuturedown mid-flight on a foreign thread leaves live GC objects behind and trips QuickJS’sJS_FreeRuntimegc_obj_listassertion.
Functions§
- spawn_
vm_ loop - Spawn the session’s single persistent VM driver. The loop runs until
the returned
VmShutdownis dropped (or everyVmHandleclone is gone); until then the task holds a clone ofctx(keeping the runtime alive) and is the only future that ever polls the runtime’s schedular.
Type Aliases§
- VmJob
- A unit of work executed inside the session’s VM event loop. The
closure runs under the runtime lock on the loop’s execution context;
its future is
ctx.spawned so it interleaves with other jobs.