chore(vscode): update to 1.56.0
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import type * as pty from 'node-pty';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
@@ -17,6 +16,8 @@ import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { WindowsShellHelper } from 'vs/platform/terminal/node/windowsShellHelper';
|
||||
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
// Writing large amounts of data can be corrupted for some reason, after looking into this is
|
||||
// appears to be a race condition around writing to the FD which may be based on how powerful the
|
||||
@@ -44,9 +45,33 @@ const enum ShutdownConstants {
|
||||
MaximumShutdownTime = 5000
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
* The minimum duration between kill and spawn calls on Windows/conpty as a mitigation for a
|
||||
* hang issue. See:
|
||||
* - https://github.com/microsoft/vscode/issues/71966
|
||||
* - https://github.com/microsoft/vscode/issues/117956
|
||||
* - https://github.com/microsoft/vscode/issues/121336
|
||||
*/
|
||||
KillSpawnThrottleInterval = 250,
|
||||
/**
|
||||
* The amount of time to wait when a call is throttles beyond the exact amount, this is used to
|
||||
* try prevent early timeouts causing a kill/spawn call to happen at double the regular
|
||||
* interval.
|
||||
*/
|
||||
KillSpawnSpacingDuration = 50,
|
||||
}
|
||||
|
||||
interface IWriteObject {
|
||||
data: string,
|
||||
isBinary: boolean
|
||||
}
|
||||
|
||||
export class TerminalProcess extends Disposable implements ITerminalChildProcess {
|
||||
readonly shouldPersist = false;
|
||||
|
||||
private static _lastKillOrStart = 0;
|
||||
|
||||
private _exitCode: number | undefined;
|
||||
private _exitMessage: string | undefined;
|
||||
private _closeTimeout: any;
|
||||
@@ -62,7 +87,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
private _isDisposed: boolean = false;
|
||||
private _windowsShellHelper: WindowsShellHelper | undefined;
|
||||
private _titleInterval: NodeJS.Timer | null = null;
|
||||
private _writeQueue: string[] = [];
|
||||
private _writeQueue: IWriteObject[] = [];
|
||||
private _writeTimeout: NodeJS.Timeout | undefined;
|
||||
private _delayedResizer: DelayedResizer | undefined;
|
||||
private readonly _initialCwd: string;
|
||||
@@ -93,17 +118,17 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
cwd: string,
|
||||
cols: number,
|
||||
rows: number,
|
||||
env: platform.IProcessEnvironment,
|
||||
env: IProcessEnvironment,
|
||||
/**
|
||||
* environment used for `findExecutable`
|
||||
*/
|
||||
private readonly _executableEnv: platform.IProcessEnvironment,
|
||||
private readonly _executableEnv: IProcessEnvironment,
|
||||
windowsEnableConpty: boolean,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
super();
|
||||
let name: string;
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
name = path.basename(this._shellLaunchConfig.executable || '');
|
||||
} else {
|
||||
// Using 'xterm-256color' here helps ensure that the majority of Linux distributions will use a
|
||||
@@ -115,7 +140,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
this._ptyOptions = {
|
||||
name,
|
||||
cwd,
|
||||
env,
|
||||
// TODO: When node-pty is updated this cast can be removed
|
||||
env: env as { [key: string]: string; },
|
||||
cols,
|
||||
rows,
|
||||
useConpty,
|
||||
@@ -123,7 +149,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
conptyInheritCursor: useConpty && !!_shellLaunchConfig.initialText
|
||||
};
|
||||
// Delay resizes to avoid conpty not respecting very early resize calls
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
if (useConpty && cols === 0 && rows === 0 && this._shellLaunchConfig.executable?.endsWith('Git\\bin\\bash.exe')) {
|
||||
this._delayedResizer = new DelayedResizer();
|
||||
this._register(this._delayedResizer.onTrigger(dimensions => {
|
||||
@@ -194,6 +220,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
if (!executable) {
|
||||
return { message: localize('launchFail.executableDoesNotExist', "Path to shell executable \"{0}\" does not exist", slc.executable) };
|
||||
}
|
||||
// Set the executable explicitly here so that node-pty doesn't need to search the
|
||||
// $PATH too.
|
||||
slc.executable = executable;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -201,6 +230,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
|
||||
private async setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): Promise<void> {
|
||||
const args = shellLaunchConfig.args || [];
|
||||
await this._throttleKillSpawn();
|
||||
this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options);
|
||||
const ptyProcess = (await import('node-pty')).spawn(shellLaunchConfig.executable!, args, options);
|
||||
this._ptyProcess = ptyProcess;
|
||||
@@ -220,7 +250,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
// Refire the data event
|
||||
this._onProcessData.fire(data);
|
||||
if (this._closeTimeout) {
|
||||
clearTimeout(this._closeTimeout);
|
||||
this._queueProcessExit();
|
||||
}
|
||||
this._windowsShellHelper?.checkShell();
|
||||
@@ -233,7 +262,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
this._sendProcessId(ptyProcess.pid);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
public override dispose(): void {
|
||||
this._isDisposed = true;
|
||||
if (this._titleInterval) {
|
||||
clearInterval(this._titleInterval);
|
||||
@@ -246,7 +275,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
// Send initial timeout async to give event listeners a chance to init
|
||||
setTimeout(() => this._sendProcessTitle(ptyProcess), 0);
|
||||
// Setup polling for non-Windows, for Windows `process` doesn't change
|
||||
if (!platform.isWindows) {
|
||||
if (!isWindows) {
|
||||
this._titleInterval = setInterval(() => {
|
||||
if (this._currentTitle !== ptyProcess.process) {
|
||||
this._sendProcessTitle(ptyProcess);
|
||||
@@ -278,6 +307,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
// point but we want to make sure
|
||||
try {
|
||||
if (this._ptyProcess) {
|
||||
await this._throttleKillSpawn();
|
||||
this._logService.trace('IPty#kill');
|
||||
this._ptyProcess.kill();
|
||||
}
|
||||
@@ -288,6 +318,19 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
private async _throttleKillSpawn(): Promise<void> {
|
||||
// Only throttle on Windows/conpty
|
||||
if (!isWindows || !('useConpty' in this._ptyOptions) || !this._ptyOptions.useConpty) {
|
||||
return;
|
||||
}
|
||||
// Use a loop to ensure multiple calls in a single interval space out
|
||||
while (Date.now() - TerminalProcess._lastKillOrStart < Constants.KillSpawnThrottleInterval) {
|
||||
this._logService.trace('Throttling kill/spawn call');
|
||||
await timeout(Constants.KillSpawnThrottleInterval - (Date.now() - TerminalProcess._lastKillOrStart) + Constants.KillSpawnSpacingDuration);
|
||||
}
|
||||
TerminalProcess._lastKillOrStart = Date.now();
|
||||
}
|
||||
|
||||
private _sendProcessId(pid: number) {
|
||||
this._onProcessReady.fire({ pid, cwd: this._initialCwd });
|
||||
}
|
||||
@@ -301,7 +344,10 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
}
|
||||
|
||||
public shutdown(immediate: boolean): void {
|
||||
if (immediate) {
|
||||
// don't force immediate disposal of the terminal processes on Windows as an additional
|
||||
// mitigation for https://github.com/microsoft/vscode/issues/71966 which causes the pty host
|
||||
// to become unresponsive, disconnecting all terminals across all windows.
|
||||
if (immediate && !isWindows) {
|
||||
this._kill();
|
||||
} else {
|
||||
if (!this._closeTimeout && !this._isDisposed) {
|
||||
@@ -317,16 +363,24 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
}
|
||||
}
|
||||
|
||||
public input(data: string): void {
|
||||
public input(data: string, isBinary?: boolean): void {
|
||||
if (this._isDisposed || !this._ptyProcess) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i <= Math.floor(data.length / WRITE_MAX_CHUNK_SIZE); i++) {
|
||||
this._writeQueue.push(data.substr(i * WRITE_MAX_CHUNK_SIZE, WRITE_MAX_CHUNK_SIZE));
|
||||
const obj = {
|
||||
isBinary: isBinary || false,
|
||||
data: data.substr(i * WRITE_MAX_CHUNK_SIZE, WRITE_MAX_CHUNK_SIZE)
|
||||
};
|
||||
this._writeQueue.push(obj);
|
||||
}
|
||||
this._startWrite();
|
||||
}
|
||||
|
||||
public async processBinary(data: string): Promise<void> {
|
||||
this.input(data, true);
|
||||
}
|
||||
|
||||
private _startWrite(): void {
|
||||
// Don't write if it's already queued of is there is nothing to write
|
||||
if (this._writeTimeout !== undefined || this._writeQueue.length === 0) {
|
||||
@@ -349,9 +403,12 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
}
|
||||
|
||||
private _doWrite(): void {
|
||||
const data = this._writeQueue.shift()!;
|
||||
this._logService.trace('IPty#write', `${data.length} characters`);
|
||||
this._ptyProcess!.write(data);
|
||||
const object = this._writeQueue.shift()!;
|
||||
if (object.isBinary) {
|
||||
this._ptyProcess!.write(Buffer.from(object.data, 'binary') as any);
|
||||
} else {
|
||||
this._ptyProcess!.write(object.data);
|
||||
}
|
||||
}
|
||||
|
||||
public resize(cols: number, rows: number): void {
|
||||
@@ -412,7 +469,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
}
|
||||
|
||||
public getCwd(): Promise<string> {
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
// Disable cwd lookup on macOS Big Sur due to spawn blocking thread (darwin v20 is macOS
|
||||
// Big Sur) https://github.com/Microsoft/vscode/issues/105446
|
||||
const osRelease = os.release().split('.');
|
||||
@@ -435,7 +492,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.isLinux) {
|
||||
if (isLinux) {
|
||||
return new Promise<string>(resolve => {
|
||||
if (!this._ptyProcess) {
|
||||
resolve(this._initialCwd);
|
||||
@@ -484,7 +541,7 @@ class DelayedResizer extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user