Skip to content

Commit c7ab305

Browse files
authored
fix(xterm): patch dispose leaks via juspay/xterm.js fork (upstream xtermjs/xterm.js#5817) (#609)
Closes #606. Part of #610 (master tracking). Follow-up to #607. After the Kolu-side addon-null cleanup reduced orphaned xterm Terminals from 24 → 6, heap-snapshot BFS-from-root traced all 6 survivors to one xterm-internal retainer chain: ``` DOMTimer → ScheduledAction → V8Function → CursorBlinkStateManager._renderCallback → WebglRenderer → Terminal ``` Three disposables in xterm.js are created but never registered: 1. **`addon-webgl/WebglRenderer._cursorBlinkStateManager`** — declared `= new MutableDisposable()` without `this._register(...)`. Its internal `setInterval` for the cursor blink survives `WebglRenderer.dispose()`, pinning the renderer. 2. **`xterm/TaskQueue.DebouncedIdleTask`** — no `dispose()` method at all. 3. **`xterm/RenderService._pausedResizeTask`** — not registered. Full writeup: [`docs/perf-investigations/mode-toggle-leak.md`](https://github.com/juspay/kolu/blob/master/docs/perf-investigations/mode-toggle-leak.md). ## Upstream - **Issue**: xtermjs/xterm.js#5818 — problem description + heap evidence - **PR**: xtermjs/xterm.js#5817 — 6-line source fix, ready for review ## Approach — fork via pnpm overrides This PR consumes the fix via the **juspay/xterm.js** fork until upstream merges + releases. Two commits on this branch tell the story: 1. **`98df21e` — interim fix via `pnpm patch`** (historical record; reverted in the next commit). Large minified-`.mjs` diff files, worked but not maintainable. 2. **`dfa2b5b` — switch to fork via git URL** (final approach): - `package.json` adds `pnpm.overrides` for `@xterm/xterm` and `@xterm/addon-webgl` pointing at `github:juspay/xterm.js#fix/dispose-leaks-built` (the `&path:/addons/addon-webgl` selector pulls the addon from the monorepo subdirectory). - Fork branch `fix/dispose-leaks-built` commits both the `.ts` source fix and pre-built `.mjs` bundles so pnpm consumes them directly — no build step during `pnpm install`, works in Nix's restricted-network sandbox. - `patches/@xterm__*.patch` deleted. - `default.nix` `fetchPnpmDeps` hash bumped. Once `xtermjs/xterm.js#5817` merges and a new xterm release ships, the overrides collapse to a one-line version bump in `package.json`. ## Verification | Stage | Rn | yn disposed-retained | | -------------------------------------- | --- | -------------------- | | Pre-any-fix (after forced GC) | 29 | 24 | | After #607 only | 11 | 6 | | **After this PR (full fix via fork)** | 5 | **0** | Residual 1 `Rn` above live-count is a transitional instance freshly created during restore. - `nix build .#default` ✓ end-to-end build with fork via `fetchPnpmDeps` tarball URL. - `just check`, `just fmt` ✓ - Production verified on pre-fork-patch builds: `aliveDetached: 0, gced: 54/55` after extensive mode toggling. - Dev-server repro with fork: 6 mode toggles → `aliveDetached: 0`, `gced: 7/8`. ## Test plan - [x] typecheck + fmt - [x] heap-snapshot reproduction against fork confirms zero disposed-retained xterm Terminals - [x] full `nix build .#default` succeeds - [x] upstream issue + PR filed - [ ] CI green across all systems 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 461e167 commit c7ab305

8 files changed

Lines changed: 526 additions & 431 deletions

File tree

.claude/skills/perf-diagnose/SKILL.md

Lines changed: 95 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agents/.apm/skills/perf-diagnose/SKILL.md

Lines changed: 95 additions & 53 deletions
Large diffs are not rendered by default.

default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ let
4646
# hash-fresh` enforces this stays in sync with pnpm-lock.yaml by forcing
4747
# fetchPnpmDeps to re-execute (--rebuild), so stale artifacts in the
4848
# binary cache can't silently satisfy a hash that no longer matches.
49-
hash = "sha256-oIcLBubUOtmUuWL3aQUcfMkv7sAAos5HhGvzB3OPYZQ=";
49+
hash = "sha256-B+bX+aTMJScRbZC55ATPAnL8tJG5QAm6fkkABpTaljY=";
5050
fetcherVersion = 3;
5151
};
5252

docs/perf-investigations/memory-learnings.md

Lines changed: 316 additions & 0 deletions
Large diffs are not rendered by default.

docs/perf-investigations/mode-toggle-leak.md

Lines changed: 0 additions & 183 deletions
This file was deleted.

0 commit comments

Comments
 (0)