Rio
adr/0010-async-via-event-loop.mdRaw Markdown · GitHub ↗

ADR-0010: Asynchrony through the event loop and coroutines

  • Status: Accepted
  • Date: 2026-06-24
  • Deciders: jka
  • Decision: D10

Context

Several operations take time: streaming model output, running git or other commands, and waiting on the network. Tcl is single-threaded by default, and the UI must never freeze while such work runs.

Decision

Long operations use Tcl's event loop: fileevent for channel readiness and coroutines (Tcl 8.6+) to keep sequential logic readable. rio does not use threads.

Examples in the code base: the agent loop runs each turn in a coroutine that yields at the approval gate and while waiting for a provider (ADR-0026, ADR-0104); command execution for the agent is asynchronous with a timer-driven timeout (ADR-0083); the GUI reads the core channel with a fileevent reader (ADR-0030).

Consequences

  • Streaming code reads as a straight sequence rather than as callbacks.
  • Anything that blocks the interpreter (a synchronous exec, a synchronous HTTP fetch) blocks every client of that core. Such calls must stay short or be made asynchronous; the agent's command tool had to become asynchronous for exactly this reason.
  • Nested event loops need care. vwait inside a fileevent handler, or starting a core call from inside another call's round trip, causes re-entrancy bugs; several later decisions defer work to idle or timer callbacks for this reason.