diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ddadd6a069d..5d6b474148c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -210,10 +210,11 @@ export class PtyService extends Disposable implements IPtyService { async attachToProcess(id: number): Promise { try { - this._throwIfNoPty(id).attach(); + await this._throwIfNoPty(id).attach(); this._logService.trace(`Persistent process reconnection "${id}"`); } catch (e) { this._logService.trace(`Persistent process reconnection "${id}" failed`, e.message); + throw e; } } @@ -543,8 +544,16 @@ export class PersistentTerminalProcess extends Disposable { })); } - attach(): void { + async attach(): Promise { this._logService.trace('persistentTerminalProcess#attach', this._persistentProcessId); + if (!this._disconnectRunner1.isScheduled() && !this._disconnectRunner2.isScheduled()) { + // Something wrong happened if the disconnect runner is not canceled, this likely means + // multiple windows attempted to attach. + if (!await this._isOrphaned()) { + throw new Error(`Cannot attach to persistent process "${this._persistentProcessId}", it is already adopted`); + } + this._logService.warn(`Persistent process "${this._persistentProcessId}": Process had no disconnect runners but was an orphan`); + } this._disconnectRunner1.cancel(); this._disconnectRunner2.cancel(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index bba89cdbcee..3e2725c70ff 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -210,7 +210,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._dimensions.rows = rows; this._isScreenReaderModeEnabled = isScreenReaderModeEnabled; - let newProcess: ITerminalChildProcess; + let newProcess: ITerminalChildProcess | undefined; if (shellLaunchConfig.customPtyImplementation) { this._processType = ProcessType.PsuedoTerminal; @@ -251,10 +251,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, { remoteAuthority: this.remoteAuthority, os: this.os @@ -294,10 +296,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { newProcess = await this._launchLocalProcess(backend, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled, variableResolver); } if (!this._isDisposed) { @@ -335,7 +339,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (this._preLaunchInputQueue.length > 0 && this._process) { // Send any queued data that's waiting - newProcess.input(this._preLaunchInputQueue.join('')); + newProcess!.input(this._preLaunchInputQueue.join('')); this._preLaunchInputQueue.length = 0; } }),