Skip to content

fix(perf): stop UI-thread subprocess forks and fixed 60fps repaint - #42

Merged
nakulbh merged 2 commits into
mainfrom
fix/ui-thread-latency
Jul 29, 2026
Merged

fix(perf): stop UI-thread subprocess forks and fixed 60fps repaint#42
nakulbh merged 2 commits into
mainfrom
fix/ui-thread-latency

Conversation

@nakulbh

@nakulbh nakulbh commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • Root cause of the "app not responding" freezes: save_session_now only stamped its "last saved" timestamp after a successful write, so once the session fingerprint stabilized (the normal steady state — nothing about the layout changes while you type) the autosave interval check passed on every frame, and capture_session() walks every pane and forked ps per pane at 60Hz on the UI thread.
  • Moves all title/cwd/git/PR metadata probing (ps, lsof//proc, git rev-parse, gh pr view) off the UI thread onto a dedicated worker (rmux-title-probe), batched into one ps snapshot per cycle with per-directory memoization and automatic back-off when gh is slow (e.g. flaky network/DNS).
  • Fixes key→screen latency for typing and nvim j/k navigation:
    • request_repaint_after(16ms) was unconditional, pinning the app at 60fps even on a fully static screen — now paced by the cursor blink interval instead.
    • Fonts::has_glyph was called per non-ASCII cell per frame (box-drawing borders in agent prompt boxes, Nerd Font icons in nvim's file tree) — now memoized in the glyph cache.
    • The per-frame grid snapshot reallocated Vec<Vec<GridCell>> from scratch every pane every frame — now reuses a buffer via TermState::snapshot_into.

Test plan

  • cargo clippy --workspace --all-targets — clean
  • cargo test --workspace — all passing (380+ tests, including new coverage for the probe worker and snapshot buffer reuse)
  • Manually launched the app, restored a real 2-workspace/4-pane session, confirmed agent resume commands (claude --resume, grok) still fire correctly
  • Would benefit from a user pass on Linux with a busy workspace tree, since that's where the reported lag was worst

🤖 Generated with Claude Code

Session autosave only stamped its "last saved" timestamp on a successful
write, so once the fingerprint stabilized the interval check passed on
every frame and capture_session() forked `ps` per pane at 60Hz — the
direct cause of "app not responding" freezes. Title/cwd/git/PR probes
(ps, lsof, git, gh) now run on a dedicated worker thread instead of the
UI thread, batched and memoized so a handful of panes don't fork a
process per pane per second.

Also cuts key->screen latency for typing and nvim navigation: the app
no longer pins request_repaint_after(16ms) unconditionally (now paced
by the cursor blink instead of animating a static screen at 60Hz),
Fonts::has_glyph lookups for box-drawing/Nerd Font glyphs are memoized
instead of re-walking the font fallback chain every cell every frame,
and the per-frame grid snapshot reuses its buffer instead of
reallocating every pane's cell grid each frame.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 14:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@nakulbh
nakulbh force-pushed the fix/ui-thread-latency branch from 3b48fed to 4e76555 Compare July 29, 2026 14:59
Public rustdoc must not link private cwd_of_process; RUSTDOCFLAGS=-Dwarnings
failed the Linux Doc CI step.
@nakulbh
nakulbh merged commit a902610 into main Jul 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants