From 1bd5eca73d0491ca5a8f66035af62a00ee5bc904 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 13 Sep 2019 10:48:04 -0500 Subject: [PATCH] Don't terminate extension host on a timeout We will clean it up on our end if necessary. This allows reconnections after any length of time. --- scripts/vscode.patch | 33 +++++++-------------------------- src/connection.ts | 24 ++++++++++++++++++------ src/server.ts | 22 ++++++++++------------ 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/scripts/vscode.patch b/scripts/vscode.patch index abfc176b4..ba005d1b4 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -1032,37 +1032,18 @@ index 7c3b6ae53e..18dec6effa 100644 get webviewResourceRoot(): string { diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -index 6d31b177ac..a586ac7466 100644 +index 6d31b177ac..d7c1705c57 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -@@ -116,7 +116,7 @@ function _createExtHostProtocol(): Promise { - protocol.onClose(() => onTerminate()); - resolve(protocol); - -- if (msg.skipWebSocketFrames) { -+ // if (msg.skipWebSocketFrames) { - // Wait for rich client to reconnect +@@ -128,7 +128,7 @@ function _createExtHostProtocol(): Promise { + } else { + // Do not wait for web companion to reconnect protocol.onSocketClose(() => { - // The socket has closed, let's give the renderer a certain amount of time to reconnect -@@ -125,12 +125,12 @@ function _createExtHostProtocol(): Promise { - onTerminate(); - }, ProtocolConstants.ReconnectionGraceTime); - }); -- } else { -- // Do not wait for web companion to reconnect -- protocol.onSocketClose(() => { - onTerminate(); -- }); -- } -+ // } else { -+ // // Do not wait for web companion to reconnect -+ // protocol.onSocketClose(() => { -+ // onTerminate(); -+ // }); -+ // } ++ process.send!('VSCODE_EXTHOST_DISCONNECTED'); // onTerminate(); + }); + } } - } - }); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 681fc606b6..e34ef5d4bc 100644 --- a/src/vs/workbench/workbench.web.main.ts diff --git a/src/connection.ts b/src/connection.ts index f872ff6ca..d31e76b9c 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -17,10 +17,7 @@ export abstract class Connection { private disposed = false; private _offline: number | undefined; - public constructor(protected protocol: Protocol) { - protocol.onClose(() => this.dispose()); // Explicit close. - protocol.onSocketClose(() => this._offline = Date.now()); // Might reconnect. - } + public constructor(protected protocol: Protocol, public readonly token: string) {} public get offline(): number | undefined { return this._offline; @@ -39,6 +36,12 @@ export abstract class Connection { } } + protected setOffline(): void { + if (!this._offline) { + this._offline = Date.now(); + } + } + /** * Set up the connection on a new socket. */ @@ -50,6 +53,12 @@ export abstract class Connection { * Used for all the IPC channels. */ export class ManagementConnection extends Connection { + public constructor(protected protocol: Protocol, token: string) { + super(protocol, token); + protocol.onClose(() => this.dispose()); // Explicit close. + protocol.onSocketClose(() => this.setOffline()); // Might reconnect. + } + protected doDispose(): void { this.protocol.sendDisconnect(); this.protocol.dispose(); @@ -66,11 +75,11 @@ export class ExtensionHostConnection extends Connection { private process?: cp.ChildProcess; public constructor( - locale:string, protocol: Protocol, buffer: VSBuffer, + locale:string, protocol: Protocol, buffer: VSBuffer, token: string, private readonly log: ILogService, private readonly environment: IEnvironmentService, ) { - super(protocol); + super(protocol, token); this.protocol.dispose(); this.spawn(locale, buffer).then((p) => this.process = p); this.protocol.getUnderlyingSocket().pause(); @@ -129,6 +138,9 @@ export class ExtensionHostConnection extends Connection { const severity = (this.log)[event.severity] ? event.severity : "info"; (this.log)[severity]("Extension host", event.arguments); } + if (event && event.type === "VSCODE_EXTHOST_DISCONNECTED") { + this.setOffline(); + } }); const listen = (message: IExtHostReadyMessage) => { diff --git a/src/server.ts b/src/server.ts index 705a1a597..5cb8770c3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -449,7 +449,7 @@ export class MainServer extends Server { public readonly onDidClientConnect = this._onDidClientConnect.event; private readonly ipc = new IPCServer(this.onDidClientConnect); - private readonly maxOfflineConnections = 5; + private readonly maxExtraOfflineConnections = 0; private readonly connections = new Map>(); private readonly services = new ServiceCollection(); @@ -601,7 +601,7 @@ export class MainServer extends Server { let connection: Connection; if (message.desiredConnectionType === ConnectionType.Management) { - connection = new ManagementConnection(protocol); + connection = new ManagementConnection(protocol, token); this._onDidClientConnect.fire({ protocol, onDidClientDisconnect: connection.onClose, }); @@ -609,28 +609,26 @@ export class MainServer extends Server { const buffer = protocol.readEntireBuffer(); connection = new ExtensionHostConnection( message.args ? message.args.language : "en", - protocol, buffer, + protocol, buffer, token, this.services.get(ILogService) as ILogService, this.services.get(IEnvironmentService) as IEnvironmentService, ); } connections.set(token, connection); - this.disposeOldOfflineConnections(); connection.onClose(() => connections.delete(token)); + this.disposeOldOfflineConnections(connections); break; case ConnectionType.Tunnel: return protocol.tunnel(); default: throw new Error("Unrecognized connection type"); } } - private disposeOldOfflineConnections(): void { - this.connections.forEach((connections) => { - const offline = Array.from(connections.values()) - .filter((connection) => typeof connection.offline !== "undefined"); - for (let i = 0, max = offline.length - this.maxOfflineConnections; i < max; ++i) { - offline[i].dispose(); - } - }); + private disposeOldOfflineConnections(connections: Map): void { + const offline = Array.from(connections.values()) + .filter((connection) => typeof connection.offline !== "undefined"); + for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { + offline[i].dispose(); + } } private async initializeServices(args: ParsedArgs): Promise {