Synsema docsENES

Cron

Un scheduler de background integrado. Cada job corre en su propio thread, sin bloquear, y ejecuta su task de verdad — los contadores de cron_list() reflejan ejecuciones reales.

cron.syn
-- Doc example: cron scheduler. Jobs run on background threads and EXECUTE their
-- task for real — the doctest asserts the observable effect, not just registration.
intent: "doc example: cron"
require time
require file("_doctest_cron.txt")

task write_marker()
    write_file("_doctest_cron.txt", "cron ran")

print("scheduled jobs: " + text(length(cron_list())))

test "a scheduled job executes its task and the counters tell the truth"
    cron_after(0.2, write_marker)
    sleep(0.8)
    assert_eq(read_file("_doctest_cron.txt"), "cron ran")
    assert_eq(length(cron_list()), 1)
    each j in cron_list()
        assert_eq(j["name"], "write_marker")
        assert_eq(j["run_count"], 1)
        assert_eq(j["errors"], 0)

Programar

task sync_inventory()
    let data be http_get("https://api.warehouse.com/stock")
    share data as "inventory"

cron_every(300, sync_inventory)     -- cada 5 minutos
cron_after(3600, send_reminder)     -- una vez, después de 1 hora

El task debe ser de 0 parámetros y estar definido en el top-level (el job lo ejecuta por nombre). Un task con parámetros obligatorios falla en la registración con un error claro — envolvelo en un task sin argumentos. cron_every exige un intervalo positivo; cron_after acepta delay 0 (ejecuta ya mismo). El argumento task es la referencia (sync_inventory) o su nombre como texto ("sync_inventory"); ambos builtins devuelven el nombre del job (texto), que es lo que toma cron_cancel(nombre).

Gestionar

cron_cancel("sync_inventory")       -- detener un job
let jobs be cron_list()             -- listar todos los jobs
print(cron_status())                -- estado formateado

Cada entrada de cron_list() trae name, interval, repeating, active, run_count (ejecuciones completadas) y errors (ticks que terminaron en error). Registrar un job con el mismo nombre reemplaza al anterior (los contadores arrancan de cero).

Semántica

Bajo serve: estado compartido

Bajo synsema serve, los jobs corren con el mismo estado compartido y las mismas capabilities que las rutas: db, state_*, memoria, blackboard. Los jobs del top-level arrancan recién cuando el server ya está sirviendo, y un cron_every registrado desde una ruta funciona y es visible globalmente.

task tick()
    state_incr("latidos", 1)

cron_every(60, tick)

serve on 8080
    route "GET /salud"
        give state_get("latidos")

Mantener los jobs vivos

Bajo run, los jobs ejecutan mientras el programa viva y se detienen cuando termina. Los jobs comparten estado entre ellos (un job puede leer lo que otro escribió con state_* o remember). Para intercambiar datos con el resto del programa, usá efectos externos: un archivo (write_file/read_file), una base en disco, o el blackboard (share/observe). Bajo serve no hace falta nada de esto: jobs y rutas ya ven el mismo estado compartido.

Usá synsema serve para mantener el proceso — y la programación — corriendo, incluso sin rutas:

synsema serve scheduler.syn         -- "Serving N cron job(s). Press Ctrl+C to stop."

Los threads esperan estacionados (cero CPU entre ticks); el intérprete de cada job se construye una vez y se reusa.