diff --git a/plans/tcl-sx-completion.md b/plans/tcl-sx-completion.md index bb752719..d9cf7148 100644 --- a/plans/tcl-sx-completion.md +++ b/plans/tcl-sx-completion.md @@ -157,6 +157,40 @@ eof-after-read, append mode, seek-to-end, fconfigure-blocking.** --- +## Phase 5b — Event loop: fileevent / after / vwait / update ✓ + +Tcl event-driven I/O scoped to script-mode (vs. server-side commands). The +mechanism rides on the existing IO suspension model: SX adds one new primitive +`(io-select-channels read-list write-list timeout-ms)` wrapping `Unix.select`, +and the Tcl event loop is implemented in Tcl itself (no sx_server.ml changes). + +| Status | Work | Unlocks in Tcl | +|---|---|---| +| [x] | `io-select-channels` SX primitive | Unix.select on registered channels | +| [x] | `fileevent $chan readable\|writable script` | event handler registration; `{}` to unregister | +| [x] | `after ms script` | one-shot timer queued in `:timers` | +| [x] | `after ms` (no script) | sleep that drives the event loop | +| [x] | `vwait varname` | block until var set/changed, runs handlers | +| [x] | `update` | non-blocking event drain (poll, fire ready handlers) | + +Event loop: `tcl-event-step interp poll-timeout-ms` — fires expired timers, +calls `io-select-channels` with fd list from `:fileevents`, runs ready handlers. +`vwait` polls every 1000ms or until var changes (whichever first); `update` is +`tcl-event-step interp 0`. + +State on interp: `:fileevents` (list of `(chan event script)`) and `:timers` +(list of `(expiry-ms script)`, sorted by expiry). + +**Trade-off:** Scoped to script mode — `vwait` from inside a server-handled +command would not interact with sx_server's stdin scheduler. Sufficient for ~95% +of real-world Tcl scripts (sockets, pipes, GUI-style polling, CLI tools). + +**Total: ~half day. 5 new idiom tests: after-vwait-timer, after-multiple-timers- +update, fileevent-readable-fires, fileevent-query-script, after-cancel-via- +vwait-timing. 354/354 green.** + +--- + ## Suggested order 1. **Phase 1** — immediate Tcl wins, zero risk, proves the approach @@ -173,6 +207,7 @@ becomes a lasting SX contribution used by every future hosted language. _Newest first._ +- 2026-05-07: Phase 5b event loop — io-select-channels SX primitive + Tcl-side fileevent/after/vwait/update; tcl-event-step drives expired timers + Unix.select on registered channels; +5 idiom tests; 354/354 green - 2026-05-07: Phase 5 channel I/O — 11 SX primitives (channel-open/close/read/read-line/write/flush/seek/tell/eof?/blocking?/set-blocking!) wrapping Unix.openfile/read/write/lseek/set_nonblock; tcl-cmd-open/close/read/gets-chan/seek/tell/flush rewritten + new tcl-cmd-fconfigure; tcl-cmd-puts dispatches on "fileN" arg; gets registration fixed; +7 idiom tests; 349/349 green - 2026-05-06: Phase 4 env-as-value — current-env (special form via Sx_ref.register_special_form), eval-in-env (primitive in setup_evaluator_bridge), env-lookup + env-extend (in setup_env_operations); 5 idiom tests; 342/342 green - 2026-05-06: Phase 3 OCaml primitives — file-read/write/append/exists?/glob + clock-seconds/milliseconds/format in sx_primitives.ml + unix dep; tcl-cmd-clock/file wired up; 337/337 green @@ -191,4 +226,4 @@ _Newest first._ - Full `clock format` locale support - Tk / GUI - Threads (mapped to coroutines only, as planned) -- `chan event` / `fileevent` — event-driven I/O callbacks (Phase 5 covers blocking + non-blocking flag, but no event loop dispatch) +- Server-mode `vwait` — Phase 5b event loop is scoped to script-mode; from inside a server-handled command it can't see sx_server's stdin scheduler