Skip to content

Commit

Permalink
Merge pull request #116389 from microsoft/tyriar_megan_sharedproc
Browse files Browse the repository at this point in the history
Move local ptys to ptyHost under shared process
  • Loading branch information
Tyriar authored Feb 11, 2021
2 parents 1abb2d9 + 5188792 commit 1819dd5
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 33 deletions.
11 changes: 11 additions & 0 deletions src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ import { DeprecatedExtensionsCleaner } from 'vs/code/electron-browser/sharedProc
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { join } from 'vs/base/common/path';
import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { LocalPtyService } from 'vs/platform/terminal/electron-browser/localPtyService';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';

class SharedProcessMain extends Disposable {

Expand Down Expand Up @@ -259,6 +262,9 @@ class SharedProcessMain extends Disposable {
services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));

// Terminal
services.set(ILocalPtyService, new SyncDescriptor(LocalPtyService));

return new InstantiationService(services);
}

Expand Down Expand Up @@ -304,6 +310,11 @@ class SharedProcessMain extends Disposable {
const userDataAutoSync = this._register(accessor.get(IInstantiationService).createInstance(UserDataAutoSyncService));
const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync);
this.server.registerChannel('userDataAutoSync', userDataAutoSyncChannel);

// Terminal
const localPtyService = accessor.get(ILocalPtyService);
const localPtyChannel = ProxyChannel.fromService(localPtyService);
this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyChannel);
}

private registerErrorHandler(logService: ILogService): void {
Expand Down
6 changes: 0 additions & 6 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService';
import { once } from 'vs/base/common/functional';
import { ILocalPtyMainService, LocalPtyMainService } from 'vs/platform/terminal/electron-main/localPtyMainService';

export class CodeApplication extends Disposable {
private windowsMainService: IWindowsMainService | undefined;
Expand Down Expand Up @@ -528,7 +527,6 @@ export class CodeApplication extends Disposable {
services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService));
services.set(IDiagnosticsService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics')))));

services.set(ILocalPtyMainService, new SyncDescriptor(LocalPtyMainService));
services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId]));
services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService));
Expand Down Expand Up @@ -616,10 +614,6 @@ export class CodeApplication extends Disposable {
const updateChannel = new UpdateChannel(updateService);
electronIpcServer.registerChannel('update', updateChannel);

const localPtyMainService = accessor.get(ILocalPtyMainService);
const localPtyChannel = createChannelReceiver(localPtyMainService);
electronIpcServer.registerChannel('localPty', localPtyChannel);

const issueMainService = accessor.get(IIssueMainService);
const issueChannel = ProxyChannel.fromService(issueMainService);
electronIpcServer.registerChannel('issue', issueChannel);
Expand Down
13 changes: 12 additions & 1 deletion src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ import { Event } from 'vs/base/common/event';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';

export interface ICommonLocalPtyService {
export enum TerminalIpcChannels {
/**
* Communicates between the renderer process and shared process.
*/
LocalPty = 'localPty',
/**
* Communicates between the shared process and the pty host process.
*/
PtyHost = 'ptyHost'
}

export interface ILocalPtyService {
readonly _serviceBrand: undefined;

readonly onProcessData: Event<{ id: number, event: IProcessDataEvent | string }>;
Expand Down
104 changes: 104 additions & 0 deletions src/vs/platform/terminal/electron-browser/localPtyService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { ILocalPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import { FileAccess } from 'vs/base/common/network';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { Emitter } from 'vs/base/common/event';

export class LocalPtyService extends Disposable implements ILocalPtyService {
declare readonly _serviceBrand: undefined;

// ProxyChannel is not used here because events get lost when forwarding across multiple proxies
private readonly _proxy: ILocalPtyService;

private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>());
readonly onProcessData = this._onProcessData.event;
private readonly _onProcessExit = this._register(new Emitter<{ id: number, event: number | undefined }>());
readonly onProcessExit = this._onProcessExit.event;
private readonly _onProcessReady = this._register(new Emitter<{ id: number, event: { pid: number, cwd: string } }>());
readonly onProcessReady = this._onProcessReady.event;
private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>());
readonly onProcessTitleChanged = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>());
readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>());
readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event;

constructor(
@ILogService private readonly _logService: ILogService
) {
super();

const client = this._register(new Client(
FileAccess.asFileUri('bootstrap-fork', require).fsPath,
{
serverName: 'Pty Host',
args: ['--type=ptyHost'],
env: {
VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client
}
}
));

// TODO: Handle exit gracefully
this._register(client.onDidProcessExit(e => {
this._logService.info('ptyHost exit', e);
// // our watcher app should never be completed because it keeps on watching. being in here indicates
// // that the watcher process died and we want to restart it here. we only do it a max number of times
// if (!this.isDisposed) {
// if (this.restartCounter <= FileWatcher.MAX_RESTARTS) {
// this.error('terminated unexpectedly and is restarted again...');
// this.restartCounter++;
// this.startWatching();
// } else {
// this.error('failed to start after retrying for some time, giving up. Please report this as a bug report!');
// }
// }
}));

this._proxy = ProxyChannel.toService(client.getChannel(TerminalIpcChannels.PtyHost));
this._register(this._proxy.onProcessData(e => this._onProcessData.fire(e)));
this._register(this._proxy.onProcessExit(e => this._onProcessExit.fire(e)));
this._register(this._proxy.onProcessReady(e => this._onProcessReady.fire(e)));
this._register(this._proxy.onProcessTitleChanged(e => this._onProcessTitleChanged.fire(e)));
this._register(this._proxy.onProcessOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e)));
this._register(this._proxy.onProcessResolvedShellLaunchConfig(e => this._onProcessResolvedShellLaunchConfig.fire(e)));
}

createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise<number> {
return this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty);
}
start(id: number): Promise<ITerminalLaunchError | { remoteTerminalId: number; } | undefined> {
return this._proxy.start(id);
}
shutdown(id: number, immediate: boolean): Promise<void> {
return this._proxy.shutdown(id, immediate);
}
input(id: number, data: string): Promise<void> {
return this._proxy.input(id, data);
}
resize(id: number, cols: number, rows: number): Promise<void> {
return this._proxy.resize(id, cols, rows);
}
acknowledgeDataEvent(id: number, charCount: number): Promise<void> {
return this._proxy.acknowledgeDataEvent(id, charCount);
}
getInitialCwd(id: number): Promise<string> {
return this._proxy.getInitialCwd(id);
}
getCwd(id: number): Promise<string> {
return this._proxy.getCwd(id);
}
getLatency(id: number): Promise<number> {
return this._proxy.getLatency(id);
}
}
2 changes: 1 addition & 1 deletion src/vs/platform/terminal/electron-sandbox/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ICommonLocalPtyService } from 'vs/platform/terminal/common/terminal';
import { ILocalPtyService as ICommonLocalPtyService } from 'vs/platform/terminal/common/terminal';

export const ILocalPtyService = createDecorator<ILocalPtyService>('localPtyService');

Expand Down
13 changes: 13 additions & 0 deletions src/vs/platform/terminal/node/ptyHostMain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { PtyService } from 'vs/platform/terminal/node/ptyService';
import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';

const server = new Server('ptyHost');
const service = new PtyService();
server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(service));
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { ICommonLocalPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
import { ILocalPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess';
import { Emitter } from 'vs/base/common/event';
import { LogService, ConsoleLogger } from 'vs/platform/log/common/log';

export const ILocalPtyMainService = createDecorator<ILocalPtyMainService>('localPtyMainService');

export interface ILocalPtyMainService extends ICommonLocalPtyService { }

let currentLocalPtyId = 0;
let currentPtyId = 0;

export class LocalPtyMainService extends Disposable implements ICommonLocalPtyService {
export class PtyService extends Disposable implements ILocalPtyService {
declare readonly _serviceBrand: undefined;

private readonly _localPtys: Map<number, ITerminalChildProcess> = new Map();
private readonly _ptys: Map<number, ITerminalChildProcess> = new Map();

private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>());
readonly onProcessData = this._onProcessData.event;
Expand All @@ -36,14 +31,14 @@ export class LocalPtyMainService extends Disposable implements ICommonLocalPtySe
readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event;

constructor(
@ILogService private readonly _logService: ILogService
) {
super();
}

async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise<number> {
const id = ++currentLocalPtyId;
const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, this._logService);
const id = ++currentPtyId;
// TODO: Impl proper logging, level doesn't get passed over
const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, new LogService(new ConsoleLogger()));
process.onProcessData(event => this._onProcessData.fire({ id, event }));
process.onProcessExit(event => this._onProcessExit.fire({ id, event }));
process.onProcessReady(event => this._onProcessReady.fire({ id, event }));
Expand All @@ -54,7 +49,7 @@ export class LocalPtyMainService extends Disposable implements ICommonLocalPtySe
if (process.onProcessResolvedShellLaunchConfig) {
process.onProcessResolvedShellLaunchConfig(event => this._onProcessResolvedShellLaunchConfig.fire({ id, event }));
}
this._localPtys.set(id, process);
this._ptys.set(id, process);
return id;
}

Expand Down Expand Up @@ -91,7 +86,7 @@ export class LocalPtyMainService extends Disposable implements ICommonLocalPtySe
}

private _throwIfNoPty(id: number): ITerminalChildProcess {
const pty = this._localPtys.get(id);
const pty = this._ptys.get(id);
if (!pty) {
throw new Error(`Could not find pty with id "${id}"`);
}
Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/buildfile.desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ exports.collectModules = function () {
createModuleDescription('vs/platform/files/node/watcher/unix/watcherApp', []),
createModuleDescription('vs/platform/files/node/watcher/nsfw/watcherApp', []),

createModuleDescription('vs/platform/terminal/node/ptyHostMain', []),

createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []),
];
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ export class TerminalInstanceService implements ITerminalInstanceService {

public async createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): Promise<ITerminalChildProcess> {
const id = await this._localPtyService.createProcess(shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty);
console.log('local pty id ' + id);
return this._instantiationService.createInstance(TerminalProcessMainProxy, id);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
*--------------------------------------------------------------------------------------------*/

import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';

// @ts-ignore: interface is implemented via proxy
export class LocalPtyService implements ILocalPtyService {
export class LocalPtyServiceProxy implements ILocalPtyService {

declare readonly _serviceBrand: undefined;

constructor(@IMainProcessService mainProcessService: IMainProcessService) {
return createChannelSender<ILocalPtyService>(mainProcessService.getChannel('localPty'));
constructor(
@ISharedProcessService sharedProcessService: ISharedProcessService
) {
return ProxyChannel.toService<LocalPtyServiceProxy & ILocalPtyService>(sharedProcessService.getChannel(TerminalIpcChannels.LocalPty));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { LocalPtyService } from 'vs/workbench/contrib/terminal/electron-sandbox/localPtyService';
import { LocalPtyServiceProxy } from 'vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy';

registerSingleton(ILocalPtyService, LocalPtyService, true);
registerSingleton(ILocalPtyService, LocalPtyServiceProxy, true);

0 comments on commit 1819dd5

Please sign in to comment.