Skip to content

Commit 7c4b87b

Browse files
committed
Fix renderer losing connection on pty host restart
Fixes microsoft#186772 Fixes microsoft#186773
1 parent 738e5c6 commit 7c4b87b

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(lastPtyId: number): 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(lastPtyId: number): 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
@@ -52,7 +52,7 @@ export class PtyHostService extends Disposable implements IPtyService {
5252

5353
private _ensurePtyHost() {
5454
if (!this.__connection) {
55-
[this.__connection, this.__proxy] = this._startPtyHost();
55+
this._startPtyHost();
5656
}
5757
}
5858

@@ -102,9 +102,9 @@ export class PtyHostService extends Disposable implements IPtyService {
102102
// remote server).
103103
registerTerminalPlatformConfiguration();
104104

105+
this._register(this._ptyHostStarter);
105106
this._register(toDisposable(() => this._disposePtyHost()));
106107

107-
108108
this._resolveVariablesRequestStore = this._register(new RequestStore(undefined, this._logService));
109109
this._resolveVariablesRequestStore.onCreateRequest(this._onPtyHostRequestResolveVariables.fire, this._onPtyHostRequestResolveVariables);
110110

@@ -145,8 +145,6 @@ export class PtyHostService extends Disposable implements IPtyService {
145145
const connection = this._ptyHostStarter.start(lastPtyId);
146146
const client = connection.client;
147147

148-
this._onPtyHostStart.fire();
149-
150148
// Setup heartbeat service and trigger a heartbeat immediately to reset the timeouts
151149
const heartbeatService = ProxyChannel.toService<IHeartbeatService>(client.getChannel(TerminalIpcChannels.Heartbeat));
152150
heartbeatService.onBeat(() => this._handleHeartbeat());
@@ -181,6 +179,8 @@ export class PtyHostService extends Disposable implements IPtyService {
181179
this.__connection = connection;
182180
this.__proxy = proxy;
183181

182+
this._onPtyHostStart.fire();
183+
184184
this._register(this._configurationService.onDidChangeConfiguration(async e => {
185185
if (e.affectsConfiguration(TerminalSettingId.IgnoreProcessNames)) {
186186
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)