Spawn, Temporización y Cancelación
En esta página
spawn registra una tarea en el planificador cooperativo — no se ejecuta de
inmediato. El planificador alterna entre tareas listas cuando el llamador invoca
tick(), every, after, o al finalizar el ámbito.
La llamada tick() es equivalente a coroutine.yield(0): devuelve el control
al planificador para que otra tarea lista pueda ejecutarse. En bucles largos,
insertar tick() es obligatorio para no monopolizar la CPU.
Dos tarefas spawn alternando con tick(); productor/consumidor via cola compartida.
// Feature: spawn + tick — parallel cooperative tasks
// Syntax: `spawn { ... }` or `spawn fn_call(...)`; `tick()` yields control.
// When to use: single-threaded cooperative parallelism for
// simulations, games, animations, lightweight agents.
//
// The internal scheduler drains each task. `tick()` is equivalent to
// `coroutine.yield(0)` — hands control back to the scheduler so it
// picks another ready task. Without `tick()`, a task hogs the CPU.
fn counter(id: int, max: int) {
var n = 0
while n < max {
print("counter {id}: {n}")
n += 1
tick()
}
}
print("=== spawn + named function ===")
spawn counter(1, 3)
spawn counter(2, 3)
// `spawn { ... }` — anonymous block, direct.
print("=== spawn block ===")
spawn {
var i = 0
while i < 3 {
print("block A: {i}")
i += 1
tick()
}
}
spawn {
var i = 0
while i < 3 {
print("block B: {i}")
i += 1
tick()
}
}
// Simple producer/consumer with a shared queue.
let queue: [int] = []
var produced = 0
var consumed = 0
spawn {
var i = 0
while i < 5 {
queue.push(i)
produced += 1
tick()
i += 1
}
}
spawn {
while consumed < 5 {
if queue.len() > 0 {
let v = queue.shift()
consumed += 1
print("consumed: {v}")
}
tick()
}
}
// The scheduler drains automatically once the top-level finishes.
Para programar trabajo en el tiempo, usa every <duración> { ... } (bucle
periódico) y after <duración> { ... } (ejecución única diferida). Ambos son
azúcar para spawn + loop + tick(<dur>) y pueden coexistir en paralelo.
Game loop con every 50ms, disparo único con after, y dos every paralelos a ritmos distintos.
// Feature: every / after — cooperative temporal loops
// Syntax: `every <duration> { body }`, `after <duration> { body }`
// When to use: games (game loop), animations, polling, lightweight
// scheduling without needing OS threads.
//
// `every` is sugar for `spawn + loop + tick(<dur>)`. `after` schedules
// the body to run a single time after the duration.
// Simple game loop: tick every 50ms until 5 frames.
var frame = 0
every 50ms {
frame += 1
print("frame {frame}")
if frame >= 5 {
break
}
}
// `after` — single deferred execution.
print("before after")
after 10ms {
print("this runs 10ms later")
}
// `every` without a duration = on every tick (most frequent).
var i = 0
every {
i += 1
print("fast tick {i}")
if i >= 3 {
break
}
}
// Combining: two `every` loops in parallel.
var a = 0
var b = 0
every 30ms {
a += 1
print("A {a}")
if a >= 3 { break }
}
every 50ms {
b += 1
print("B {b}")
if b >= 3 { break }
}
Como el modelo es cooperativo, no existe task.kill(). La cancelación se
realiza mediante una flag compartida que la tarea comprueba en cada tick().
Este patrón garantiza que el estado nunca se corrompa a mitad de camino.
Cancelación por flag y patrón de plazo con after señalando un centinela.
// Feature: cooperative cancellation of tasks
// Syntax: shared flag/sentinel + check on every `tick()`.
// When to use: long tasks that need to stop when the user
// cancels, the window closes, or a deadline is reached.
//
// Since spawn is cooperative, cancellation is too: the task must
// ASK whether it was canceled. There is no forced kill — that is a
// feature, not a bug (avoids corrupted state).
let cancel = #{requested: false}
fn long_task(id: int) {
var step = 0
while step < 1000 {
if cancel.requested {
print("task {id} canceled at step {step}")
return
}
if step % 3 == 0 {
print("task {id}: step {step}")
}
step += 1
tick()
}
print("task {id} finished normally")
}
spawn long_task(1)
spawn long_task(2)
// After a few iterations, signal cancellation.
var cycles = 0
every 10ms {
cycles += 1
if cycles >= 3 {
cancel.requested = true
print(">> cancellation requested")
break
}
}
// Deadline pattern: cancel after X ms.
let deadline = #{expired: false}
spawn {
var n = 0
while n < 1000 {
if deadline.expired {
print("deadline reached after {n} steps")
return
}
n += 1
tick()
}
}
after 50ms {
deadline.expired = true
}
Desafío
Añade una segunda flag paused al ejemplo de cancelación. La tarea debe dejar
de imprimir mientras paused sea true, pero reanudar al ser reactivada.