Reactor de I/O (std::io_runtime)
En esta página
std::io_runtime da acceso al reactor de I/O de Zolo — el mismo bucle de
eventos que sirve conexiones TCP, lecturas de archivo asíncronas y otros I/O.
En Linux >= 5.19 usa io_uring; en macOS, Windows y kernels más antiguos cae en
el backend fallback(thread-park). Como depende del runtime del host, no se
puede ejecutar en el sandbox WASM; usa la CLI de Zolo.
Para una pausa simple sin otros I/O en vuelo, process.sleep(ms) es más
directo. Usa io_runtime cuando el programa ya está conduciendo el reactor y
quieres que el timer conviva con los demás I/O en el mismo bucle.
Timer de una vez: sleep_ms
io_runtime.sleep_ms(ms) programa un timer de disparo único y bloquea hasta
que dispara, cediendo el control al reactor (sin busy spin). io_runtime.backend()
informa qué implementación fue seleccionada en tiempo de ejecución:
Verifica el backend y mide el tiempo de un sleep_ms(100) con os.clock.
// Feature: io_runtime.sleep_ms — reactor-backed timer
// Syntax: io_runtime.sleep_ms(ms: int)
// When to use: when the program is already driving the zolo-io reactor
// (TCP server, file IO, other timers) and you want the same loop turn
// to deliver this sleep alongside whatever else is in flight. For a
// pure pause with no other IO, `process.sleep` / `scheduler.sleep`
// (which use OS thread::sleep) are simpler.
use std::io_runtime
use std::os
// Confirm which backend the runtime resolved to. On Linux ≥ 5.19 you
// see "zolo-io/uring(multishot-accept+timerfd+eventfd)"; on macOS,
// Windows, and older Linux kernels you see "zolo-io/fallback(thread-park)".
print("backend: {io_runtime.backend()}")
// Schedule a one-shot timer for 100ms and block until it fires. The
// reactor handles the actual wait via timerfd (Linux) or condvar
// (fallback) — this Lua-side loop is not a busy spin.
let start = os.clock()
io_runtime.sleep_ms(100)
let elapsed_ms = (os.clock() - start) * 1000.0
print("elapsed: {elapsed_ms}ms") // expected: ~100 (±10ms wakeup overhead)
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
Timers periódicos: every_ms, wait_next y cancel
io_runtime.every_ms(interval, tag) arma un timer recurrente identificado por
una etiqueta numérica. io_runtime.wait_next() bloquea mediante el reactor y
devuelve el próximo evento { kind, user_data }. Múltiples timers comparten el
mismo bucle; los disparos que llegan juntos se encolan para no perderse.
Cancela siempre con io_runtime.cancel(handle) al salir del ámbito:
Dos timers concurrentes (50 ms y 150 ms); recolecta 6 eventos y cancela ambos.
// Feature: io_runtime.every_ms + wait_next + cancel — recurring timers
// When to use: build a simple event loop with multiple periodic timers
// (heartbeat, metrics flush, watchdog) without spawning OS threads.
// The reactor batches all fires into one `io_uring_enter` syscall on
// Linux; the fallback uses a condvar park.
use std::io_runtime
// Two concurrent timers — distinguish via the optional `tag` arg which
// is echoed in each event's `user_data` field.
let fast = io_runtime.every_ms(50, 1)
let slow = io_runtime.every_ms(150, 2)
var fires = 0
var fast_count = 0
var slow_count = 0
// Pull events one at a time. wait_next() blocks via the reactor, so
// the loop is not a busy spin. Events that arrive in the same kernel
// batch are queued internally and handed back on successive calls so
// concurrent timer fires never silently disappear.
while fires < 6 {
let ev = io_runtime.wait_next()
if ev.kind == "timer" {
if ev.user_data == 1 {
fast_count += 1
}
if ev.user_data == 2 {
slow_count += 1
}
}
fires += 1
}
// Always cancel timers before leaving the scope — the reactor would
// keep them armed otherwise. Idempotent / safe to call twice.
io_runtime.cancel(fast)
io_runtime.cancel(slow)
print("fast fires: {fast_count}") // expected: ~5 (50ms × 5 < 300ms)
print("slow fires: {slow_count}") // expected: ~1-2 (150ms × 2 ≈ 300ms)
print("total events: {fires}") // expected: 6
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
Desafío
Agrega un tercer timer de 200 ms con etiqueta 3 y ajusta el bucle para
recolectar 10 eventos. ¿Cuántos disparos del timer lento esperas?
Consulta también