Skip to content

Commit f2386a5

Browse files
authored
Merge pull request microsoft#186787 from microsoft/tyriar/pty_host_lifecycle
Fix renderer losing connection on pty host restart
2 parents 303f904 + 7c4b87b commit f2386a5

File tree

6 files changed

+24
-15
lines changed

6 files changed

+24
-15
lines changed

src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utility
1414
import { Client as MessagePortClient } from 'vs/base/parts/ipc/electron-main/ipc.mp';
1515
import { IpcMainEvent } from 'electron';
1616
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
17-
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
17+
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
1818
import { Emitter } from 'vs/base/common/event';
1919
import { deepClone } from 'vs/base/common/objects';
2020
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2121

22-
export class ElectronPtyHostStarter implements IPtyHostStarter {
22+
export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarter {
2323

2424
private utilityProcess: UtilityProcess | undefined = undefined;
2525

@@ -35,9 +35,14 @@ export class ElectronPtyHostStarter implements IPtyHostStarter {
3535
@ILifecycleMainService private readonly _lifecycleMainService: ILifecycleMainService,
3636
@ILogService private readonly _logService: ILogService
3737
) {
38+
super();
39+
3840
this._lifecycleMainService.onWillShutdown(() => this._onWillShutdown.fire());
3941
// Listen for new windows to establish connection directly to pty host
4042
validatedIpcMain.on('vscode:createPtyHostMessageChannel', (e, nonce) => this._onWindowConnection(e, nonce));
43+
this._register(toDisposable(() => {
44+
validatedIpcMain.removeHandler('vscode:createPtyHostMessageChannel');
45+
}));
4146
}
4247

4348
start(): IPtyHostConnection {
@@ -62,9 +67,9 @@ export class ElectronPtyHostStarter implements IPtyHostStarter {
6267

6368
const store = new DisposableStore();
6469
store.add(client);
65-
store.add(this.utilityProcess);
6670
store.add(toDisposable(() => {
67-
validatedIpcMain.removeHandler('vscode:createPtyHostMessageChannel');
71+
this.utilityProcess?.kill();
72+
this.utilityProcess?.dispose();
6873
this.utilityProcess = undefined;
6974
}));
7075

src/vs/platform/terminal/node/nodePtyHostStarter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { DisposableStore } from 'vs/base/common/lifecycle';
6+
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
77
import { FileAccess } from 'vs/base/common/network';
88
import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp';
99
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
1010
import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService';
1111
import { IReconnectConstants } from 'vs/platform/terminal/common/terminal';
1212
import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost';
1313

14-
export class NodePtyHostStarter implements IPtyHostStarter {
14+
export class NodePtyHostStarter extends Disposable implements IPtyHostStarter {
1515
constructor(
1616
private readonly _reconnectConstants: IReconnectConstants,
1717
@IEnvironmentService private readonly _environmentService: INativeEnvironmentService
1818
) {
19+
super();
1920
}
2021

2122
start(): IPtyHostConnection {

src/vs/platform/terminal/node/ptyHost.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Event } from 'vs/base/common/event';
7-
import { DisposableStore } from 'vs/base/common/lifecycle';
7+
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
88
import { IChannelClient } from 'vs/base/parts/ipc/common/ipc';
99

1010
export interface IPtyHostConnection {
@@ -13,7 +13,7 @@ export interface IPtyHostConnection {
1313
readonly onDidProcessExit: Event<{ code: number; signal: string }>;
1414
}
1515

16-
export interface IPtyHostStarter {
16+
export interface IPtyHostStarter extends IDisposable {
1717
onBeforeWindowConnection?: Event<void>;
1818
onWillShutdown?: Event<void>;
1919

src/vs/platform/terminal/node/ptyHostService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class PtyHostService extends Disposable implements IPtyService {
4646

4747
private _ensurePtyHost() {
4848
if (!this.__connection) {
49-
[this.__connection, this.__proxy] = this._startPtyHost();
49+
this._startPtyHost();
5050
}
5151
}
5252

@@ -96,9 +96,9 @@ export class PtyHostService extends Disposable implements IPtyService {
9696
// remote server).
9797
registerTerminalPlatformConfiguration();
9898

99+
this._register(this._ptyHostStarter);
99100
this._register(toDisposable(() => this._disposePtyHost()));
100101

101-
102102
this._resolveVariablesRequestStore = this._register(new RequestStore(undefined, this._logService));
103103
this._resolveVariablesRequestStore.onCreateRequest(this._onPtyHostRequestResolveVariables.fire, this._onPtyHostRequestResolveVariables);
104104

@@ -139,8 +139,6 @@ export class PtyHostService extends Disposable implements IPtyService {
139139
const connection = this._ptyHostStarter.start();
140140
const client = connection.client;
141141

142-
this._onPtyHostStart.fire();
143-
144142
// Setup heartbeat service and trigger a heartbeat immediately to reset the timeouts
145143
const heartbeatService = ProxyChannel.toService<IHeartbeatService>(client.getChannel(TerminalIpcChannels.Heartbeat));
146144
heartbeatService.onBeat(() => this._handleHeartbeat());
@@ -175,6 +173,8 @@ export class PtyHostService extends Disposable implements IPtyService {
175173
this.__connection = connection;
176174
this.__proxy = proxy;
177175

176+
this._onPtyHostStart.fire();
177+
178178
this._register(this._configurationService.onDidChangeConfiguration(async e => {
179179
if (e.affectsConfiguration(TerminalSettingId.IgnoreProcessNames)) {
180180
await this._refreshIgnoreProcessNames();

src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export abstract class BaseTerminalBackend extends Disposable {
2222

2323
get isResponsive(): boolean { return !this._isPtyHostUnresponsive; }
2424

25+
protected readonly _onPtyHostConnected = this._register(new Emitter<void>());
26+
readonly onPtyHostConnected = this._onPtyHostConnected.event;
2527
protected readonly _onPtyHostRestart = this._register(new Emitter<void>());
2628
readonly onPtyHostRestart = this._onPtyHostRestart.event;
2729
protected readonly _onPtyHostUnresponsive = this._register(new Emitter<void>());
@@ -50,13 +52,14 @@ export abstract class BaseTerminalBackend extends Disposable {
5052
}));
5153
}
5254
if (this._ptyHostController.onPtyHostStart) {
55+
this.onPtyHostConnected(() => hasStarted = true);
5356
this._register(this._ptyHostController.onPtyHostStart(() => {
5457
this._logService.debug(`The terminal's pty host process is starting`);
55-
// Only fire the event on the 2nd
58+
// Only fire the _restart_ event after it has started
5659
if (hasStarted) {
60+
this._logService.trace('IPtyHostController#onPtyHostRestart');
5761
this._onPtyHostRestart.fire();
5862
}
59-
hasStarted = true;
6063
statusBarAccessor?.dispose();
6164
this._isPtyHostUnresponsive = false;
6265
}));

src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke
107107
// separate interface/service for this one.
108108
const client = new MessagePortClient(port, `window:${this._environmentService.window.id}`);
109109
clientEventually.complete(client);
110+
this._onPtyHostConnected.fire();
110111

111112
// Attach process listeners
112113
this._proxy.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event));
@@ -190,7 +191,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke
190191
shouldPersist: boolean
191192
): Promise<ITerminalChildProcess> {
192193
const executableEnv = await this._shellEnvironmentService.getShellEnv();
193-
// TODO: Using _proxy here bypasses the lastPtyId tracking on the main process
194194
const id = await this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, unicodeVersion, env, executableEnv, options, shouldPersist, this._getWorkspaceId(), this._getWorkspaceName());
195195
const pty = new LocalPty(id, shouldPersist, this._proxy);
196196
this._ptys.set(id, pty);

0 commit comments

Comments
 (0)