From a9fbd36e4f912cc6c4488dab2c6edd130bb92b50 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 30 Mar 2023 11:05:25 -0700 Subject: [PATCH] Start extracting pty host starter into interface Part of #175335 --- .../node/sharedProcess/sharedProcessMain.ts | 10 +- .../terminal/node/nodePtyHostStarter.ts | 75 +++++++++++++ .../platform/terminal/node/ptyHostService.ts | 100 +++++++++--------- src/vs/server/node/serverServices.ts | 6 +- 4 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 src/vs/platform/terminal/node/nodePtyHostStarter.ts diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index cd4ebe3db1fc1..7f5800a7e8b6e 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -119,6 +119,7 @@ import { UserDataAutoSyncService } from 'vs/platform/userDataSync/node/userDataA import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { RemoteStorageService } from 'vs/platform/storage/common/storageService'; +import { NodePtyHostStarter } from 'vs/platform/terminal/node/nodePtyHostStarter'; class SharedProcessMain extends Disposable { @@ -379,15 +380,14 @@ class SharedProcessMain extends Disposable { services.set(IUserDataSyncResourceProviderService, new SyncDescriptor(UserDataSyncResourceProviderService, undefined, true)); // Terminal - - const ptyHostService = new PtyHostService({ + const ptyHostStarter = new NodePtyHostStarter({ graceTime: LocalReconnectConstants.GraceTime, shortGraceTime: LocalReconnectConstants.ShortGraceTime, scrollback: configurationService.getValue(TerminalSettingId.PersistentSessionScrollback) ?? 100 - }, - false, + }, false, environmentService); + const ptyHostService = new PtyHostService( + ptyHostStarter, configurationService, - environmentService, logService, loggerService ); diff --git a/src/vs/platform/terminal/node/nodePtyHostStarter.ts b/src/vs/platform/terminal/node/nodePtyHostStarter.ts new file mode 100644 index 0000000000000..54cddf483e449 --- /dev/null +++ b/src/vs/platform/terminal/node/nodePtyHostStarter.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { FileAccess } from 'vs/base/common/network'; +import { IChannelClient } from 'vs/base/parts/ipc/common/ipc'; +import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService'; +import { IReconnectConstants } from 'vs/platform/terminal/common/terminal'; + +export interface IPtyHostStarter { + /** + * Creates a pty host and connects to it. + * + * @param lastPtyId Tracks the last terminal ID from the pty host so we can give it to the new + * pty host if it's restarted and avoid ID conflicts. + */ + start(lastPtyId: number): IChannelClient; +} + +export class NodePtyHostStarter implements IPtyHostStarter { + constructor( + private readonly _reconnectConstants: IReconnectConstants, + private readonly _isRemote: boolean, + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService + ) { + } + + start(lastPtyId: number): IChannelClient { + const opts: IIPCOptions = { + serverName: 'Pty Host', + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], + env: { + VSCODE_LAST_PTY_ID: lastPtyId, + VSCODE_PTY_REMOTE: this._isRemote, + VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', + VSCODE_PIPE_LOGGING: 'true', + VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, + VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime, + VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime, + VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback + } + }; + + const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt); + if (ptyHostDebug) { + if (ptyHostDebug.break && ptyHostDebug.port) { + opts.debugBrk = ptyHostDebug.port; + } else if (!ptyHostDebug.break && ptyHostDebug.port) { + opts.debug = ptyHostDebug.port; + } + } + + const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts); + // this._onPtyHostStart.fire(); + + // TODO: Handle exit + // this._register(client.onDidProcessExit(e => { + // this._onPtyHostExit.fire(e.code); + // if (!this._isDisposed) { + // if (this._restartCount <= Constants.MaxRestarts) { + // this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}`); + // this._restartCount++; + // this.restartPtyHost(); + // } else { + // this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}, giving up`); + // } + // } + // })); + + return client; + } +} diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 75b8ef8d1216c..cb36fd5e6a00f 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -5,13 +5,9 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { FileAccess } from 'vs/base/common/network'; import { IProcessEnvironment, isWindows, OperatingSystem } from 'vs/base/common/platform'; -import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; +import { IChannelClient, ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; @@ -21,6 +17,7 @@ import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs import { detectAvailableProfiles } from 'vs/platform/terminal/node/terminalProfiles'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc'; +import { IPtyHostStarter } from 'vs/platform/terminal/node/nodePtyHostStarter'; enum Constants { MaxRestarts = 5 @@ -39,7 +36,7 @@ let lastPtyId = 0; export class PtyHostService extends Disposable implements IPtyService { declare readonly _serviceBrand: undefined; - private _client: Client; + private _client: IChannelClient; // ProxyChannel is not used here because events get lost when forwarding across multiple proxies private _proxy: IPtyService; @@ -78,10 +75,8 @@ export class PtyHostService extends Disposable implements IPtyService { readonly onProcessExit = this._onProcessExit.event; constructor( - private readonly _reconnectConstants: IReconnectConstants, - private readonly isRemote: boolean, + private readonly _ptyHostStarter: IPtyHostStarter, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IEnvironmentService private readonly _environmentService: INativeEnvironmentService, @ILogService private readonly _logService: ILogService, @ILoggerService private readonly _loggerService: ILoggerService, ) { @@ -133,32 +128,38 @@ export class PtyHostService extends Disposable implements IPtyService { } } - private _startPtyHost(): [Client, IPtyService] { - const opts: IIPCOptions = { - serverName: 'Pty Host', - args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], - env: { - VSCODE_LAST_PTY_ID: lastPtyId, - VSCODE_PTY_REMOTE: this.isRemote, - VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', - VSCODE_PIPE_LOGGING: 'true', - VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, - VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime, - VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime, - VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback - } - }; - - const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt); - if (ptyHostDebug) { - if (ptyHostDebug.break && ptyHostDebug.port) { - opts.debugBrk = ptyHostDebug.port; - } else if (!ptyHostDebug.break && ptyHostDebug.port) { - opts.debug = ptyHostDebug.port; - } - } - - const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts); + private _startPtyHost(): [IChannelClient, IPtyService] { + // TODO: Call starter + + + // const opts: IIPCOptions = { + // serverName: 'Pty Host', + // args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], + // env: { + // VSCODE_LAST_PTY_ID: lastPtyId, + // VSCODE_PTY_REMOTE: this.isRemote, + // VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', + // VSCODE_PIPE_LOGGING: 'true', + // VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, + // VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime, + // VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime, + // VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback + // } + // }; + + // const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt); + // if (ptyHostDebug) { + // if (ptyHostDebug.break && ptyHostDebug.port) { + // opts.debugBrk = ptyHostDebug.port; + // } else if (!ptyHostDebug.break && ptyHostDebug.port) { + // opts.debug = ptyHostDebug.port; + // } + // } + + // const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts); + const client = this._ptyHostStarter.start(lastPtyId); + + // TODO: Verify this is correct order this._onPtyHostStart.fire(); // Setup heartbeat service and trigger a heartbeat immediately to reset the timeouts @@ -166,19 +167,19 @@ export class PtyHostService extends Disposable implements IPtyService { heartbeatService.onBeat(() => this._handleHeartbeat()); this._handleHeartbeat(); - // Handle exit - this._register(client.onDidProcessExit(e => { - this._onPtyHostExit.fire(e.code); - if (!this._isDisposed) { - if (this._restartCount <= Constants.MaxRestarts) { - this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}`); - this._restartCount++; - this.restartPtyHost(); - } else { - this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}, giving up`); - } - } - })); + // TODO: Handle exit + // this._register(client.onDidProcessExit(e => { + // this._onPtyHostExit.fire(e.code); + // if (!this._isDisposed) { + // if (this._restartCount <= Constants.MaxRestarts) { + // this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}`); + // this._restartCount++; + // this.restartPtyHost(); + // } else { + // this._logService.error(`ptyHost terminated unexpectedly with code ${e.code}, giving up`); + // } + // } + // })); // Setup logging this._register(new RemoteLoggerChannelClient(this._loggerService, client.getChannel(TerminalIpcChannels.Logger))); @@ -346,7 +347,8 @@ export class PtyHostService extends Disposable implements IPtyService { private _disposePtyHost(): void { this._proxy.shutdownAll?.(); - this._client.dispose(); + // TODO: Support disposing of the client + // this._client.dispose(); } private _handleHeartbeat() { diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 3384aee71bd27..77759f21d7731 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -80,6 +80,7 @@ import { localize } from 'vs/nls'; import { RemoteExtensionsScannerChannel, RemoteExtensionsScannerService } from 'vs/server/node/remoteExtensionsScanner'; import { RemoteExtensionsScannerChannelName } from 'vs/platform/remote/common/remoteExtensionsScanner'; import { RemoteUserDataProfilesServiceChannel } from 'vs/platform/userDataProfile/common/userDataProfileIpc'; +import { NodePtyHostStarter } from 'vs/platform/terminal/node/nodePtyHostStarter'; const eventPrefix = 'monacoworkbench'; @@ -190,8 +191,8 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const instantiationService: IInstantiationService = new InstantiationService(services); services.set(ILanguagePackService, instantiationService.createInstance(NativeLanguagePackService)); - const ptyService = instantiationService.createInstance( - PtyHostService, + const ptyHostStarter = instantiationService.createInstance( + NodePtyHostStarter, { graceTime: ProtocolConstants.ReconnectionGraceTime, shortGraceTime: ProtocolConstants.ReconnectionShortGraceTime, @@ -199,6 +200,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken }, true ); + const ptyService = instantiationService.createInstance(PtyHostService, ptyHostStarter); services.set(IPtyService, ptyService); services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId]));