Pause VT parser for idle sessions to eliminate CPU waste

When no WebSocket client is connected, the session's readLoop still
processes every byte of terminal output through the go-te VT parser
(tracker.Feed), Screen.Draw grapheme segmentation, and string
allocations — even though nobody is consuming the screen state.
For programs like btop inside tmux that produce continuous full-screen
redraws, this causes sustained CPU usage and GC pressure over hours.

Fix: after a 10-second idle threshold (no client connected), skip
tracker.Feed() and only maintain the replay buffer. When a client
reconnects (UpdateConnector) or a screenshot is requested
(GetScreenSnapshot), rebuild the tracker by replaying the buffer
through a fresh VT parser instance.

Changes:
- Add idleSince atomic timestamp + MarkIdle() to Session interface
- handleOutput() skips tracker.Feed when idle > threshold
- UpdateConnector() clears idle flag and rebuilds tracker from replay
- GetScreenSnapshot() rebuilds stale tracker on-demand for screenshots
- Wire MarkIdle() call into handleWebSocket cleanup (client disconnect)
- Add TestIdleTrackerPauseAndRebuild covering the full lifecycle
This commit is contained in:
GitHub Copilot
2026-02-18 09:21:18 +00:00
parent 025d91a632
commit 2c5d3c72f9
7 changed files with 152 additions and 0 deletions
+5
View File
@@ -487,6 +487,11 @@ func (s *LocalServer) handleWebSocket(w http.ResponseWriter, r *http.Request) {
s.mu.Unlock()
go s.wsSender(client)
defer s.stopWSClient(routeKey, client)
defer func() {
if session := s.sessionManager.GetSessionByRouteKey(routeKey); session != nil {
session.MarkIdle()
}
}()
// Helper to send JSON through the send channel (avoids concurrent conn writes)
sendJSON := func(v any) {