Skip to content

Commit

Permalink
Move extension host spawning to a worker thread (on the main process)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Nov 11, 2021
1 parent 18777d9 commit 778014d
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService';
import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter';
import { DirectMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter';
import { WorkerMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter';
import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal';
import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
import { IFileService } from 'vs/platform/files/common/files';
Expand Down Expand Up @@ -517,7 +517,7 @@ export class CodeApplication extends Disposable {
services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService));

// Extension Host Starter
services.set(IExtensionHostStarter, new SyncDescriptor(DirectMainProcessExtensionHostStarter));
services.set(IExtensionHostStarter, new SyncDescriptor(WorkerMainProcessExtensionHostStarter));

// Storage
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Promises } from 'vs/base/common/async';
import { ExtensionHostStarter, IPartialLogService } from 'vs/platform/extensions/node/extensionHostStarter';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { ILogService } from 'vs/platform/log/common/log';
Expand All @@ -18,18 +17,12 @@ export class DirectMainProcessExtensionHostStarter extends ExtensionHostStarter

// Abnormal shutdown: terminate extension hosts asap
lifecycleMainService.onWillKill(() => {
for (const [, extHost] of this._extHosts) {
extHost.kill();
}
this.killAllNow();
});

// Normal shutdown: gracefully await extension host shutdowns
lifecycleMainService.onWillShutdown((e) => {
const exitPromises: Promise<void>[] = [];
for (const [, extHost] of this._extHosts) {
exitPromises.push(extHost.waitForExit(6000));
}
e.join(Promises.settled(exitPromises).then(() => { }));
e.join(this.waitForAllExit(6000));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SerializedError } from 'vs/base/common/errors';
import { canceled, SerializedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter';
import { Event } from 'vs/base/common/event';
Expand All @@ -12,6 +12,8 @@ import { ILogService } from 'vs/platform/log/common/log';
import { Worker } from 'worker_threads';
import { IWorker, IWorkerCallback, IWorkerFactory, SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker';
import { IExtensionHostStarterWorkerHost } from 'vs/platform/extensions/node/extensionHostStarterWorker';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter';

class NodeWorker implements IWorker {

Expand Down Expand Up @@ -51,14 +53,16 @@ class ExtensionHostStarterWorkerHost implements IExtensionHostStarterWorkerHost
}
}

export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter {
export class WorkerMainProcessExtensionHostStarter implements IDisposable, IExtensionHostStarter {
_serviceBrand: undefined;

private _proxy: ExtensionHostStarter | null;
private readonly _worker: SimpleWorkerClient<ExtensionHostStarter, IExtensionHostStarterWorkerHost>;
private _shutdown = false;

constructor(
@ILogService private readonly _logService: ILogService
@ILogService private readonly _logService: ILogService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService
) {
this._proxy = null;

Expand All @@ -75,6 +79,22 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
new ExtensionHostStarterWorkerHost(this._logService)
);
this._initialize();

// Abnormal shutdown: terminate extension hosts asap
lifecycleMainService.onWillKill(async () => {
this._shutdown = true;
if (this._proxy) {
this._proxy.killAllNow();
}
});

// Normal shutdown: gracefully await extension host shutdowns
lifecycleMainService.onWillShutdown((e) => {
this._shutdown = true;
if (this._proxy) {
e.join(this._proxy.waitForAllExit(6000));
}
});
}

dispose(): void {
Expand All @@ -83,6 +103,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter

async _initialize(): Promise<void> {
this._proxy = await this._worker.getProxyObject();
this._logService.info(`ExtensionHostStarterWorker created`);
}

onDynamicStdout(id: string): Event<string> {
Expand All @@ -107,21 +128,33 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter

async createExtensionHost(): Promise<{ id: string; }> {
const proxy = await this._worker.getProxyObject();
if (this._shutdown) {
throw canceled();
}
return proxy.createExtensionHost();
}

async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> {
const proxy = await this._worker.getProxyObject();
if (this._shutdown) {
throw canceled();
}
return proxy.start(id, opts);
}

async enableInspectPort(id: string): Promise<boolean> {
const proxy = await this._worker.getProxyObject();
if (this._shutdown) {
throw canceled();
}
return proxy.enableInspectPort(id);
}

async kill(id: string): Promise<void> {
const proxy = await this._worker.getProxyObject();
if (this._shutdown) {
throw canceled();
}
return proxy.kill(id);
}
}
23 changes: 15 additions & 8 deletions src/vs/platform/extensions/node/extensionHostStarter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { mixin } from 'vs/base/common/objects';
import { cwd } from 'vs/base/common/process';
import { StopWatch } from 'vs/base/common/stopwatch';
import { timeout } from 'vs/base/common/async';
import { Promises, timeout } from 'vs/base/common/async';

export interface IPartialLogService {
readonly _serviceBrand: undefined;
Expand Down Expand Up @@ -52,13 +52,6 @@ class ExtensionHostProcess extends Disposable {

start(opts: IExtensionHostProcessOptions): { pid: number; } {
const sw = StopWatch.create(false);
// opts.env = opts.env || {};
// opts.env.ELECTRON_RUN_AS_NODE = '1';
// this._process = spawn(
// process.execPath,
// [FileAccess.asFileUri('bootstrap-fork', require).fsPath, '--type=extensionHost', '--skipWorkspaceStorageLock'],
// mixin({ cwd: cwd() }, opts),
// );
this._process = fork(
FileAccess.asFileUri('bootstrap-fork', require).fsPath,
['--type=extensionHost', '--skipWorkspaceStorageLock'],
Expand Down Expand Up @@ -225,6 +218,20 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
}
extHostProcess.kill();
}

async killAllNow(): Promise<void> {
for (const [, extHost] of this._extHosts) {
extHost.kill();
}
}

async waitForAllExit(maxWaitTimeMs: number): Promise<void> {
const exitPromises: Promise<void>[] = [];
for (const [, extHost] of this._extHosts) {
exitPromises.push(extHost.waitForExit(maxWaitTimeMs));
}
return Promises.settled(exitPromises).then(() => { });
}
}

registerSingleton(IExtensionHostStarter, ExtensionHostStarter, true);

0 comments on commit 778014d

Please sign in to comment.