8000 fix(renderer): stop the WebGL cursor-blink interval when releasing the addon (#421) by ConnorRobinson13 · Pull Request #433 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

fix(renderer): stop the WebGL cursor-blink interval when releasing the addon (#421) - #433

Merged
amirlehmam merged 2 commits into
amirlehmam:masterfrom
ConnorRobinson13:fix/renderer-retention-421
Nov 4, 2026
Merged

amirlehmam merged 2 commits into
amirlehmam:masterfrom
ConnorRobinson13:fix/renderer-retention-421

Conversation

@ConnorRobinson13
Copy link
Copy Markdown
Contributor

Refs #421 (draft, verification in progress)

Cause: @xterm/addon-webgl 0.19.0 never disposes WebglRenderer's cursor-blink manager (xtermjs/xterm.js#5818, fixed upstream but not in any stable release). Its 600 ms interval outlives the addon and keeps the renderer and the whole Terminal, scrollback included, alive. wmux releases WebGL on every hide and close of a terminal, so each hide or close of a focused terminal leaked one Terminal. This matches the report: memory grows while you work and stays flat when the app is left alone, it's mostly native memory, and closing workspaces frees nothing.

Fix: clear the blink manager before disposing the addon, on both the hide/close path and the context-loss path. The field is private, so the access is guarded. A test pins it to the shipped addon build and fails if the field is renamed, or once the addon fixes the leak itself and this workaround can be removed.

Evidence so far: an isolated 1-hour soak (3×3 panes running agent-style output, one workspace closed and reopened per cycle) reproduced the leak. Post-GC private memory grew about 14 MB per cycle, and 15 xterm cores were alive with 9 terminals open. Heap retainer paths for every leaked core ran through the blink interval.

Still to do before ready for review: finish the same soak with this fix and post the numbers. A separate PR will follow for a smaller hidden-window-only retention (one-shot rAFs never cancelled on unmount).

🤖 Generated with Claude Code

@ConnorRobinson13
Copy link
Copy Markdown
Contributor Author

Soak results with the fix: same isolated 1-hour run that reproduced the leak (3×3 panes of agent-style output, one workspace closed and reopened per cycle, window kept rendering).

before after
post-GC private bytes, cycles 3→6 258 → 300 MB (~+14 MB/cycle) 267 → 260 MB (flat)
JS heap, cycles 3→6 31.0 → 36.3 MB 28.9 → 28.7 MB
xterm cores alive at end (9 open) 15 9

The "after" build also included the separate hidden-window fix (follow-up PR). That one only acts while the window is occluded, which this run avoided, so the difference above is this change. Marking ready for review.

@ConnorRobinson13
ConnorRobinson13 marked this pull request as ready for review November 4, 2026 03:16
connor479 and others added 2 commits November 4, 2026 04:33
…e addon (amirlehmam#421)

@xterm/addon-webgl 0.19.0 never disposes WebglRenderer's cursor-blink
manager (xtermjs/xterm.js#5818). Its 600 ms interval outlives the addon
and keeps the renderer and the whole Terminal, scrollback included,
reachable. wmux releases WebGL on every hide and close, so each one
leaked a Terminal, and renderer memory climbed for as long as panes were
switched or closed.

Clear the manager before disposing the addon, on both the hide/close and
the context-loss paths. The field is private, so the access is guarded,
and a test pins it to the shipped build: it fails if the field is
renamed, or once the addon registers the manager itself and this
workaround can go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amirlehmam
amirlehmam force-pushed the fix/renderer-retention-421 branch from 744c065 to 11ec37d Compare November 4, 2026 03:33
@amirlehmam
Copy link
Copy Markdown
Owner

Thank you, this is the leak. I checked it against the shipped 0.19.0 build: _cursorBlinkStateManager is created with new and never _registered, so its 600 ms interval keeps the renderer, and through it the Terminal and its scrollback, alive after dispose(). Your soak numbers (9 cores alive for 9 terminals, flat post-GC private bytes) fit that.

2.55.1 moved every WebGL dispose into disposeWebglAddon (src/renderer/utils/webgl-release.ts), which conflicted with this branch. I rebased it and put the clear there, so the hide, close, rebuild and context-loss paths all go through it. Your test is unchanged and passes. It also fails when the clear line is removed. Merging, it ships in the next release.

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.

3 participants

0