From 785b932d75912fb973df6f5b870b384e09aea90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 28 Sep 2022 15:57:58 +0200 Subject: [PATCH] lazy cleanup on buffer --- src/common/Types.d.ts | 1 + src/common/buffer/Buffer.ts | 29 +++++++++++++++++++++++++++-- src/common/buffer/BufferLine.ts | 8 ++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index bbf00f1b..c4c470ad 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -206,6 +206,7 @@ export interface IBufferLine { deleteCells(pos: number, n: number, fill: ICellData, eraseAttr?: IAttributeData): void; replaceCells(start: number, end: number, fill: ICellData, eraseAttr?: IAttributeData, respectProtect?: boolean): void; resize(cols: number, fill: ICellData): boolean; + cleanupMemory(): number; fill(fillCellData: ICellData, respectProtect?: boolean): void; copyFrom(line: IBufferLine): void; clone(): IBufferLine; diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index c8b0d1b2..83866304 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -14,6 +14,7 @@ import { Marker } from 'common/buffer/Marker'; import { IOptionsService, IBufferService } from 'common/services/Services'; import { DEFAULT_CHARSET } from 'common/data/Charsets'; import { ExtendedAttrs } from 'common/buffer/AttributeData'; +import { DebouncedIdleTask } from 'common/TaskQueue'; export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1 @@ -151,6 +152,9 @@ export class Buffer implements IBuffer { // store reference to null cell with default attrs const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); + // defer memory cleanup of bufferlines + let needsCleanup = 0; + // Increase max length if needed before adjustments to allow space to fill // as required. const newMaxLength = this._getCorrectBufferLength(newRows); @@ -164,7 +168,7 @@ export class Buffer implements IBuffer { // Deal with columns increasing (reducing needs to happen after reflow) if (this._cols < newCols) { for (let i = 0; i < this.lines.length; i++) { - this.lines.get(i)!.resize(newCols, nullCell); + needsCleanup |= +this.lines.get(i)!.resize(newCols, nullCell); } } @@ -243,13 +247,34 @@ export class Buffer implements IBuffer { // Trim the end of the line off if cols shrunk if (this._cols > newCols) { for (let i = 0; i < this.lines.length; i++) { - this.lines.get(i)!.resize(newCols, nullCell); + needsCleanup |= +this.lines.get(i)!.resize(newCols, nullCell); } } } this._cols = newCols; this._rows = newRows; + + if (needsCleanup) { + this._memoryCleanupTask.set(() => this._cleanupMemory()); + } else { + // FIXME: DebouncedIdleTask has no clear method? + this._memoryCleanupTask.set(() => {}); + } + } + + private _memoryCleanupTask: DebouncedIdleTask = new DebouncedIdleTask(); + + private _cleanupMemory(): void { + let counted = 0; + for (let i = 0; i < this.lines.length; i++) { + counted += this.lines.get(i)!.cleanupMemory(); + // throttle to 5k lines + if (counted > 5000) { + this._memoryCleanupTask.set(() => this._cleanupMemory()); + break; + } + } } private get _isReflowEnabled(): boolean { diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 2805f3c8..2e4fbca9 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -347,7 +347,7 @@ export class BufferLine implements IBufferLine { */ public resize(cols: number, fillCellData: ICellData): boolean { if (cols === this.length) { - return this._data.length * 4 < this._data.buffer.byteLength * CLEANUP_THRESHOLD; + return this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength; } const uint32Cells = cols * CELL_SIZE; if (cols > this.length) { @@ -384,7 +384,7 @@ export class BufferLine implements IBufferLine { } } this.length = cols; - return uint32Cells * 4 < this._data.buffer.byteLength * CLEANUP_THRESHOLD; + return uint32Cells * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength; } /** @@ -393,8 +393,8 @@ export class BufferLine implements IBufferLine { * memory by a factor of CLEANUP_THRESHOLD. * Returns 0 or 1 indicating whether a cleanup happened. */ - public cleanupBuffer(): number { - if (this._data.length * 4 < this._data.buffer.byteLength * CLEANUP_THRESHOLD) { + public cleanupMemory(): number { + if (this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength) { const data = new Uint32Array(this._data.length); data.set(this._data); this._data = data;