Skip to content

perf: coalesce per-session PTY output dispatcher posts to prevent UI starvation #70

Description

@AThraen

Problem

With many concurrent live sessions (~20+), typing into the active terminal can stall for hundreds of milliseconds. Every PTY output chunk from every session does:

// TerminalBridge.OnPtyData
string json = JsonSerializer.Serialize(new { type = "output", data = rawData });
WpfApplication.Current?.Dispatcher.BeginInvoke(() =>
{
    _webView.CoreWebView2?.PostWebMessageAsString(json);
});

Each chunk is one dispatcher post. Chatty background sessions (Claude spinners, status lines, repaints) flood the shared UI dispatcher queue. A keystroke message from the active session — which also goes through the same dispatcher via OnWebMessageReceived — has to wait its turn behind dozens of unrelated output posts, so the foreground terminal feels frozen.

Reproduced live (2026-06-02) with 23 live sessions + 8 dormant: 61 WebView2 processes, 237 child processes, ~9.4 GB resident. Typing latency was visibly bad. The existing DebugTerminalTrace (#36-era feature) was built for exactly this diagnosis.

Proposal

Coalesce per-session output posts into one dispatcher tick:

  • Per-bridge StringBuilder (or List<string>) guarded by a lock, plus a _pendingPost flag.
  • OnPtyData appends to the buffer. If _pendingPost is false, set it and BeginInvoke a single flush.
  • Flush callback drains the buffer under the lock, sets _pendingPost = false, and does ONE PostWebMessageAsString with the concatenated payload.

Net effect: N output chunks within a single dispatcher round-trip become one queue entry. Background sessions can no longer starve the foreground.

xterm.js already handles arbitrary-length writes — the JSON envelope just needs to wrap one big string, no protocol change.

Notes

  • Keep the [DEBUG-tt] trace points so we can measure before/after dispatcher latency.
  • Be careful with the boot-overlay race: the first PTY byte must still trigger PostBootDoneIfNeeded() synchronously. That fires from OnPtyData before the dispatcher post today and should stay that way.
  • Should not affect AlertDetector / OutputIndexer — those consume RawOutputReceived synchronously on the PTY thread.

Workaround

Sleep (💤) sessions that aren't in active use. Going from 23 → ~5 live sessions eliminates the typing freeze.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions