From 428d4768e0f530286760563c60d65ada4edc031e Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Fri, 7 Oct 2016 14:59:05 +0200 Subject: [PATCH] Run extension host detached under Windows to ensure correct extension shutdown --- .../workbench/api/node/extHostExtensionService.ts | 13 ++++++++++--- src/vs/workbench/node/extensionHostMain.ts | 14 +++++++++++--- .../thread/electron-browser/threadService.ts | 8 ++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 137317c3415..700032a2bb4 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -148,16 +148,21 @@ export class ExtHostExtensionService extends AbstractExtensionService { + let result:TPromise = TPromise.as(void 0); + let extension = this._activatedExtensions[extensionId]; if (!extension) { - return; + return result; } // call deactivate if available try { if (typeof extension.module.deactivate === 'function') { - extension.module.deactivate(); + result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => { + // TODO: Do something with err if this is not the shutdown case + return TPromise.as(void 0); + }); } } catch (err) { // TODO: Do something with err if this is not the shutdown case @@ -169,6 +174,8 @@ export class ExtHostExtensionService extends AbstractExtensionService { + // TODO: write to log once we have one + }); + + let allPromises: TPromise[] = []; try { let allExtensions = ExtensionsRegistry.getAllExtensionDescriptions(); let allExtensionsIds = allExtensions.map(ext => ext.id); let activatedExtensions = allExtensionsIds.filter(id => this._extensionService.isActivated(id)); - activatedExtensions.forEach((extensionId) => { - this._extensionService.deactivate(extensionId); + allPromises = activatedExtensions.map((extensionId) => { + return this._extensionService.deactivate(extensionId); }); } catch (err) { // TODO: write to log once we have one } + let extensionsDeactivated = TPromise.join(allPromises).then(() => void 0); + // Give extensions 1 second to wrap up any async dispose, then exit setTimeout(() => { - exit(); + TPromise.any([TPromise.timeout(4000), extensionsDeactivated]).then(() => exit(), () => exit()); }, 1000); } diff --git a/src/vs/workbench/services/thread/electron-browser/threadService.ts b/src/vs/workbench/services/thread/electron-browser/threadService.ts index 1acc78c7aea..f867f0854ae 100644 --- a/src/vs/workbench/services/thread/electron-browser/threadService.ts +++ b/src/vs/workbench/services/thread/electron-browser/threadService.ts @@ -12,6 +12,7 @@ import * as objects from 'vs/base/common/objects'; import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import {TPromise} from 'vs/base/common/winjs.base'; +import {isWindows} from 'vs/base/common/platform'; import {findFreePort} from 'vs/base/node/ports'; import {IMainProcessExtHostIPC, create} from 'vs/platform/extensions/common/ipcRemoteCom'; import {IMessageService, Severity} from 'vs/platform/message/common/message'; @@ -151,6 +152,13 @@ class ExtensionHostProcessManager { if (port) { opts.execArgv = ['--nolazy', (this.isExtensionDevelopmentDebugging ? '--debug-brk=' : '--debug=') + port]; } + // We only detach the extension host on windows. Linux and Mac orphan by default + // and detach under Linux and Mac create another process group. + if (isWindows) { + // We detach because we have noticed that when the renderer exits, its child processes + // (i.e. extension host) is taken down in a brutal fashion by the OS + opts.detached = true; + } // Run Extension Host as fork of current process this.extensionHostProcessHandle = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); -- GitLab