Skip to content

Commit

Permalink
Start extracting pty host starter into interface
Browse files Browse the repository at this point in the history
Part of #175335
  • Loading branch information
Tyriar committed Mar 30, 2023
1 parent b5aa594 commit a9fbd36
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 56 deletions.
10 changes: 5 additions & 5 deletions src/vs/code/node/sharedProcess/sharedProcessMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
},
false,
}, false, environmentService);
const ptyHostService = new PtyHostService(
ptyHostStarter,
configurationService,
environmentService,
logService,
loggerService
);
Expand Down
75 changes: 75 additions & 0 deletions src/vs/platform/terminal/node/nodePtyHostStarter.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
100 changes: 51 additions & 49 deletions src/vs/platform/terminal/node/ptyHostService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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,
) {
Expand Down Expand Up @@ -133,52 +128,58 @@ 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
const heartbeatService = ProxyChannel.toService<IHeartbeatService>(client.getChannel(TerminalIpcChannels.Heartbeat));
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)));
Expand Down Expand Up @@ -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() {
Expand Down
6 changes: 4 additions & 2 deletions src/vs/server/node/serverServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -190,15 +191,16 @@ 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,
scrollback: configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
},
true
);
const ptyService = instantiationService.createInstance(PtyHostService, ptyHostStarter);
services.set(IPtyService, ptyService);

services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId]));
Expand Down

0 comments on commit a9fbd36

Please sign in to comment.