From 3fbe07bc561e135df4e9939923e7e8d5e4ee2b98 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 6 Sep 2016 15:56:47 +0200 Subject: [PATCH 1/6] some gc sync'ing between main and ext host --- src/typings/gc-signals.d.ts | 19 ++++++ src/vs/workbench/api/node/extHost.api.impl.ts | 4 +- .../api/node/extHost.contribution.ts | 2 + src/vs/workbench/api/node/extHost.protocol.ts | 5 ++ .../workbench/api/node/extHostHeapMonitor.ts | 62 +++++++++++++++++++ .../api/node/extHostLanguageFeatures.ts | 34 +++++----- .../api/node/mainThreadHeapMonitor.ts | 38 ++++++++++++ .../api/node/mainThreadLanguageFeatures.ts | 8 ++- .../test/node/api/extHostApiCommands.test.ts | 3 +- .../node/api/extHostLanguageFeatures.test.ts | 3 +- 10 files changed, 156 insertions(+), 22 deletions(-) create mode 100644 src/typings/gc-signals.d.ts create mode 100644 src/vs/workbench/api/node/extHostHeapMonitor.ts create mode 100644 src/vs/workbench/api/node/mainThreadHeapMonitor.ts diff --git a/src/typings/gc-signals.d.ts b/src/typings/gc-signals.d.ts new file mode 100644 index 00000000000..cc982d1260c --- /dev/null +++ b/src/typings/gc-signals.d.ts @@ -0,0 +1,19 @@ +declare module 'gc-signals' { + export interface GCSignal { + } + /** + * Create a new GC signal. When being garbage collected the passed + * value is stored for later consumption. + */ + export const GCSignal: { + new (id: number): GCSignal; + }; + /** + * Consume ids of garbage collected signals. + */ + export function consumeSignals(): number[]; + export function onDidGarbageCollectSignals(callback: (ids: number[]) => any): { + dispose(): void; + }; + export function trackGarbageCollection(obj: any, id: number): number; +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index f4c1b144ae0..90236ba0053 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -17,6 +17,7 @@ import {ExtHostConfiguration} from 'vs/workbench/api/node/extHostConfiguration'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; import {ExtHostWorkspace} from 'vs/workbench/api/node/extHostWorkspace'; import {ExtHostQuickOpen} from 'vs/workbench/api/node/extHostQuickOpen'; +import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; import {ExtHostStatusBar} from 'vs/workbench/api/node/extHostStatusBar'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostOutputService} from 'vs/workbench/api/node/extHostOutputService'; @@ -99,12 +100,13 @@ export class ExtHostAPIImplementation { // Addressable instances const col = new InstanceCollection(); + const extHostHeapMonitor = col.define(ExtHostContext.ExtHostHeapMonitor).set(new ExtHostHeapMonitor()); const extHostDocuments = col.define(ExtHostContext.ExtHostDocuments).set(new ExtHostDocuments(threadService)); const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set(new ExtHostEditors(threadService, extHostDocuments)); const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set(new ExtHostCommands(threadService, extHostEditors)); const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration))); const extHostDiagnostics = col.define(ExtHostContext.ExtHostDiagnostics).set(new ExtHostDiagnostics(threadService)); - const languageFeatures = col.define(ExtHostContext.ExtHostLanguageFeatures).set(new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostDiagnostics)); + const languageFeatures = col.define(ExtHostContext.ExtHostLanguageFeatures).set(new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapMonitor, extHostDiagnostics)); const extHostFileSystemEvent = col.define(ExtHostContext.ExtHostFileSystemEventService).set(new ExtHostFileSystemEventService()); const extHostQuickOpen = col.define(ExtHostContext.ExtHostQuickOpen).set(new ExtHostQuickOpen(threadService)); col.define(ExtHostContext.ExtHostExtensionService).set(extensionService); diff --git a/src/vs/workbench/api/node/extHost.contribution.ts b/src/vs/workbench/api/node/extHost.contribution.ts index 639227e8ee1..43fce942a35 100644 --- a/src/vs/workbench/api/node/extHost.contribution.ts +++ b/src/vs/workbench/api/node/extHost.contribution.ts @@ -32,6 +32,7 @@ import {MainThreadTerminalService} from './mainThreadTerminalService'; import {MainThreadWorkspace} from './mainThreadWorkspace'; import {MainProcessExtensionService} from './mainThreadExtensionService'; import {MainThreadFileSystemEventService} from './mainThreadFileSystemEventService'; +import {MainThreadHeapMonitor} from './mainThreadHeapMonitor'; // --- other interested parties import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax'; @@ -87,6 +88,7 @@ export class ExtHostContribution implements IWorkbenchContribution { create(JSONValidationExtensionPoint); create(LanguageConfigurationFileHandler); create(MainThreadFileSystemEventService); + create(MainThreadHeapMonitor); } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 06a1389dc30..2f5fa81afee 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -265,6 +265,10 @@ export abstract class ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents) { throw ni(); } } +export abstract class ExtHostHeapMonitorShape { + $onGarbageCollection(ids: number[]): void { throw ni(); } +} + export abstract class ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: URI): TPromise { throw ni(); } $provideCodeLenses(handle: number, resource: URI): TPromise { throw ni(); } @@ -321,6 +325,7 @@ export const ExtHostContext = { ExtHostDocuments: createExtId('ExtHostDocuments', ExtHostDocumentsShape), ExtHostEditors: createExtId('ExtHostEditors', ExtHostEditorsShape), ExtHostFileSystemEventService: createExtId('ExtHostFileSystemEventService', ExtHostFileSystemEventServiceShape), + ExtHostHeapMonitor: createExtId('ExtHostHeapMonitor', ExtHostHeapMonitorShape), ExtHostLanguageFeatures: createExtId('ExtHostLanguageFeatures', ExtHostLanguageFeaturesShape), ExtHostQuickOpen: createExtId('ExtHostQuickOpen', ExtHostQuickOpenShape), ExtHostExtensionService: createExtId('ExtHostExtensionService', ExtHostExtensionServiceShape), diff --git a/src/vs/workbench/api/node/extHostHeapMonitor.ts b/src/vs/workbench/api/node/extHostHeapMonitor.ts new file mode 100644 index 00000000000..7f6bfae86ea --- /dev/null +++ b/src/vs/workbench/api/node/extHostHeapMonitor.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import {ExtHostHeapMonitorShape} from './extHost.protocol'; + +export class ExtHostHeapMonitor extends ExtHostHeapMonitorShape { + + private static _idPool = 0; + + private _data: { [n: number]: any } = Object.create(null); + private _callbacks: { [n: number]: Function } = Object.create(null); + + private _mixinObjectIdentifier(obj: any): number { + const id = ExtHostHeapMonitor._idPool++; + + Object.defineProperties(obj, { + '$heap_ident': { + value: id, + enumerable: true, + configurable: false, + writable: false + }, + '$mid': { + value: 3, + enumerable: true, + configurable: false, + writable: false + } + }); + + return id; + } + + linkObjects(external: any, internal: any, callback?: () => any) { + const id = this._mixinObjectIdentifier(external); + this._data[id] = internal; + if (typeof callback === 'function') { + this._callbacks[id] = callback; + } + } + + getInternalObject(external: any): T { + const id = external.$heap_ident; + if (typeof id === 'number') { + return this._data[id]; + } + } + + $onGarbageCollection(ids: number[]): void { + for (const id of ids) { + delete this._data[id]; + const callback = this._callbacks[id]; + if (callback) { + delete this._callbacks[id]; + setTimeout(callback); + } + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index e0e5db8ee61..9852b157bb2 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -10,9 +10,10 @@ import {IDisposable, dispose} from 'vs/base/common/lifecycle'; import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; import * as vscode from 'vscode'; import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; -import {Range, Disposable, CompletionList} from 'vs/workbench/api/node/extHostTypes'; +import {Range, Disposable, CompletionList, CompletionItem} from 'vs/workbench/api/node/extHostTypes'; import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; +import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; @@ -503,11 +504,12 @@ interface ISuggestion2 extends modes.ISuggestion { class SuggestAdapter { private _documents: ExtHostDocuments; + private _heapMonitor: ExtHostHeapMonitor; private _provider: vscode.CompletionItemProvider; - private _cache: { [key: string]: { list: CompletionList; disposables: IDisposable[]; } } = Object.create(null); - constructor(documents: ExtHostDocuments, provider: vscode.CompletionItemProvider) { + constructor(documents: ExtHostDocuments, heapMonitor: ExtHostHeapMonitor, provider: vscode.CompletionItemProvider) { this._documents = documents; + this._heapMonitor = heapMonitor; this._provider = provider; } @@ -516,12 +518,6 @@ class SuggestAdapter { const doc = this._documents.getDocumentData(resource).document; const pos = TypeConverters.toPosition(position); - const key = resource.toString(); - if (this._cache[key]) { - dispose(this._cache[key].disposables); - delete this._cache[key]; - } - return asWinJsPromise(token => this._provider.provideCompletionItems(doc, pos, token)).then(value => { const result: modes.ISuggestResult = { @@ -533,7 +529,6 @@ class SuggestAdapter { const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) || new Range(pos, pos)) .with({ end: pos }); - const disposables: IDisposable[] = []; let list: CompletionList; if (!value) { // undefined and null are valid results @@ -550,8 +545,11 @@ class SuggestAdapter { for (let i = 0; i < list.items.length; i++) { const item = list.items[i]; + const disposables: IDisposable[] = []; const suggestion = TypeConverters.Suggest.from(item, disposables); + this._heapMonitor.linkObjects(suggestion, item, () => dispose(disposables)); + if (item.textEdit) { const editRange = item.textEdit.range; @@ -581,25 +579,22 @@ class SuggestAdapter { result.suggestions.push(suggestion); } - // cache for details call - this._cache[key] = { list, disposables }; - return result; }); } resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise { - if (typeof this._provider.resolveCompletionItem !== 'function' || !this._cache[resource.toString()]) { + + if (typeof this._provider.resolveCompletionItem !== 'function') { return TPromise.as(suggestion); } - const {list, disposables} = this._cache[resource.toString()]; - const item = list.items[Number(( suggestion).id)]; + const item = this._heapMonitor.getInternalObject(suggestion); if (!item) { return TPromise.as(suggestion); } return asWinJsPromise(token => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => { - return TypeConverters.Suggest.from(resolvedItem || item, disposables); + return TypeConverters.Suggest.from(resolvedItem || item, []); }); } } @@ -670,6 +665,7 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { private _proxy: MainThreadLanguageFeaturesShape; private _documents: ExtHostDocuments; private _commands: ExtHostCommands; + private _heapMonitor: ExtHostHeapMonitor; private _diagnostics: ExtHostDiagnostics; private _adapter: { [handle: number]: Adapter } = Object.create(null); @@ -677,12 +673,14 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { threadService: IThreadService, documents: ExtHostDocuments, commands: ExtHostCommands, + heapMonitor: ExtHostHeapMonitor, diagnostics: ExtHostDiagnostics ) { super(); this._proxy = threadService.get(MainContext.MainThreadLanguageFeatures); this._documents = documents; this._commands = commands; + this._heapMonitor = heapMonitor; this._diagnostics = diagnostics; } @@ -869,7 +867,7 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { const handle = this._nextHandle(); - this._adapter[handle] = new SuggestAdapter(this._documents, provider); + this._adapter[handle] = new SuggestAdapter(this._documents, this._heapMonitor, provider); this._proxy.$registerSuggestSupport(handle, selector, triggerCharacters); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/mainThreadHeapMonitor.ts b/src/vs/workbench/api/node/mainThreadHeapMonitor.ts new file mode 100644 index 00000000000..101ca35d9ea --- /dev/null +++ b/src/vs/workbench/api/node/mainThreadHeapMonitor.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import {IDisposable} from 'vs/base/common/lifecycle'; +import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; +import {ExtHostContext} from './extHost.protocol'; +import {onDidGarbageCollectSignals, consumeSignals, trackGarbageCollection} from 'gc-signals'; + +export class MainThreadHeapMonitor { + + private _subscription: IDisposable; + private _consumeHandle: number; + + constructor( @IThreadService threadService: IThreadService) { + const proxy = threadService.get(ExtHostContext.ExtHostHeapMonitor); + + this._subscription = onDidGarbageCollectSignals(ids => { + proxy.$onGarbageCollection(ids); + }); + + this._consumeHandle = setInterval(consumeSignals, 15 * 1000); + } + + dispose() { + clearInterval(this._consumeHandle); + this._subscription.dispose(); + } + + trackObject(obj: any) { + if (typeof obj.$heap_ident === 'number') { + trackGarbageCollection(obj, obj.$heap_ident); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index 4df3d0f6dc9..f9776ca8a3e 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -17,6 +17,7 @@ import {Position as EditorPosition} from 'vs/editor/common/core/position'; import {Range as EditorRange} from 'vs/editor/common/core/range'; import {ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape} from './extHost.protocol'; import {LanguageConfigurationRegistry, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry'; +import {trackGarbageCollection} from 'gc-signals'; export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape { @@ -180,7 +181,12 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape this._registrations[handle] = modes.SuggestRegistry.register(selector, { triggerCharacters: triggerCharacters, provideCompletionItems: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable => { - return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)); + return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)).then(result => { + for (const suggestion of result.suggestions) { + trackGarbageCollection(suggestion, (suggestion).$heap_ident); + } + return result; + }); }, resolveCompletionItem: (model:IReadOnlyModel, position:EditorPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable => { return wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion)); diff --git a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts index 168c41180da..f80a1bde549 100644 --- a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts @@ -23,6 +23,7 @@ import {ExtHostLanguageFeatures} from 'vs/workbench/api/node/extHostLanguageFeat import {MainThreadLanguageFeatures} from 'vs/workbench/api/node/mainThreadLanguageFeatures'; import {registerApiCommands} from 'vs/workbench/api/node/extHostApiCommands'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; +import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; import {MainThreadCommands} from 'vs/workbench/api/node/mainThreadCommands'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; import * as ExtHostTypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; @@ -108,7 +109,7 @@ suite('ExtHostLanguageFeatureCommands', function() { const diagnostics = new ExtHostDiagnostics(threadService); threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, diagnostics); + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapMonitor(), diagnostics); threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, instantiationService.createInstance(MainThreadLanguageFeatures)); diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index f4e2808920f..7cdb832bbbc 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -39,6 +39,7 @@ import {getLinks} from 'vs/editor/contrib/links/common/links'; import {asWinJsPromise} from 'vs/base/common/async'; import {MainContext, ExtHostContext} from 'vs/workbench/api/node/extHost.protocol'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; +import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; const defaultSelector = { scheme: 'far' }; const model: EditorCommon.IModel = EditorModel.createFromString( @@ -97,7 +98,7 @@ suite('ExtHostLanguageFeatures', function() { const diagnostics = new ExtHostDiagnostics(threadService); threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, diagnostics); + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapMonitor(), diagnostics); threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, instantiationService.createInstance(MainThreadLanguageFeatures)); From 59d8e568db6deeb05280bd0e291b33cca2b2031b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 6 Sep 2016 16:13:32 +0200 Subject: [PATCH 2/6] update package dependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a98d65a865a..953ef7f7aeb 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "applicationinsights": "0.15.6", "chokidar": "bpasero/chokidar#vscode", "emmet": "1.3.1", + "gc-signals": "^0.0.1", "getmac": "1.0.7", "graceful-fs": "4.1.2", "http-proxy-agent": "0.2.7", From b725c7120fff9ab6594e615c43c688f77282f42f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 6 Sep 2016 17:11:55 +0200 Subject: [PATCH 3/6] update shrinkwrap file --- npm-shrinkwrap.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 61cb690f2ab..4266bd833e9 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -152,6 +152,11 @@ "from": "bpasero/fsevents#vscode", "resolved": "git://github.com/bpasero/fsevents.git#fe2aaccaaffbd69a23374cf46a8c6bafe8e51b01" }, + "gc-signals": { + "version": "0.0.1", + "from": "gc-signals@0.0.1", + "resolved": "https://registry.npmjs.org/gc-signals/-/gc-signals-0.0.1.tgz" + }, "getmac": { "version": "1.0.7", "from": "getmac@1.0.7", From 21820b36c192ee1d5c0a7faaa32a819e4a90e999 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Sep 2016 10:06:03 +0200 Subject: [PATCH 4/6] simplify API --- src/vs/workbench/api/node/extHost.protocol.ts | 14 ++++++ .../workbench/api/node/extHostHeapMonitor.ts | 46 +++++-------------- .../api/node/extHostLanguageFeatures.ts | 18 +++----- .../api/node/mainThreadHeapMonitor.ts | 8 +--- .../api/node/mainThreadLanguageFeatures.ts | 4 +- 5 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 2f5fa81afee..f6afe370227 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -265,6 +265,20 @@ export abstract class ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents) { throw ni(); } } +export interface ObjectIdentifier { + $ident: number; +} + +export namespace ObjectIdentifier { + export function mixin(obj: T, id: number): T & ObjectIdentifier { + Object.defineProperty(obj, '$ident', { value: id, enumerable: true }); + return obj; + } + export function get(obj: any): number { + return obj['$ident']; + } +} + export abstract class ExtHostHeapMonitorShape { $onGarbageCollection(ids: number[]): void { throw ni(); } } diff --git a/src/vs/workbench/api/node/extHostHeapMonitor.ts b/src/vs/workbench/api/node/extHostHeapMonitor.ts index 7f6bfae86ea..af81049ff0d 100644 --- a/src/vs/workbench/api/node/extHostHeapMonitor.ts +++ b/src/vs/workbench/api/node/extHostHeapMonitor.ts @@ -13,50 +13,28 @@ export class ExtHostHeapMonitor extends ExtHostHeapMonitorShape { private _data: { [n: number]: any } = Object.create(null); private _callbacks: { [n: number]: Function } = Object.create(null); - private _mixinObjectIdentifier(obj: any): number { + keep(obj:any, callback?:() => any): number { const id = ExtHostHeapMonitor._idPool++; - - Object.defineProperties(obj, { - '$heap_ident': { - value: id, - enumerable: true, - configurable: false, - writable: false - }, - '$mid': { - value: 3, - enumerable: true, - configurable: false, - writable: false - } - }); - - return id; - } - - linkObjects(external: any, internal: any, callback?: () => any) { - const id = this._mixinObjectIdentifier(external); - this._data[id] = internal; + this._data[id] = obj; if (typeof callback === 'function') { this._callbacks[id] = callback; } + return id; } - getInternalObject(external: any): T { - const id = external.$heap_ident; - if (typeof id === 'number') { - return this._data[id]; - } + delete(id: number): boolean { + delete this._callbacks[id]; + return this._data[id]; + } + + get(id: number): T { + return this._data[id]; } $onGarbageCollection(ids: number[]): void { for (const id of ids) { - delete this._data[id]; - const callback = this._callbacks[id]; - if (callback) { - delete this._callbacks[id]; - setTimeout(callback); - } + setTimeout(this._callbacks[id]); + this.delete(id); } } } \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 9852b157bb2..2ec8654b624 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -19,7 +19,7 @@ import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; import {IWorkspaceSymbolProvider, IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import {asWinJsPromise, ShallowCancelThenPromise} from 'vs/base/common/async'; -import {MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape} from './extHost.protocol'; +import {MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier} from './extHost.protocol'; import {regExpLeadsToEndlessLoop} from 'vs/base/common/strings'; // --- adapter @@ -497,9 +497,6 @@ class RenameAdapter { } } -interface ISuggestion2 extends modes.ISuggestion { - id: string; -} class SuggestAdapter { @@ -546,9 +543,11 @@ class SuggestAdapter { const item = list.items[i]; const disposables: IDisposable[] = []; - const suggestion = TypeConverters.Suggest.from(item, disposables); + const suggestion = TypeConverters.Suggest.from(item, disposables); - this._heapMonitor.linkObjects(suggestion, item, () => dispose(disposables)); + // assign identifier to suggestion + const id = this._heapMonitor.keep(item, () => dispose(disposables)); + ObjectIdentifier.mixin(suggestion, id); if (item.textEdit) { @@ -572,9 +571,6 @@ class SuggestAdapter { suggestion.overwriteAfter = 0; } - // assign identifier to suggestion - suggestion.id = String(i); - // store suggestion result.suggestions.push(suggestion); } @@ -589,12 +585,12 @@ class SuggestAdapter { return TPromise.as(suggestion); } - const item = this._heapMonitor.getInternalObject(suggestion); + const item = this._heapMonitor.get(ObjectIdentifier.get(suggestion)); if (!item) { return TPromise.as(suggestion); } return asWinJsPromise(token => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => { - return TypeConverters.Suggest.from(resolvedItem || item, []); + return TypeConverters.Suggest.from(resolvedItem || item, []); }); } } diff --git a/src/vs/workbench/api/node/mainThreadHeapMonitor.ts b/src/vs/workbench/api/node/mainThreadHeapMonitor.ts index 101ca35d9ea..64c60246ff4 100644 --- a/src/vs/workbench/api/node/mainThreadHeapMonitor.ts +++ b/src/vs/workbench/api/node/mainThreadHeapMonitor.ts @@ -8,7 +8,7 @@ import {IDisposable} from 'vs/base/common/lifecycle'; import {IThreadService} from 'vs/workbench/services/thread/common/threadService'; import {ExtHostContext} from './extHost.protocol'; -import {onDidGarbageCollectSignals, consumeSignals, trackGarbageCollection} from 'gc-signals'; +import {onDidGarbageCollectSignals, consumeSignals} from 'gc-signals'; export class MainThreadHeapMonitor { @@ -29,10 +29,4 @@ export class MainThreadHeapMonitor { clearInterval(this._consumeHandle); this._subscription.dispose(); } - - trackObject(obj: any) { - if (typeof obj.$heap_ident === 'number') { - trackGarbageCollection(obj, obj.$heap_ident); - } - } } \ No newline at end of file diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index f9776ca8a3e..ca3e536223a 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -15,7 +15,7 @@ import {wireCancellationToken} from 'vs/base/common/async'; import {CancellationToken} from 'vs/base/common/cancellation'; import {Position as EditorPosition} from 'vs/editor/common/core/position'; import {Range as EditorRange} from 'vs/editor/common/core/range'; -import {ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape} from './extHost.protocol'; +import {ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier} from './extHost.protocol'; import {LanguageConfigurationRegistry, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry'; import {trackGarbageCollection} from 'gc-signals'; @@ -183,7 +183,7 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape provideCompletionItems: (model:IReadOnlyModel, position:EditorPosition, token:CancellationToken): Thenable => { return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)).then(result => { for (const suggestion of result.suggestions) { - trackGarbageCollection(suggestion, (suggestion).$heap_ident); + trackGarbageCollection(suggestion, ObjectIdentifier.get(suggestion)); } return result; }); From 94acaef890eefb60f24ea3c8e028b4bf69a9ef8b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Sep 2016 10:28:26 +0200 Subject: [PATCH 5/6] dispose commands --- src/vs/workbench/api/node/extHostLanguageFeatures.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 2ec8654b624..990d4ce5c42 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -503,6 +503,7 @@ class SuggestAdapter { private _documents: ExtHostDocuments; private _heapMonitor: ExtHostHeapMonitor; private _provider: vscode.CompletionItemProvider; + private _disposables: { [id: number]: IDisposable[] } = []; constructor(documents: ExtHostDocuments, heapMonitor: ExtHostHeapMonitor, provider: vscode.CompletionItemProvider) { this._documents = documents; @@ -544,9 +545,8 @@ class SuggestAdapter { const item = list.items[i]; const disposables: IDisposable[] = []; const suggestion = TypeConverters.Suggest.from(item, disposables); - - // assign identifier to suggestion - const id = this._heapMonitor.keep(item, () => dispose(disposables)); + const id = this._heapMonitor.keep(item, () => dispose(this._disposables[id])); + this._disposables[id] = disposables; ObjectIdentifier.mixin(suggestion, id); if (item.textEdit) { @@ -585,12 +585,13 @@ class SuggestAdapter { return TPromise.as(suggestion); } - const item = this._heapMonitor.get(ObjectIdentifier.get(suggestion)); + const id = ObjectIdentifier.get(suggestion); + const item = this._heapMonitor.get(id); if (!item) { return TPromise.as(suggestion); } return asWinJsPromise(token => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => { - return TypeConverters.Suggest.from(resolvedItem || item, []); + return TypeConverters.Suggest.from(resolvedItem || item, this._disposables[id]); }); } } From af55c2005512b474809370e9b8ef9a4dd1eb659c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Sep 2016 10:31:21 +0200 Subject: [PATCH 6/6] monitor -> service --- src/vs/workbench/api/node/extHost.api.impl.ts | 4 ++-- .../workbench/api/node/extHost.contribution.ts | 4 ++-- src/vs/workbench/api/node/extHost.protocol.ts | 4 ++-- ...tHostHeapMonitor.ts => extHostHeapService.ts} | 6 +++--- .../api/node/extHostLanguageFeatures.ts | 16 ++++++++-------- ...adHeapMonitor.ts => mainThreadHeapService.ts} | 4 ++-- .../test/node/api/extHostApiCommands.test.ts | 4 ++-- .../node/api/extHostLanguageFeatures.test.ts | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) rename src/vs/workbench/api/node/{extHostHeapMonitor.ts => extHostHeapService.ts} (85%) rename src/vs/workbench/api/node/{mainThreadHeapMonitor.ts => mainThreadHeapService.ts} (90%) diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 90236ba0053..60c3d773de3 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -17,7 +17,7 @@ import {ExtHostConfiguration} from 'vs/workbench/api/node/extHostConfiguration'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; import {ExtHostWorkspace} from 'vs/workbench/api/node/extHostWorkspace'; import {ExtHostQuickOpen} from 'vs/workbench/api/node/extHostQuickOpen'; -import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; +import {ExtHostHeapService} from 'vs/workbench/api/node/extHostHeapService'; import {ExtHostStatusBar} from 'vs/workbench/api/node/extHostStatusBar'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostOutputService} from 'vs/workbench/api/node/extHostOutputService'; @@ -100,7 +100,7 @@ export class ExtHostAPIImplementation { // Addressable instances const col = new InstanceCollection(); - const extHostHeapMonitor = col.define(ExtHostContext.ExtHostHeapMonitor).set(new ExtHostHeapMonitor()); + const extHostHeapMonitor = col.define(ExtHostContext.ExtHostHeapService).set(new ExtHostHeapService()); const extHostDocuments = col.define(ExtHostContext.ExtHostDocuments).set(new ExtHostDocuments(threadService)); const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set(new ExtHostEditors(threadService, extHostDocuments)); const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set(new ExtHostCommands(threadService, extHostEditors)); diff --git a/src/vs/workbench/api/node/extHost.contribution.ts b/src/vs/workbench/api/node/extHost.contribution.ts index 43fce942a35..56753b05010 100644 --- a/src/vs/workbench/api/node/extHost.contribution.ts +++ b/src/vs/workbench/api/node/extHost.contribution.ts @@ -32,7 +32,7 @@ import {MainThreadTerminalService} from './mainThreadTerminalService'; import {MainThreadWorkspace} from './mainThreadWorkspace'; import {MainProcessExtensionService} from './mainThreadExtensionService'; import {MainThreadFileSystemEventService} from './mainThreadFileSystemEventService'; -import {MainThreadHeapMonitor} from './mainThreadHeapMonitor'; +import {MainThreadHeapService} from './mainThreadHeapService'; // --- other interested parties import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax'; @@ -88,7 +88,7 @@ export class ExtHostContribution implements IWorkbenchContribution { create(JSONValidationExtensionPoint); create(LanguageConfigurationFileHandler); create(MainThreadFileSystemEventService); - create(MainThreadHeapMonitor); + create(MainThreadHeapService); } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index f6afe370227..a2031bbce9a 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -279,7 +279,7 @@ export namespace ObjectIdentifier { } } -export abstract class ExtHostHeapMonitorShape { +export abstract class ExtHostHeapServiceShape { $onGarbageCollection(ids: number[]): void { throw ni(); } } @@ -339,7 +339,7 @@ export const ExtHostContext = { ExtHostDocuments: createExtId('ExtHostDocuments', ExtHostDocumentsShape), ExtHostEditors: createExtId('ExtHostEditors', ExtHostEditorsShape), ExtHostFileSystemEventService: createExtId('ExtHostFileSystemEventService', ExtHostFileSystemEventServiceShape), - ExtHostHeapMonitor: createExtId('ExtHostHeapMonitor', ExtHostHeapMonitorShape), + ExtHostHeapService: createExtId('ExtHostHeapMonitor', ExtHostHeapServiceShape), ExtHostLanguageFeatures: createExtId('ExtHostLanguageFeatures', ExtHostLanguageFeaturesShape), ExtHostQuickOpen: createExtId('ExtHostQuickOpen', ExtHostQuickOpenShape), ExtHostExtensionService: createExtId('ExtHostExtensionService', ExtHostExtensionServiceShape), diff --git a/src/vs/workbench/api/node/extHostHeapMonitor.ts b/src/vs/workbench/api/node/extHostHeapService.ts similarity index 85% rename from src/vs/workbench/api/node/extHostHeapMonitor.ts rename to src/vs/workbench/api/node/extHostHeapService.ts index af81049ff0d..47a3d8acdbc 100644 --- a/src/vs/workbench/api/node/extHostHeapMonitor.ts +++ b/src/vs/workbench/api/node/extHostHeapService.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {ExtHostHeapMonitorShape} from './extHost.protocol'; +import {ExtHostHeapServiceShape} from './extHost.protocol'; -export class ExtHostHeapMonitor extends ExtHostHeapMonitorShape { +export class ExtHostHeapService extends ExtHostHeapServiceShape { private static _idPool = 0; @@ -14,7 +14,7 @@ export class ExtHostHeapMonitor extends ExtHostHeapMonitorShape { private _callbacks: { [n: number]: Function } = Object.create(null); keep(obj:any, callback?:() => any): number { - const id = ExtHostHeapMonitor._idPool++; + const id = ExtHostHeapService._idPool++; this._data[id] = obj; if (typeof callback === 'function') { this._callbacks[id] = callback; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 990d4ce5c42..02b6c218188 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -13,7 +13,7 @@ import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import {Range, Disposable, CompletionList, CompletionItem} from 'vs/workbench/api/node/extHostTypes'; import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; -import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; +import {ExtHostHeapService} from 'vs/workbench/api/node/extHostHeapService'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; @@ -501,13 +501,13 @@ class RenameAdapter { class SuggestAdapter { private _documents: ExtHostDocuments; - private _heapMonitor: ExtHostHeapMonitor; + private _heapService: ExtHostHeapService; private _provider: vscode.CompletionItemProvider; private _disposables: { [id: number]: IDisposable[] } = []; - constructor(documents: ExtHostDocuments, heapMonitor: ExtHostHeapMonitor, provider: vscode.CompletionItemProvider) { + constructor(documents: ExtHostDocuments, heapMonitor: ExtHostHeapService, provider: vscode.CompletionItemProvider) { this._documents = documents; - this._heapMonitor = heapMonitor; + this._heapService = heapMonitor; this._provider = provider; } @@ -545,7 +545,7 @@ class SuggestAdapter { const item = list.items[i]; const disposables: IDisposable[] = []; const suggestion = TypeConverters.Suggest.from(item, disposables); - const id = this._heapMonitor.keep(item, () => dispose(this._disposables[id])); + const id = this._heapService.keep(item, () => dispose(this._disposables[id])); this._disposables[id] = disposables; ObjectIdentifier.mixin(suggestion, id); @@ -586,7 +586,7 @@ class SuggestAdapter { } const id = ObjectIdentifier.get(suggestion); - const item = this._heapMonitor.get(id); + const item = this._heapService.get(id); if (!item) { return TPromise.as(suggestion); } @@ -662,7 +662,7 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { private _proxy: MainThreadLanguageFeaturesShape; private _documents: ExtHostDocuments; private _commands: ExtHostCommands; - private _heapMonitor: ExtHostHeapMonitor; + private _heapMonitor: ExtHostHeapService; private _diagnostics: ExtHostDiagnostics; private _adapter: { [handle: number]: Adapter } = Object.create(null); @@ -670,7 +670,7 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { threadService: IThreadService, documents: ExtHostDocuments, commands: ExtHostCommands, - heapMonitor: ExtHostHeapMonitor, + heapMonitor: ExtHostHeapService, diagnostics: ExtHostDiagnostics ) { super(); diff --git a/src/vs/workbench/api/node/mainThreadHeapMonitor.ts b/src/vs/workbench/api/node/mainThreadHeapService.ts similarity index 90% rename from src/vs/workbench/api/node/mainThreadHeapMonitor.ts rename to src/vs/workbench/api/node/mainThreadHeapService.ts index 64c60246ff4..4f3f6f53a26 100644 --- a/src/vs/workbench/api/node/mainThreadHeapMonitor.ts +++ b/src/vs/workbench/api/node/mainThreadHeapService.ts @@ -10,13 +10,13 @@ import {IThreadService} from 'vs/workbench/services/thread/common/threadService' import {ExtHostContext} from './extHost.protocol'; import {onDidGarbageCollectSignals, consumeSignals} from 'gc-signals'; -export class MainThreadHeapMonitor { +export class MainThreadHeapService { private _subscription: IDisposable; private _consumeHandle: number; constructor( @IThreadService threadService: IThreadService) { - const proxy = threadService.get(ExtHostContext.ExtHostHeapMonitor); + const proxy = threadService.get(ExtHostContext.ExtHostHeapService); this._subscription = onDidGarbageCollectSignals(ids => { proxy.$onGarbageCollection(ids); diff --git a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts index f80a1bde549..c4b0d1dbaf3 100644 --- a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts @@ -23,7 +23,7 @@ import {ExtHostLanguageFeatures} from 'vs/workbench/api/node/extHostLanguageFeat import {MainThreadLanguageFeatures} from 'vs/workbench/api/node/mainThreadLanguageFeatures'; import {registerApiCommands} from 'vs/workbench/api/node/extHostApiCommands'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; -import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; +import {ExtHostHeapService} from 'vs/workbench/api/node/extHostHeapService'; import {MainThreadCommands} from 'vs/workbench/api/node/mainThreadCommands'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; import * as ExtHostTypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; @@ -109,7 +109,7 @@ suite('ExtHostLanguageFeatureCommands', function() { const diagnostics = new ExtHostDiagnostics(threadService); threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapMonitor(), diagnostics); + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapService(), diagnostics); threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, instantiationService.createInstance(MainThreadLanguageFeatures)); diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index 7cdb832bbbc..e22a9435875 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -39,7 +39,7 @@ import {getLinks} from 'vs/editor/contrib/links/common/links'; import {asWinJsPromise} from 'vs/base/common/async'; import {MainContext, ExtHostContext} from 'vs/workbench/api/node/extHost.protocol'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; -import {ExtHostHeapMonitor} from 'vs/workbench/api/node/extHostHeapMonitor'; +import {ExtHostHeapService} from 'vs/workbench/api/node/extHostHeapService'; const defaultSelector = { scheme: 'far' }; const model: EditorCommon.IModel = EditorModel.createFromString( @@ -98,7 +98,7 @@ suite('ExtHostLanguageFeatures', function() { const diagnostics = new ExtHostDiagnostics(threadService); threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapMonitor(), diagnostics); + extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, new ExtHostHeapService(), diagnostics); threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, instantiationService.createInstance(MainThreadLanguageFeatures));