pub fn run_event_loop() -> Result<(), String>Expand description
Drive the event loop to quiescence.
Liveness is Node’s handle count: the loop runs while a microtask is
pending, an open handle is registered (a listening server, a live socket, an
in-flight async op), or a referenced timer is still pending. That last term
is what makes setInterval(fn, 1000) hold the process open forever, as it
does in Node — the interval re-arms itself, so a ref’d timer is always
pending and the loop never reaches its exit condition.
Two clock regimes, selected per iteration:
-
Virtual clock (no open handles and no repeating timer): the original deterministic path — fire the earliest
(delay, seq)timer immediately, no real waiting. Parity output and test speed for ordinarysetTimeoutscripts are unchanged. -
Real clock (an open handle, or any pending interval): fire every timer whose wall-clock deadline has passed, then BLOCK on the I/O channel (
recv_timeoutbounded by the next deadline, or unboundedrecvif no timers) and run the receivedIoTaskon the main thread. The host keeps its ownSender, sorecvnever disconnects while the process should stay alive.A repeating timer must take this path: virtual time never advances, so an interval on the virtual clock would re-fire at the same instant forever, spinning a core and starving every longer-delay timer behind it.
Errors thrown by a task/timer/I/O dispatch abort the loop (uncaught → surfaced).