From 2ead86a27feba4a3da8339570baf2625757dbbb8 Mon Sep 17 00:00:00 2001 From: JustVugg Date: Tue, 14 Jul 2026 18:15:29 +0200 Subject: [PATCH] =?UTF-8?q?serve=20mux:=20fix=20Windows=20request=20dispat?= =?UTF-8?q?ch=20=E2=80=94=20BINARY-mode=20stdin/stdout=20+=20PeekNamedPipe?= =?UTF-8?q?-only=20polling=20(fixes=20#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two Windows-only bugs in run_serve_mux left the gateway hanging after READY: 1. No _setmode(_O_BINARY): the CRT collapsed CRLF inside fread() payloads (waits forever for missing bytes) and expanded LF in the READY/STAT sentinels. 2. WaitForSingleObject on an anonymous pipe is undefined (always-signaled or WAIT_FAILED) and PeekNamedPipe fails on file/console handles, so the dispatch gate never opened. New rule: idle -> block in getline (POSIX select(NULL) semantics); active -> PeekNamedPipe poll. Reproduced and verified on real Windows via MinGW cross-compile + WSL interop: old binary writes READY (with CRLF corruption) then hangs forever on a crafted SUBMIT frame; fixed binary answers DONE + STAT and exits cleanly. Linux path untouched (0 warnings, oracle-exact). Co-Authored-By: Claude Opus 4.8 --- c/glm.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/c/glm.c b/c/glm.c index d6e5716..561d410 100644 --- a/c/glm.c +++ b/c/glm.c @@ -3418,6 +3418,14 @@ static void run_serve_mux(Model *m, const char *snap){ KVState *initial=m->kv; free(initial->kv_start); free(initial); ServeCtx *ctx=calloc(nctx,sizeof(*ctx)); ServeReq *req=calloc(nctx,sizeof(*req)); for(int i=0;i0) + /* Anonymous pipes are NOT waitable objects: WaitForSingleObject on them is + * undefined (always-signaled or WAIT_FAILED), and PeekNamedPipe fails on + * file/console handles — the old gate never dispatched (#195). New rule: + * idle -> block in getline() inside mux_submit (same semantics as the + * POSIX select(NULL)); active -> poll the pipe with PeekNamedPipe, and on + * non-pipe stdin just defer submits until the batch finishes. */ + if(eof) ready=0; + else if(!active) ready=1; + else ready=(PeekNamedPipe(ih,NULL,0,NULL,&avail,NULL) && avail>0)?1:0; + if(ready) #endif if(mux_submit(m,&T,ctx,req,nctx,maxctx,eos)<0) eof=1; }