wasm-gui: a WebAssembly Linux desktop (M1–M6) rendered by the native Rust client#104
Open
NathanFlurry wants to merge 28 commits into
Open
wasm-gui: a WebAssembly Linux desktop (M1–M6) rendered by the native Rust client#104NathanFlurry wants to merge 28 commits into
NathanFlurry wants to merge 28 commits into
Conversation
…ilt, M4b/M5 tests pass
Root cause: net.poll did a blocking recv_timeout(up to 50ms) on the single sync-RPC service thread, so while servicing one chatty guest's poll (e.g. an Xt app's main loop) no other guest's sync RPC could run — starving the WM and other clients. Lowering the cap to 3ms lets the service thread round-robin quickly across guests. Result: in the 3-client desktop (twm+xclock+xwin) the event-driven xwin client now renders fully (green+white rects, decorated) where before it lost draws; twm+xclock (2-client) renders the clock face. M5-twm + M5-multiclient still pass (no regression). Open: xclock still fails specifically in the 3-client case (xclock+xwin together) — investigating.
…indow) Second root cause found + fixed: the X server (long-running WASM guest) was killed at the 30s default wall-clock fuel budget (DEFAULT_WASM_EXECUTION_TIMEOUT_MS) -> 'WebAssembly fuel budget exhausted' -> desktop collapsed past 30s. Host now sets limits.resources.maxWasmFuel=1h on the trusted VM. Combined with the net.poll fairness cap (3ms), the 3-client desktop now renders robustly: twm CONCURRENTLY decorates a real libX11 window AND a stock xclock (live analog face), past 30s, 3/3 deterministic. test-m6-desktop.sh strengthened to assert the xclock face too. Proof: ~/tmp/gui-progress/m6-desktop-robust.png. SPEC.md M6 updated.
New: guest-xclient/xtest-agent.c (XTEST/libXtst input injector) + xinput-target.c (repaints on KeyPress=green/ButtonPress=orange) + build-xclient.sh (reusable libX11-client build) + test-m6-input.sh. The host launches the agent which synthesizes a ButtonPress; the target client repaints orange == the injected event was delivered through the wasm X server to a real libX11 client. Proof: ~/tmp/gui-progress/m6-input-button.png. Host --inject <pid>=<cmd> wired for dynamic stdin injection (currently blocked by a separate WASM-guest stdin-delivery gap; argv path proves the chain). The live winit blit still needs a display to verify (headless box). SPEC.md updated.
|
🚅 Environment secure-exec-pr-104 in rivet-frontend has no services deployed. |
e127565 to
b693106
Compare
…M1-M7 suite green)
- M6.2 Xft antialiased text (expat+fontconfig+libXft; freetype memory-stream patch; C-locale DB)
- M6-INTERACTIVE: runnable cross-platform (winit+softbuffer, macOS+Linux) live desktop with
host-driven cursor/click/keyboard/drag via x11rb XTEST to the X server host-backed AF_UNIX socket
(twm drag fix: NoGrabServer+OpaqueMove)
- M7: JWM 2.4.6 cross-compiled to wasm as the desktop shell (panel/taskbar/window list/live clock)
- PNG proof exporter (fb2png.py + export-proof-png.sh); fresh proof in ~/tmp/gui-progress/
- scripts: run-desktop, test-m6-{input,drag,xft}, test-m7-jwm, prepare-{xftfonts,jwm}, export-proof-png
- full suite 8/8 green: M4b, M5 multiclient+twm, M6 desktop+input+drag+xft, M7 JWM
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b693106 to
66eb007
Compare
Allocate a PTY pair and place its ends in two distinct processes: master fd into the parent (terminal emulator) fd table, slave fd into the child (shell) fd table. open_pty places both ends in one process; a terminal needs them split. This is the kernel primitive behind the forthcoming __pty_spawn host import for M6.3 (xterm/terminal). Read/write/poll already route through PTYs, so the terminal reads/writes the master and the shell's dup'd 0/1/2 slave just works. Unit test open_pty_split_wires_master_in_parent_and_slave_in_child verifies bidirectional I/O (master<->slave) across the two processes plus rollback on a bad child pid. Full kernel suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the kernel open_pty_split primitive into the child-process spawn path: - configure_child_stdio() factored from the two spawn variants; adds a 'pty' stdio mode that allocates a split PTY (master in parent, slave in child), dups the slave onto the child's stdin/stdout/stderr, and returns ptyMasterFd in the spawn response. - __pty_read/__pty_write sync-RPC handlers let the terminal (parent) drive the master fd, mirroring __kernel_stdin_read / __kernel_stdio_write semantics. - ActiveProcess.pty_master_fd tracks the master for reaping. - arch-guard: allowlist node_import_cache.rs for the process-wide materialize-timeout env read. A wasm terminal can now spawn a wasm shell over a real kernel PTY: shell stdin reads the slave, shell stdout writes the slave (write_process_stdout -> fd_write(1) -> pty), terminal reads/writes the master. Sidecar suite + architecture guards green. (WASM import shims + e2e test next.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e bindings (M6.3) Complete the host->guest PTY syscall surface so a wasm terminal can drive a kernel PTY: - node_import_cache.rs: host_net.pty_spawn (launches a child over child_process.spawn stdio 'pty', returns the master fd), pty_read/pty_write (drive the master via __pty_read/__pty_write). full tier. - v8-bridge.source.js: _ptyReadRaw/_ptyWriteRaw facades; v8_runtime.rs maps them to __pty_read/write; wasm.rs runner switch adds the two cases. - bump NODE_IMPORT_CACHE_ASSET_VERSION 70->71. Whole stack builds. pty_spawn reuses the existing engine-level child_process bridge (de-risked: _childProcessSpawnStart is an unconditional binding). C terminal/shell guests + e2e proof next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9853eb2 to
f1c842d
Compare
2fc31c1 to
bcf22e2
Compare
…t cycles) Builds on the PTY stdin pump: pty-shell.c is now a real line-oriented interpreter loop (prompt -> read line from PTY slave stdin -> respond), and pty-term.c drives a multi-command interactive session over the kernel PTY: echo hello -> hello ; ping -> pong ; exit -> bye (clean shutdown). test-m6-3-pty.sh asserts PTY_CHILD_REPLY_OK + PTY_CHILD_PING_OK + PTY_CHILD_EXIT_OK + PTY_SESSION_OK, proving repeated bidirectional terminal I/O (terminal->shell stdin AND shell stdout->terminal) across several cycles, not a one-shot echo. A real shell (dash/bash) needs fork/exec/job-control, which wasi lacks; this interpreter is faithful to what a terminal emulator drives over the PTY primitive. SPEC.md M6.3 status updated. test-m6-3-pty.sh green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bcf22e2 to
051930a
Compare
…led to wasm
st, cross-compiled from source to wasm32-wasip1 (scripts/build-st.sh), runs as a
wasm guest in secure-exec: it spawns the wasm shell (/pty-shell.wasm) over a real
kernel PTY and renders the shell's terminal output via Xft to the wasm X server.
scripts/test-m6-3-st.sh asserts the framebuffer shows the shell's prompt as
antialiased Xft text (215 glyph-core + 258 AA-edge px); proof PNG m6-3-st.png.
Integration (third_party/st):
- wasmpty.c: st's forkpty/openpty + select/read/write PTY backend replaced by the
host_net pty_spawn/pty_read/pty_write primitive (the path proven by test-m6-3-pty.sh).
- st.c: ttynew -> stwasm_spawn('/pty-shell.wasm'); ttyread -> non-blocking stwasm_read
(0 = no data this tick, not EOF); ttywriteraw -> stwasm_write; ttyresize/ttyhangup/
stty neutralized; termios.h dropped (wasi has none); tcsendbreak stubbed.
- x.c: the pselect(xfd, ttyfd) loop becomes a non-blocking poll loop (ttyread via
pty_read + XPending + 8ms nanosleep idle throttle), since neither fd is a libc fd.
- config.h: DejaVu Sans Mono Xft font (shipped via fontconfig); STWASM_SHELL.
- link adds expat + zlib (fontconfig deps) + wasi-emulated mman/process-clocks.
Host: run_xdemo gained --pty-shell to install the child shell into the VM so a
terminal-emulator client can pty_spawn('/pty-shell.wasm').
Separable gap (documented in SPEC.md): typing into st THROUGH X does not work yet
because this Xvfb cannot compile an XKB keymap (xkbcomp is exec'd; wasi has no
fork/exec), so the server has no keyboard device. st's keypress->ttywrite->pty_write
path is wired; the terminal<->shell bidirectional I/O is proven by test-m6-3-pty.sh.
.gitignore: ignore third_party/st.tar.gz (the patched source tree is tracked).
build-st.sh fetches upstream st-0.9.2 if the tree is absent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…map, no xkbcomp)
The wasm X server now has a functional keyboard device, so host-driven KeyPress/
XTEST events are delivered AND translated to characters for real libX11 clients.
test-m6-keyboard.sh asserts an injected key repaints the input-target green
(green=39888).
Root cause fixed: wasi has no fork/exec, so Xvfb cannot run xkbcomp to compile a
keymap at runtime; its keyboard device never activated ('XTest keyboard not
activated') and all key events were dropped. Fix: compile a US keymap on the host
(scripts/prepare-xkb.sh -> /xkb/default.xkm), install it in the VM (--vm-tree), and
patch the server's XkbCompileKeymap to load it directly via fmemopen instead of
forking xkbcomp (patches/xserver-keymap-no-xkbcomp.patch; third_party/xserver is
not VCS-tracked so the change is persisted as a patch file). KEY GOTCHA: VFS-backed
files don't support per-access fseek/fread streaming under wasi (same limitation
freetype hit) and XkmReadFile fseeks to each section offset, so the .xkm must be
slurped into memory and read via fmemopen.
Also:
- host: --inject 'host=focus' sets X input focus (window-under-pointer + PointerRoot)
so keys reach a client when no WM owns focus.
- st: build with XIM disabled + core XLookupString (no XIM server exists under wasi;
an XIC makes XFilterEvent swallow every KeyPress before kpress runs).
- scripts/prepare-xkb.sh, scripts/test-m6-keyboard.sh.
Note (SPEC.md): live typing into st specifically is still blocked by the synchronous
child-driving model (st's ttyread drives the interactive PTY child, whose read(0)
spins in-isolate waiting for input, so the drive never returns and st can't process
the keystroke that would feed it). That is the core 'drive child isolates
concurrently' execution-engine limitation, separate from the keyboard/terminal work;
terminal<->shell I/O is proven by test-m6-3-pty.sh and the X keyboard by
test-m6-keyboard.sh. Rebuild note: link-xvfb.sh must be followed by wasm-opt
--fpcast-emu.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…very, not deadlock) Flushed logging + sidecar tracing disproved the earlier 'child-drive deadlock' and 'XIM filtering' theories for why typing into st doesn't work: st's poll loop runs fine (poll_descendant ENTERs 150+x and returns; the apparent block was a stderr-buffering artifact), and the server's input focus IS st's window (confirmed via get_input_focus: VIEWABLE + KeyPressMask). The real, still-open issue is that the server delivers only early events (Expose/VisibilityNotify) to st's host_net X connection and never late ones (FocusIn/KeyPress), even with XSync forcing round-trips - while an identical simple client (xinput-target) receives keys via the same flow. So it is st-connection-specific X-over-host_net event delivery, needing server-side event-routing instrumentation. Docs-only correction (SPEC.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… test) Demonstrates and locks in robust concurrency: host --xdemo --concurrent launches all X clients simultaneously (no per-client settle/ordering gating), and scripts/test-m2-3-concurrent.sh starts twm + xclock + xftdemo at once -- twm reaches its event loop and decorates both concurrently-launched windows, xftdemo opens its Xft font, 0 clients crash, and the framebuffer shows the twm-managed desktop (proof ~/tmp/gui-progress/m2-3-concurrent.png). 5/5 reliability runs: 0 failures, consistent render. The enabling fix was M6.4's net.poll fairness (JAVASCRIPT_NET_POLL_MAX_WAIT 50ms->3ms) which removed the sync-RPC-bridge starvation that made concurrent libX11 init flaky; this commit adds the concurrent launch path (host main.rs --concurrent) + the regression test to lock it in. The WM is slightly slower to finish decorating under concurrent contention but is reliable, not flaky. SPEC.md 'Robust concurrency' marked done. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sses DeliverFocusedEvent) 10 server-side instrumentation passes this session narrowed the st live-typing gap: keyboard events do NOT flow through DeliverFocusedEvent (0 calls in both the working xinput-target run and the st run), and TryClientEvents/DeliverToWindowOwner core type-2 checks never fire either -- core KeyPress is synthesized late (EventToCore at write time) and live events travel as XI internal device events via DeliverGrabbedEvent/DeliverDeviceEvents. WriteToClient tracing is confounded by event batching (count=N*32)/XI2. Server focus IS st's window (confirmed get_input_focus), st selects KeyPressMask, and an identical simple client (xinput-target, real core KeyPress via XNextEvent) works -- so the difference is st-connection/window specific. SPEC.md records where the next investigation should start (DeliverGrabbedEvent + DeliverDeviceEvents + the mieq/ProcessInputEvents drain, NOT DeliverFocusedEvent). Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the keyboard path 12 instrumentation passes total: keyboard events bypass TryClientEvents, DeliverFocusedEvent, AND DeliverDeviceEvents (each 0 calls for internal key/button events ET_KeyPress=2..ET_ButtonRelease=5 in BOTH the working green run and the st run), yet green delivers KeyPress to xinput-target. So this server's keyboard delivery is atypical (DeliverGrabbedEvent / XKB-direct / XTEST-specific). SPEC.md updated with the eliminations + the next-session starting points (DeliverGrabbedEvent + ProcXTestFakeInput + mieqProcessDeviceEvent). Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ting (+ tracing caveat) 13 instrumentation passes culminated in the real finding: st live-typing is a TIMING/LOAD-sensitivity issue, not an event-routing bug. KEY META-FINDING: hot-path ErrorF instrumentation in the X server event pipeline perturbs the very timing that is the bug -- with traces added, even the working xinput-target stops receiving KeyPress (green=0/orange=40000), which invalidates the earlier dew/dfe/dde "0 type-2 delivery" conclusions (the key just wasn't delivered in time in the slowed runs). Untraced, test-m6-keyboard passes 3/3 reliably (green=39888): core KeyPress delivery to a LIGHT client works fine. st fails because it is HEAVY -- Xft rendering + driving the PTY child produce dense request/sync-RPC traffic that starves the wasm X server's input-event (mieq) processing over the single service thread (same single-bridge scheduling class as M2.3/M6.4 net.poll, now for input). Next session: low-overhead counters (not hot-path ErrorF) + input-vs-request scheduling fairness in the server main loop, NOT a delivery-function fix. SPEC.md + memory updated. Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…target diagnostic) ~16 passes. Decisive isolation this turn via a minimal control client (guest-xclient/xpoll-target.c): it uses st's EXACT non-blocking XPending+nanosleep poll loop (no Xft, no PTY) and receives KeyPress fine (green=39888) under twm click-to-focus -- so the poll loop is NOT the cause. A hardcoded NOPTY st build (no PTY driving) still got zero input -- so PTY driving is NOT the cause. With focus CONFIRMED on st's window (get_input_focus: st win, VIEWABLE, KeyPressMask set), st receives ZERO input events (no ButtonPress AND no KeyPress, only Expose/Visibility), while xpoll-target under identical conditions gets them. So st live-typing is st-window/connection specific: st uses XCreateWindow with a custom visual+colormap (CWColormap, for Xft) + a heavy libxcb connection, vs xpoll-target's XCreateSimpleWindow on the default visual. Next: compare window attrs/visual and check whether st's heavy libxcb reply traffic buries input events in the xcb event queue. Adds xpoll-target.c (tracked diagnostic). SPEC.md updated. Docs + diagnostic only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ents NOT (XI2/core synthesis) ~22 passes. Decisive symptom this turn: with st self-focusing (XSetInputFocus on its own window), st receives WINDOW events (Expose, VisibilityNotify, MapNotify, and now FocusIn/FocusOut) but ZERO DEVICE events -- no KeyPress AND no ButtonPress -- even with focus CONFIRMED on st (FocusIn received) and a button injected over its on-screen location. Minimal clients (xpoll-target/xinput-target) receive both via the identical path. So st live-typing is specifically a DEVICE-EVENT delivery problem for st's connection -- focus, poll loop, PTY driving, window creation, visual, and XIM are all ruled out. Device events flow via XI2 with core events synthesized for core clients; next session: examine the XI2->core synthesis / per-client device-event selection for st's connection. Focus + keymap both work (xinput-target green via the same path). SPEC.md updated with the crystallized symptom. Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…wed to terminal machinery ~25 passes. Built guest-xclient/xftpoll-target.c (the working poll loop + XftFontOpenName + setlocale/XSetLocaleModifiers, exactly like st's main) and proved it ALONE + focus + key => green=40000. So Xft font init is NOT the cause and locale/IM modifiers are NOT the cause. Combined with the prior definitive control (xpoll-target works alone+focus), the eliminated set is now: WM, focus, poll loop, PTY driving, visual/colormap, window creation, XIM, Xft font init, locale. Remaining suspects are st's terminal machinery the minimal clients lack: XftDraw rendering to the offscreen pixmap (xw.buf) + XCopyArea, the GC created on the ROOT window, selection setup (selinit), xsetenv, or terminal-core init. Adds xpoll-target.c (no Xft) and xftpoll-target.c (Xft+locale) as working baselines for continuing the bisection. SPEC.md updated with results + next steps. Docs + diagnostics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…minal-core/run-flow ~28 passes. Extended xftpoll-target.c to replicate st's ENTIRE X-protocol behavior: st's exact XCreateWindow (explicit attrs, CWColormap, GC-on-root, 644x408 at 0,0), XftFontOpenName, setlocale+XSetLocaleModifiers, offscreen-pixmap XftDraw + XCopyArea, AND host_net.pty_spawn of /pty-shell.wasm -- and it STILL receives KeyPress (green). Conversely st with selinit() removed still gets ZERO device events. So NONE of st's X-protocol behavior is the cause. Eliminated (16 total): WM, focus (incl. self XSetInputFocus), poll loop, PTY driving, visual/colormap, exact window creation, GC-on-root, XIM, Xft init, Xft RENDER/pixmap rendering, locale, PTY child spawn, selection setup, window size/position. The cause is st's terminal-core/run() flow -- the only thing the now-near-identical working baseline (xftpoll-target.c) lacks. Next: bisect by REMOVING pieces from st (tnew/twrite, run() do-while, xsetenv, handler[]) or use non-perturbing server-side counters. xftpoll-target.c (works, near-st) committed as the reference baseline. SPEC.md updated. Docs + diagnostic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…; X path fully ruled out)
~33 passes. Split tests pinned st live-typing precisely:
(a) st with its run() loop replaced by the minimal working-baseline loop (no
ttyread/draw) STILL gets no KeyPress => NOT the loop body, it is st's INIT.
(b) A probe right after xinit() (before xsetenv/selinit/run/ttynew) gets FocusIn but
no KeyPress => xinit itself breaks device-event delivery.
(c) Removing cursor / WM-protocols / _NET_WM_PID / resettitle / xhints(input=1) from
xinit did NOT restore keys => none of those.
(d) 500 round-trip requests added to the working baseline did NOT break it => NOT
request count.
So the remaining suspects are xinit's xloadfonts (4 Xft faces) and xloadcols (256+
colors) -- the only xinit ops the working near-st baseline (xftpoll-target.c) lacks and
that aren't generic request volume. Next: stub xloadfonts/xloadcols and re-probe; bisect
which Xft/Fc/Render call triggers it. Everything else in st's X path is ruled out (~18
eliminated suspects). SPEC.md updated; xftpoll-target.c kept as the working reference.
Docs + diagnostic.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n exhausted) ~34 passes. Extended the working baseline xftpoll-target.c to perform EVERY operation st's xinit does -- st's exact XCreateWindow + GC-on-root, FOUR Xft faces (xloadfonts), 256 XftColor allocs (xloadcols), offscreen-pixmap render, AND pty_spawn -- and it STILL receives KeyPress (green). So bisection-by-addition is exhausted: every individual st operation replicated, none breaks input. The cause must be operation ORDER (st loads fonts/colors BEFORE creating its window; the baseline creates the window first) or a subtle cumulative interaction. Remaining methodologies (documented in SPEC.md): st-side REMOVAL bisection (stub xloadfonts/xloadcols, reorder), X PROTOCOL TRACE diff (st vs xftpoll-target byte streams around the inject), or non-perturbing server-side counters. ~19 suspects eliminated; xftpoll-target.c kept as the working reference. Docs + diagnostic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ol-trace remains ~35 passes. Order ruled out too: the baseline xftpoll-target.c was made to load fonts+colors BEFORE XCreateWindow (st's exact order) and STILL receives keys. So the baseline now replicates st's ENTIRE X behavior -- every operation, in st's order -- and works, while st fails even right after xinit with focus CONFIRMED (FocusIn arrives, so KeyPressMask is provably set). CONCLUSION: the cause is NOT identifiable by replicating st's X calls; the entire X-call layer is excluded (~20 suspects eliminated across the session). The only remaining methodology is an X PROTOCOL-TRACE DIFF (capture bytes the server sends to st vs xftpoll-target around the inject, via an in-VM socket proxy or a libxcb raw-read hook, and diff). Documented in SPEC.md as the precise frontier. The working reference baseline xftpoll-target.c is committed. Docs + diagnostic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ivers KeyPress correctly) ~37 passes; the decisive finding. A non-perturbing per-client counter in the server's WriteToClient (zero-I/O increments + rare /data dump) proved the server DOES write the KeyPress to st's connection: st gets keypress_writes=1, EXACTLY like the working xinput-target (=1). The only difference is xinput-target SURFACES it (green) and st does NOT. So the ENTIRE server side -- routing, focus, XI2/core synthesis, keymap, device-event delivery -- is CORRECT and the bytes reach st's socket. st-typing is a CLIENT-SIDE libxcb/Xlib event-surfacing bug in st: it surfaces early events (Expose/Visibility) but XPending/XNextEvent stops surfacing the later KeyPress. This moots the server-side/protocol-trace avenues. Next is client-side only: instrument st's XPending/_XEventsQueued/xcb_poll_for_event vs the working xftpoll-target baseline. SPEC.md + memory updated. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s flush/wire/recv ~38 passes. Forcing a socket read in st's loop (XEventsQueued QueuedAfterReading) does NOT surface the KeyPress -- the bytes aren't reaching st's libxcb read at all. The prior server counter measured WriteToClient (BUFFERING), not the flush/wire send. So the boundary is now: server-flush (FlushClient -> Xtranssock -> net_send), host_net wire delivery (net_send/net_recv for st's heavy connection), or libxcb recv -- NOT st's read logic (forced reads don't help). st recvs EARLY events (Expose) but not the LATER KeyPress. Next: count net_send-to-st (server) + net_recv-on-st (client) to pin flush-vs-wire-vs-recv; suspect a host_net per-socket buffering/poll-readiness stall on st's connection after heavy traffic. Layer-by-layer wire investigation, multi-session. SPEC.md updated. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…es/sends correctly) ~39 passes. A server-side FlushClient byte counter (non-perturbing /data dump) shows the server FLUSHES/net_sends to st: client 1 (st) flushed_bytes=2596 over the run. So the server both buffers AND writev/net_sends data to st's socket -- not just buffers. Combined with the forced-read result (XEventsQueued(QueuedAfterReading) doesn't surface the KeyPress), the failure is st's CLIENT-SIDE host_net RECV: st recvs EARLY bytes (Expose surfaces) but not the LATER KeyPress bytes the server sent. So the bug is in host_net net_recv / net_poll readability for st's heavy X connection (bytes are on the wire / in the kernel socket but st's recv stops pulling after the initial burst). Next: count net_recv bytes on st's client socket (runner host_net.net_recv, keyed by fd) vs the 2596 sent; check net_poll readability for st's X fd after heavy traffic (POLLIN/POLLOUT area). The X server, focus, keymap, and st's X-call/read logic are all excluded. SPEC.md updated. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l/refill for st's connection ~39 passes. Traced the client recv path end to end: st libxcb -> net_recv (runner) -> drains a runner-LOCAL buffer (dequeueHostNetBytes) refilled by pollHostNetSocket (ONE net.poll RPC -> ONE chunk into socket.readChunks) -> sidecar net.poll handler -> kernel recv_buffer (UNBOUNDED VecDeque, level-triggered POLLIN when non-empty). Kernel buffering is correct, so the suspect is the net.poll RPC / runner local-buffer refill for st's heavy connection (one-chunk-per-poll under-draining, or a readiness edge st misses). Measurement caveat recorded: the FlushClient counter (st flushed_bytes=2596) is cumulative to its last /data dump and may not include the late keypress flush -- re-run dumping AFTER the inject to confirm the keypress is net_sent. Next: per-fd byte counters in runner net_recv + sidecar net.poll dumped after the inject (server-sent vs client-recv for st's X fd), and check net.poll level-vs-edge for an already-drained socket. Everything above the host_net wire is excluded. SPEC.md updated. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…X-server flush-timing frontier ~40 passes. Traced the FULL recv path: the X11 socket is a real HOST UnixStream under the VM shadow (not the kernel socket_table). Chain: X server FlushClient -> _XSERVTransWritev -> net_send -> server's accepted host UnixStream -> OS socketpair -> st's connected host UnixStream -> st's reader thread (spawn_unix_socket_reader: blocking read loop -> UNBOUNDED mpsc channel) -> ActiveUnixSocket.poll -> net.poll RPC -> runner net_recv -> libxcb -> XPending. EVERY component is structurally correct. st surfaces EARLY events (Expose, and FocusIn from its own XSetInputFocus) but not the LATE host-injected keypress; the server delivers it. The single unresolved link is X-server output-flush TIMING for the late keypress to an IDLE client (st sends no requests to trigger a flush; the keypress comes from another client's XTEST), and the flush counter is cumulative to its dump so it can't confirm the late keypress was sent. Decisive next test (multi-session): inject-synchronized counters (dump AFTER the inject, gated on a sentinel) at FlushClient + st's reader thread + net.poll to pin server-flush vs reader vs poll for the LATE keypress specifically. Everything except this timing question is verified correct. SPEC.md updated. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Experiment under
experiments/wasm-gui/: a graphical Linux desktop built by cross-compiling real, standard X11 software towasm32-wasip1with our own toolchain and running it inside the real secure-exec V8 sidecar, rendered by a native Rust app oncrates/secure-exec-client(per SPEC §1a: no wasmer / node:wasi / TS client /Command::newin the execute+render path).What works (all wasm guests in one VM, over host_net AF_UNIX)
test-m4b.sh,test-m5-multiclient.sh,test-m5-twm.sh.test-m6-desktop.sh. Proof:~/tmp/gui-progress/m6-desktop-robust.png.test-m6-input.sh.Core sidecar fixes (benefit all of secure-exec)
net.pollblocked the single service thread up to 50ms, starving other guests; lowered to 3ms so it round-robins.limits.resources.maxWasmFuel.Notes
third_party/,scripts/).winitwindow blit needs a machine with a display to verify (the dev box is headless); the input delivery path is verified headlessly via XTEST.experiments/wasm-gui/SPEC.md.🤖 Generated with Claude Code