Skip to content

Commit 7a6e724

Browse files
committed
Fix dispose leaks: register CursorBlinkStateManager + _pausedResizeTask
Three related disposable-registration gaps that leak xterm Terminal instances when the host application unmounts a Terminal. Evidence + full writeup at juspay/kolu#606. 1. addon-webgl/WebglRenderer._cursorBlinkStateManager was declared `= new MutableDisposable()` without the `this._register(...)` wrapper that every sibling MutableDisposable in the class uses. CursorBlinkStateManager runs a setInterval for the cursor blink and its `dispose()` correctly clears it — but dispose() was never called because the disposable wasn't registered. The live timer kept the renderer (and thus the Terminal) alive past host unmount. 2. common/TaskQueue.DebouncedIdleTask had no `dispose()` method. Added one that clears the internal IdleTaskQueue (cancelling any pending idle callback). 3. browser/services/RenderService._pausedResizeTask (a DebouncedIdleTask) was not registered. Wrapped with `this._register(...)` so RenderService.dispose propagates. Verified in a downstream app (kolu) with a repro that toggles Focus vs Canvas layout modes: - Pre-fix: 24/28 disposed xterm Terminals retained past host unmount + forced GC. - Post-fix: 0/28 retained. Retention chain (before fix): DOMTimer → ScheduledAction → V8Function → CursorBlinkStateManager._renderCallback → WebglRenderer → Terminal
1 parent 34e0179 commit 7a6e724

3 files changed

Lines changed: 6 additions & 2 deletions

File tree

addons/addon-webgl/src/WebglRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
3030

3131
export class WebglRenderer extends Disposable implements IRenderer {
3232
private _renderLayers: IRenderLayer[];
33-
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
33+
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this._register(new MutableDisposable());
3434
private _textBlinkStateManager: TextBlinkStateManager;
3535
private _charAtlasDisposable = this._register(new MutableDisposable());
3636
private _charAtlas: ITextureAtlas | undefined;

src/browser/services/RenderService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export class RenderService extends Disposable implements IRenderService {
6868
) {
6969
super();
7070

71-
this._pausedResizeTask = new DebouncedIdleTask(this._logService);
71+
this._pausedResizeTask = this._register(new DebouncedIdleTask(this._logService));
7272

7373
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end), this._coreBrowserService);
7474
this._register(this._renderDebouncer);

src/common/TaskQueue.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,8 @@ export class DebouncedIdleTask {
168168
public flush(): void {
169169
this._queue.flush();
170170
}
171+
172+
public dispose(): void {
173+
this._queue.clear();
174+
}
171175
}

0 commit comments

Comments
 (0)