Skip to content

Commit dab1430

Browse files
committed
Shutdown exthost terminal processes gracefully
Fixes microsoft#107444
1 parent b241d4c commit dab1430

2 files changed

Lines changed: 29 additions & 18 deletions

File tree

src/vs/workbench/api/common/extHostTerminalService.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IT
1313
import { timeout } from 'vs/base/common/async';
1414
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
1515
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
16-
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
16+
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
1717
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
1818
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1919
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
@@ -22,7 +22,7 @@ import { NotSupportedError } from 'vs/base/common/errors';
2222
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
2323
import { CancellationTokenSource } from 'vs/base/common/cancellation';
2424

25-
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
25+
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
2626

2727
readonly _serviceBrand: undefined;
2828

@@ -306,14 +306,14 @@ interface ICachedLinkEntry {
306306
link: vscode.TerminalLink;
307307
}
308308

309-
export abstract class BaseExtHostTerminalService implements IExtHostTerminalService, ExtHostTerminalServiceShape {
309+
export abstract class BaseExtHostTerminalService extends Disposable implements IExtHostTerminalService, ExtHostTerminalServiceShape {
310310

311311
readonly _serviceBrand: undefined;
312312

313313
protected _proxy: MainThreadTerminalServiceShape;
314314
protected _activeTerminal: ExtHostTerminal | undefined;
315315
protected _terminals: ExtHostTerminal[] = [];
316-
protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
316+
protected _terminalProcesses: Map<number, ITerminalChildProcess> = new Map();
317317
protected _terminalProcessDisposables: { [id: number]: IDisposable } = {};
318318
protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
319319
protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal | undefined> } = {};
@@ -342,13 +342,21 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
342342
supportsProcesses: boolean,
343343
@IExtHostRpcService extHostRpc: IExtHostRpcService
344344
) {
345+
super();
345346
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService);
346347
this._bufferer = new TerminalDataBufferer(this._proxy.$sendProcessData);
347348
this._onDidWriteTerminalData = new Emitter<vscode.TerminalDataWriteEvent>({
348349
onFirstListenerAdd: () => this._proxy.$startSendingDataEvents(),
349350
onLastListenerRemove: () => this._proxy.$stopSendingDataEvents()
350351
});
351352
this._proxy.$registerProcessSupport(supportsProcesses);
353+
this._register({
354+
dispose: () => {
355+
for (const [_, terminalProcess] of this._terminalProcesses) {
356+
terminalProcess.shutdown(true);
357+
}
358+
}
359+
});
352360
}
353361

354362
public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal;
@@ -421,11 +429,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
421429
public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise<void> {
422430
await this._getTerminalByIdEventually(id);
423431

424-
if (this._terminalProcesses[id]) {
425-
// Extension pty terminal only - when virtual process resize fires it means that the
426-
// terminal's maximum dimensions changed
427-
this._terminalProcesses[id]?.resize(cols, rows);
428-
}
432+
// Extension pty terminal only - when virtual process resize fires it means that the
433+
// terminal's maximum dimensions changed
434+
this._terminalProcesses.get(id)?.resize(cols, rows);
429435
}
430436

431437
public async $acceptTerminalTitleChange(id: number, name: string): Promise<void> {
@@ -497,8 +503,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
497503
});
498504
}
499505

500-
if (this._terminalProcesses[id]) {
501-
(this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions);
506+
const terminalProcess = this._terminalProcesses.get(id);
507+
if (terminalProcess) {
508+
(terminalProcess as ExtHostPseudoterminal).startSendingEvents(initialDimensions);
502509
} else {
503510
// Defer startSendingEvents call to when _setupExtHostProcessListeners is called
504511
this._extensionTerminalAwaitingStart[id] = { initialDimensions };
@@ -520,7 +527,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
520527
if (p.onProcessOverrideDimensions) {
521528
disposables.add(p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e)));
522529
}
523-
this._terminalProcesses[id] = p;
530+
this._terminalProcesses.set(id, p);
524531

525532
const awaitingStart = this._extensionTerminalAwaitingStart[id];
526533
if (awaitingStart && p instanceof ExtHostPseudoterminal) {
@@ -532,12 +539,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
532539
}
533540

534541
public $acceptProcessInput(id: number, data: string): void {
535-
this._terminalProcesses[id]?.input(data);
542+
this._terminalProcesses.get(id)?.input(data);
536543
}
537544

538545
public $acceptProcessResize(id: number, cols: number, rows: number): void {
539546
try {
540-
this._terminalProcesses[id]?.resize(cols, rows);
547+
this._terminalProcesses.get(id)?.resize(cols, rows);
541548
} catch (error) {
542549
// We tried to write to a closed pipe / channel.
543550
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
@@ -547,15 +554,15 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
547554
}
548555

549556
public $acceptProcessShutdown(id: number, immediate: boolean): void {
550-
this._terminalProcesses[id]?.shutdown(immediate);
557+
this._terminalProcesses.get(id)?.shutdown(immediate);
551558
}
552559

553560
public $acceptProcessRequestInitialCwd(id: number): void {
554-
this._terminalProcesses[id]?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd));
561+
this._terminalProcesses.get(id)?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd));
555562
}
556563

557564
public $acceptProcessRequestCwd(id: number): void {
558-
this._terminalProcesses[id]?.getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd));
565+
this._terminalProcesses.get(id)?.getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd));
559566
}
560567

561568
public $acceptProcessRequestLatency(id: number): number {
@@ -648,7 +655,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
648655
this._bufferer.stopBuffering(id);
649656

650657
// Remove process reference
651-
delete this._terminalProcesses[id];
658+
this._terminalProcesses.delete(id);
652659
delete this._extensionTerminalAwaitingStart[id];
653660

654661
// Clean up process disposables

src/vs/workbench/services/extensions/common/extensionHostMain.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
2121
import { IExtHostRpcService, ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
2222
import { IURITransformerService, URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
2323
import { IExtHostExtensionService, IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
24+
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
2425

2526
export interface IExitFn {
2627
(code?: number): any;
@@ -61,6 +62,9 @@ export class ExtensionHostMain {
6162

6263
// todo@joh
6364
// ugly self - inject
65+
const terminalService = instaService.invokeFunction(accessor => accessor.get(IExtHostTerminalService));
66+
this._disposables.add(terminalService);
67+
6468
const logService = instaService.invokeFunction(accessor => accessor.get(ILogService));
6569
this._disposables.add(logService);
6670

0 commit comments

Comments
 (0)