Skip to content

Commit c48a57b

Browse files
authored
Merge pull request #118437 from microsoft/tyriar/1_54_117956
Don't force kill ptys on Windows on window exit
2 parents bd65564 + b31f6e1 commit c48a57b

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ import { localize } from 'vs/nls';
2424
const WRITE_MAX_CHUNK_SIZE = 50;
2525
const WRITE_INTERVAL_MS = 5;
2626

27+
const enum ShutdownConstants {
28+
/**
29+
* The amount of time that must pass between data events after exit is queued before the actual
30+
* kill call is triggered. This data flush mechanism works around an [issue in node-pty][1]
31+
* where not all data is flushed which causes problems for task problem matchers. Additionally
32+
* on Windows under conpty, killing a process while data is being output will cause the [conhost
33+
* flush to hang the pty host][2] because [conhost should be hosted on another thread][3].
34+
*
35+
* [1]: https://github.com/Tyriar/node-pty/issues/72
36+
* [2]: https://github.com/microsoft/vscode/issues/71966
37+
* [3]: https://github.com/microsoft/node-pty/pull/415
38+
*/
39+
DataFlushTimeout = 250,
40+
/**
41+
* The maximum time to allow after dispose is called because forcefully killing the process.
42+
*/
43+
MaximumShutdownTime = 5000
44+
}
45+
2746
export class TerminalProcess extends Disposable implements ITerminalChildProcess {
2847
readonly id = 0;
2948
readonly shouldPersist = false;
@@ -223,7 +242,10 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
223242
if (this._closeTimeout) {
224243
clearTimeout(this._closeTimeout);
225244
}
226-
this._closeTimeout = setTimeout(() => this._kill(), 250);
245+
this._closeTimeout = setTimeout(() => {
246+
this._closeTimeout = undefined;
247+
this._kill();
248+
}, ShutdownConstants.DataFlushTimeout);
227249
}
228250

229251
private async _kill(): Promise<void> {
@@ -263,7 +285,16 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
263285
if (immediate) {
264286
this._kill();
265287
} else {
266-
this._queueProcessExit();
288+
if (!this._closeTimeout && !this._isDisposed) {
289+
this._queueProcessExit();
290+
// Allow a maximum amount of time for the process to exit, otherwise force kill it
291+
setTimeout(() => {
292+
if (this._closeTimeout && !this._isDisposed) {
293+
this._closeTimeout = undefined;
294+
this._kill();
295+
}
296+
}, ShutdownConstants.MaximumShutdownTime);
297+
}
267298
}
268299
}
269300

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,12 @@ export class TerminalService implements ITerminalService {
334334
return;
335335
}
336336

337-
// Force dispose of all terminal instances
338-
this.terminalInstances.forEach(instance => instance.dispose(true));
337+
// Force dispose of all terminal instances, don't force immediate disposal of the terminal
338+
// processes on Windows as an additional mitigation for https://github.com/microsoft/vscode/issues/71966
339+
// which causes the pty host to become unresponsive, disconnecting all terminals across all
340+
// windows
341+
this.terminalInstances.forEach(instance => instance.dispose(!isWindows));
342+
339343
this._terminalInstanceService.setTerminalLayoutInfo(undefined);
340344
}
341345

0 commit comments

Comments
 (0)