Saltar al contenido

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.

04-spawn-tick.zolo
Playground
// 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.

05-every-after.zolo
Playground
// 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.

08-cancellation.zolo
Playground
// 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.

Buscar en Zolo

9 resultados

enespt-br