Re #167719. Avoid heap operations when dealing with large files. (#193309)

This commit is contained in:
Peng Lyu 2023-09-18 13:02:27 -07:00 committed by GitHub
parent e6b8e0352e
commit 90854deeb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 5 deletions

View file

@ -840,6 +840,13 @@ export interface ITextModel {
*/
isTooLargeForTokenization(): boolean;
/**
* The file is so large, that operations on it might be too large for heap
* and can lead to OOM crashes so they should be disabled.
* @internal
*/
isTooLargeForHeapOperation(): boolean;
/**
* Search the model.
* @param searchString The string used to search. If it is a regular expression, set `isRegex` to true.

View file

@ -177,6 +177,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
static _MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB, // used in tests
private static readonly LARGE_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; // 20 MB;
private static readonly LARGE_FILE_LINE_COUNT_THRESHOLD = 300 * 1000; // 300K lines
private static readonly LARGE_FILE_HEAP_OPERATION_THRESHOLD = 256 * 1024 * 1024; // 256M characters, usually ~> 512MB memory usage
public static DEFAULT_CREATION_OPTIONS: model.ITextModelCreationOptions = {
isForSimpleWidget: false,
@ -257,6 +258,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
private _initialUndoRedoSnapshot: ResourceEditStackSnapshot | null;
private readonly _isTooLargeForSyncing: boolean;
private readonly _isTooLargeForTokenization: boolean;
private readonly _isTooLargeForHeapOperation: boolean;
//#region Editing
private readonly _commandManager: EditStack;
@ -345,8 +347,11 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
(bufferTextLength > TextModel.LARGE_FILE_SIZE_THRESHOLD)
|| (bufferLineCount > TextModel.LARGE_FILE_LINE_COUNT_THRESHOLD)
);
this._isTooLargeForHeapOperation = bufferTextLength > TextModel.LARGE_FILE_HEAP_OPERATION_THRESHOLD;
} else {
this._isTooLargeForTokenization = false;
this._isTooLargeForHeapOperation = false;
}
this._isTooLargeForSyncing = (bufferTextLength > TextModel._MODEL_SYNC_LIMIT);
@ -587,6 +592,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
return this._isTooLargeForTokenization;
}
public isTooLargeForHeapOperation(): boolean {
return this._isTooLargeForHeapOperation;
}
public isDisposed(): boolean {
return this._isDisposed;
}
@ -743,6 +752,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
public getValue(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): string {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
const fullModelRange = this.getFullModelRange();
const fullModelValue = this.getValueInRange(fullModelRange, eol);
@ -809,6 +822,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
public getLinesContent(): string[] {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
return this._buffer.getLinesContent();
}

View file

@ -97,6 +97,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
protected readonly _storageService: IStorageService;
private readonly _clipboardService: IClipboardService;
protected readonly _contextKeyService: IContextKeyService;
protected readonly _notificationService: INotificationService;
get editor() {
return this._editor;
@ -110,7 +111,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
@IClipboardService clipboardService: IClipboardService,
@INotificationService notificationService: INotificationService
) {
super();
this._editor = editor;
@ -118,6 +120,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
this._contextKeyService = contextKeyService;
this._storageService = storageService;
this._clipboardService = clipboardService;
this._notificationService = notificationService;
this._updateHistoryDelayer = new Delayer<void>(500);
this._state = this._register(new FindReplaceState());
@ -390,6 +393,10 @@ export class CommonFindController extends Disposable implements IEditorContribut
public replaceAll(): boolean {
if (this._model) {
if (this._editor.getModel()?.isTooLargeForHeapOperation()) {
this._notificationService.warn(nls.localize('too.large.for.replaceall', "The file is too large to perform a replace all operation."));
return false;
}
this._model.replaceAll();
return true;
}
@ -437,11 +444,11 @@ export class FindController extends CommonFindController implements IFindControl
@IContextKeyService _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@INotificationService private readonly _notificationService: INotificationService,
@INotificationService notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, _storageService, clipboardService);
super(editor, _contextKeyService, _storageService, clipboardService, notificationService);
this._widget = null;
this._findOptionsWidget = null;
}

View file

@ -20,6 +20,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
class TestFindController extends CommonFindController {
@ -33,9 +34,10 @@ class TestFindController extends CommonFindController {
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
@IClipboardService clipboardService: IClipboardService,
@INotificationService notificationService: INotificationService
) {
super(editor, contextKeyService, storageService, clipboardService);
super(editor, contextKeyService, storageService, clipboardService, notificationService);
this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService);
this._updateHistoryDelayer = new Delayer<void>(50);
this.hasFocus = false;