@@ -23,6 +23,7 @@ import { IFileService } from '../../../../platform/files/common/files.js';
23
23
import { IEditorResolverService , RegisteredEditorPriority } from '../../editor/common/editorResolverService.js' ;
24
24
import { Disposable } from '../../../../base/common/lifecycle.js' ;
25
25
import { InstantiationType , registerSingleton } from '../../../../platform/instantiation/common/extensions.js' ;
26
+ import { onUnexpectedError } from '../../../../base/common/errors.js' ;
26
27
27
28
export const ITextEditorService = createDecorator < ITextEditorService > ( 'textEditorService' ) ;
28
29
@@ -55,6 +56,16 @@ export interface ITextEditorService {
55
56
resolveTextEditor ( input : IUntypedFileEditorInput ) : Promise < IFileEditorInput > ;
56
57
}
57
58
59
+ class FileEditorInputLeakError extends Error {
60
+
61
+ constructor ( message : string , stack : string ) {
62
+ super ( message ) ;
63
+
64
+ this . name = 'FileEditorInputLeakError' ;
65
+ this . stack = stack ;
66
+ }
67
+ }
68
+
58
69
export class TextEditorService extends Disposable implements ITextEditorService {
59
70
60
71
declare readonly _serviceBrand : undefined ;
@@ -247,10 +258,65 @@ export class TextEditorService extends Disposable implements ITextEditorService
247
258
// Otherwise create and add to cache
248
259
input = factoryFn ( ) ;
249
260
this . editorInputCache . set ( resource , input ) ;
250
- Event . once ( input . onWillDispose ) ( ( ) => this . editorInputCache . delete ( resource ) ) ;
261
+
262
+ // Track Leaks
263
+ const leakId = this . trackLeaks ( input ) ;
264
+
265
+ Event . once ( input . onWillDispose ) ( ( ) => {
266
+
267
+ // Remove from cache
268
+ this . editorInputCache . delete ( resource ) ;
269
+
270
+ // Untrack Leaks
271
+ if ( leakId ) {
272
+ this . untrackLeaks ( leakId ) ;
273
+ }
274
+ } ) ;
251
275
252
276
return input ;
253
277
}
278
+
279
+ //#region Leak Monitoring
280
+
281
+ private static readonly LEAK_TRACKING_THRESHOLD = 256 ;
282
+ private static readonly LEAK_REPORTING_THRESHOLD = 2 * this . LEAK_TRACKING_THRESHOLD ;
283
+ private static LEAK_REPORTED = false ;
284
+
285
+ private readonly mapLeakToCounter = new Map < string , number > ( ) ;
286
+
287
+ private trackLeaks ( input : TextResourceEditorInput | IFileEditorInput | UntitledTextEditorInput ) : string | undefined {
288
+ if ( TextEditorService . LEAK_REPORTED || this . editorInputCache . size < TextEditorService . LEAK_TRACKING_THRESHOLD ) {
289
+ return undefined ;
290
+ }
291
+
292
+ const leakId = `${ input . resource . scheme } #${ input . typeId || '<no typeId>' } #${ input . editorId || '<no editorId>' } \n${ new Error ( ) . stack ?. split ( '\n' ) . slice ( 2 ) . join ( '\n' ) ?? '' } ` ;
293
+ const leakCounter = ( this . mapLeakToCounter . get ( leakId ) ?? 0 ) + 1 ;
294
+ this . mapLeakToCounter . set ( leakId , leakCounter ) ;
295
+
296
+ if ( this . editorInputCache . size > TextEditorService . LEAK_REPORTING_THRESHOLD ) {
297
+ TextEditorService . LEAK_REPORTED = true ;
298
+
299
+ const [ topLeak , topCount ] = Array . from ( this . mapLeakToCounter . entries ( ) ) . reduce (
300
+ ( [ topLeak , topCount ] , [ key , val ] ) => val > topCount ? [ key , val ] : [ topLeak , topCount ]
301
+ ) ;
302
+
303
+ const message = `Potential text editor input LEAK detected, having ${ this . editorInputCache . size } text editor inputs already. Most frequent owner (${ topCount } )` ;
304
+ onUnexpectedError ( new FileEditorInputLeakError ( message , topLeak ) ) ;
305
+ }
306
+
307
+ return leakId ;
308
+ }
309
+
310
+ private untrackLeaks ( leakId : string ) : void {
311
+ const stackCounter = ( this . mapLeakToCounter . get ( leakId ) ?? 1 ) - 1 ;
312
+ this . mapLeakToCounter . set ( leakId , stackCounter ) ;
313
+
314
+ if ( stackCounter === 0 ) {
315
+ this . mapLeakToCounter . delete ( leakId ) ;
316
+ }
317
+ }
318
+
319
+ //#endregion
254
320
}
255
321
256
322
registerSingleton ( ITextEditorService , TextEditorService , InstantiationType . Eager /* do not change: https://github.com/microsoft/vscode/issues/137675 */ ) ;
0 commit comments