From 3669afe7e966b9d8927b2c20ba73131af470cf6b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 25 Jun 2018 18:20:12 +0200 Subject: [PATCH] Fix #48656 --- .../sharedProcess/sharedProcessMain.ts | 8 +++- .../node/extensionManagementService.ts | 38 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 58473214eeb..50027d9528e 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -42,6 +42,7 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza import { LocalizationsChannel } from 'vs/platform/localizations/common/localizationsIpc'; import { DialogChannelClient } from 'vs/platform/dialogs/common/dialogIpc'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -61,11 +62,13 @@ const eventPrefix = 'monacoworkbench'; function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void { const services = new ServiceCollection(); + const disposables: IDisposable[] = []; + process.once('exit', () => dispose(disposables)); const environmentService = new EnvironmentService(initData.args, process.execPath); const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { route: () => 'main' })); const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath)); - process.once('exit', () => logService.dispose()); + disposables.push(logService); logService.info('main', JSON.stringify(configuration)); @@ -98,7 +101,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I // It is important to dispose the AI adapter properly because // only then they flush remaining data. - process.once('exit', () => appenders.forEach(a => a.dispose())); + disposables.push(...appenders); const appender = combinedAppender(...appenders); server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appender)); @@ -138,6 +141,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I server.registerChannel('localizations', localizationsChannel); createSharedProcessContributions(instantiationService2); + disposables.push(extensionManagementService as ExtensionManagementService); }); }); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 611209b6926..8a57dd69022 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -114,6 +114,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private lastReportTimestamp = 0; private readonly installationStartTime: Map = new Map(); private readonly installingExtensions: Map> = new Map>(); + private readonly uninstallingExtensions: Map> = new Map>(); private readonly manifestCache: ExtensionsManifestCache; private readonly extensionLifecycle: ExtensionsLifecycle; @@ -140,9 +141,15 @@ export class ExtensionManagementService extends Disposable implements IExtension this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); this.uninstalledFileLimiter = new Limiter(1); - this._register(toDisposable(() => this.installingExtensions.clear())); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService)); + + this._register(toDisposable(() => { + this.installingExtensions.forEach(promise => promise.cancel()); + this.uninstallingExtensions.forEach(promise => promise.cancel()); + this.installingExtensions.clear(); + this.uninstallingExtensions.clear(); + })); } install(zipPath: string): TPromise { @@ -208,7 +215,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise { - return this.getInstalled() + return this.toNonCancellablePromise(this.getInstalled() .then(installed => { const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed); return this.installExtension({ zipPath, id: identifier.id, metadata }) @@ -230,12 +237,12 @@ export class ExtensionManagementService extends Disposable implements IExtension local => { this._onDidInstallExtension.fire({ identifier, zipPath, local, operation }); return local; }, error => { this._onDidInstallExtension.fire({ identifier, zipPath, operation, error }); return TPromise.wrapError(error); } ); - }); + })); } installFromGallery(extension: IGalleryExtension): TPromise { this.onInstallExtensions([extension]); - return this.getInstalled(LocalExtensionType.User) + return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User) .then(installed => this.collectExtensionsToInstall(extension) .then( extensionsToInstall => { @@ -249,7 +256,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier))[0]), errors => this.onDidInstallExtensions(extensionsToInstall, [], operataions, errors)); }, - error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension.identifier, installed)], [error]))); + error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension.identifier, installed)], [error])))); } reinstallFromGallery(extension: ILocalExtension): TPromise { @@ -487,13 +494,13 @@ export class ExtensionManagementService extends Disposable implements IExtension } uninstall(extension: ILocalExtension, force = false): TPromise { - return this.getInstalled(LocalExtensionType.User) + return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User) .then(installed => { const promises = installed .filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name) .map(e => this.checkForDependenciesAndUninstall(e, installed, force)); return TPromise.join(promises).then(() => null, error => TPromise.wrapError(this.joinErrors(error))); - }); + })); } updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): TPromise { @@ -667,9 +674,16 @@ export class ExtensionManagementService extends Disposable implements IExtension } private uninstallExtension(local: ILocalExtension): TPromise { - // Set all versions of the extension as uninstalled - return this.scanUserExtensions(false) - .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id: getGalleryExtensionIdFromLocal(local), uuid: local.identifier.uuid })))); + const id = getGalleryExtensionIdFromLocal(local); + let promise = this.uninstallingExtensions.get(id); + if (!promise) { + // Set all versions of the extension as uninstalled + promise = this.scanUserExtensions(false) + .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id, uuid: local.identifier.uuid })))) + .then(() => { this.uninstallingExtensions.delete(id); }); + this.uninstallingExtensions.set(id, promise); + } + return promise; } private async postUninstallExtension(extension: ILocalExtension, error?: Error): TPromise { @@ -863,6 +877,10 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } + private toNonCancellablePromise(promise: TPromise): TPromise { + return new TPromise((c, e) => promise.then(result => c(result), error => e(error)), () => this.logService.debug('Request Cancelled')); + } + private reportTelemetry(eventName: string, extensionData: any, duration: number, error?: Error): void { const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0; /* __GDPR__ -- GitLab