flan/test/programs/dev-chatty.flan
Joseph Ferano 6197b32b50 A program that prints is not a program that stopped polling
The merged build's stdout is a 64K pipe back into the daemon's own process,
and the accept loop is the only thing reading it -- which it is not doing
while serve is answering a request. The two five-second waits for a frame
boundary now drain the pipe on every tick, so a program stopped inside fwrite
is one the daemon lets go rather than one it waits out and then accuses of
not calling agent/poll.

drain and not take: the text stays in the buffer until with_output puts it on
the reply, which is where the output an evaluation caused belongs. And the
drain sits beside the sleep rather than inside the select, because a readable
pipe would make the tick free and count the timeout out in a fraction of it.

dev-chatty.flan prints 4K a frame, which is the only fixture here that fills
the pipe at all; without the drain it fails in 5.1s with the old sentence.
2026-09-18 07:31:25 +07:00

42 lines
2.0 KiB
Plaintext

;;;; A program that prints far more than a pipe holds, for the one failure a
;;;; quiet fixture cannot reach.
;;;;
;;;; In a merged `flan dev' the program's stdout is a 64K pipe back into the
;;;; same process, and the only thing reading it is the daemon's accept loop —
;;;; which is not running while the daemon is answering a request. Every other
;;;; fixture here prints a line or two per frame, so the pipe never fills and
;;;; the arrangement looks sound. This one prints 4K per frame, which fills 64K
;;;; in sixteen frames: less time than a module takes to build. By the time an
;;;; evaluation is delivered the game thread is stopped inside fwrite, and it
;;;; stays there until somebody reads — so a daemon that waits for a frame
;;;; boundary without draining waits for one that cannot arrive, and then says
;;;; the program is not calling (agent/poll).
;;;;
;;;; It is calling it. See lib/dev.ml's [eval_expr], which drains every tick.
(import agent "vendor:agent")
;;; Counted so that an evaluation has something of the program's own to read,
;;; and so a transcript can be checked for progress rather than only for text.
(defvar frames i64)
(defn chatter [] i64
;; Sixty-four lines of sixty-three characters and a newline: 4096 bytes a
;; frame, written through the line buffer flan_rt.c asks for, so each line
;; is its own write and the block happens in the middle of a frame rather
;; than at a flush somewhere else.
(dotimes [i 64]
(println "..............................................................."))
(set frames (+ frames 1))
frames)
(defn main [] i32
(agent/start "/tmp/flan-dev-chatty-fallback.sock")
;; 24000 for dev-repl.flan's reason: the test closes the connection when it
;; is done and the daemon takes the program with it, so the count only has
;; to outlast the checks. A program that reached its last frame mid-test
;; would fail honestly and for the wrong reason.
(dotimes [i 24000]
(chatter)
(agent/wait 1))
0)