-
Notifications
You must be signed in to change notification settings - Fork 253
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #415 from microsoft/hang_fix
Host conout socket in a worker
- Loading branch information
Showing
10 changed files
with
164 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Copyright (c) 2020, Microsoft Corporation (MIT License). | ||
*/ | ||
|
||
export interface IWorkerData { | ||
conoutPipeName: string; | ||
} | ||
|
||
export const enum ConoutWorkerMessage { | ||
READY = 1 | ||
} | ||
|
||
export function getWorkerPipeName(conoutPipeName: string): string { | ||
return `${conoutPipeName}-worker`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/** | ||
* Copyright (c) 2020, Microsoft Corporation (MIT License). | ||
*/ | ||
|
||
import { Worker } from 'worker_threads'; | ||
import { Socket } from 'net'; | ||
import { IDisposable } from './types'; | ||
import { IWorkerData, ConoutWorkerMessage, getWorkerPipeName } from './shared/conout'; | ||
import { join } from 'path'; | ||
import { IEvent, EventEmitter2 } from './eventEmitter2'; | ||
|
||
/** | ||
* The amount of time to wait for additional data after the conpty shell process has exited before | ||
* shutting down the worker and sockets. The timer will be reset if a new data event comes in after | ||
* the timer has started. | ||
*/ | ||
const FLUSH_DATA_INTERVAL = 1000; | ||
|
||
/** | ||
* Connects to and manages the lifecycle of the conout socket. This socket must be drained on | ||
* another thread in order to avoid deadlocks where Conpty waits for the out socket to drain | ||
* when `ClosePseudoConsole` is called. This happens when data is being written to the terminal when | ||
* the pty is closed. | ||
* | ||
* See also: | ||
* - https://github.com/microsoft/node-pty/issues/375 | ||
* - https://github.com/microsoft/vscode/issues/76548 | ||
* - https://github.com/microsoft/terminal/issues/1810 | ||
* - https://docs.microsoft.com/en-us/windows/console/closepseudoconsole | ||
*/ | ||
export class ConoutConnection implements IDisposable { | ||
private _worker: Worker; | ||
private _drainTimeout: NodeJS.Timeout; | ||
private _isDisposed: boolean = false; | ||
|
||
private _onReady = new EventEmitter2<void>(); | ||
public get onReady(): IEvent<void> { return this._onReady.event; } | ||
|
||
constructor( | ||
private _conoutPipeName: string | ||
) { | ||
const workerData: IWorkerData = { conoutPipeName: _conoutPipeName }; | ||
this._worker = new Worker(join(__dirname, 'worker/conoutSocketWorker.js'), { workerData }); | ||
this._worker.on('message', (message: ConoutWorkerMessage) => { | ||
switch (message) { | ||
case ConoutWorkerMessage.READY: | ||
this._onReady.fire(); | ||
return; | ||
default: | ||
console.warn('Unexpected ConoutWorkerMessage', message); | ||
} | ||
}); | ||
} | ||
|
||
dispose(): void { | ||
if (this._isDisposed) { | ||
return; | ||
} | ||
this._isDisposed = true; | ||
// Drain all data from the socket before closing | ||
this._drainDataAndClose(); | ||
} | ||
|
||
connectSocket(socket: Socket): void { | ||
socket.connect(getWorkerPipeName(this._conoutPipeName)); | ||
} | ||
|
||
private _drainDataAndClose(): void { | ||
if (this._drainTimeout) { | ||
clearTimeout(this._drainTimeout); | ||
} | ||
this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL); | ||
} | ||
|
||
private async _destroySocket(): Promise<void> { | ||
await this._worker.terminate(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Copyright (c) 2020, Microsoft Corporation (MIT License). | ||
*/ | ||
|
||
import { parentPort, workerData } from 'worker_threads'; | ||
import { Socket, createServer } from 'net'; | ||
import { ConoutWorkerMessage, IWorkerData, getWorkerPipeName } from '../shared/conout'; | ||
|
||
const conoutPipeName = (workerData as IWorkerData).conoutPipeName; | ||
const conoutSocket = new Socket(); | ||
conoutSocket.setEncoding('utf8'); | ||
conoutSocket.connect(conoutPipeName, () => { | ||
const server = createServer(workerSocket => { | ||
conoutSocket.pipe(workerSocket); | ||
}); | ||
server.listen(getWorkerPipeName(conoutPipeName)); | ||
parentPort.postMessage(ConoutWorkerMessage.READY); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// This test creates a pty periodically, spamming it with echo calls and killing it shortly after. | ||
// It's a test case for https://github.com/microsoft/node-pty/issues/375, the script will hang | ||
// when it show this bug instead of continuing to create more processes. | ||
|
||
var os = require('os'); | ||
var pty = require('..'); | ||
|
||
var isWindows = os.platform() === 'win32'; | ||
var shell = isWindows ? 'cmd.exe' : 'bash'; | ||
|
||
let i = 0; | ||
|
||
setInterval(() => { | ||
console.log(`creating pty ${++i}`); | ||
var ptyProcess = pty.spawn(shell, [], { | ||
name: 'xterm-256color', | ||
cols: 80, | ||
rows: 26, | ||
cwd: isWindows ? process.env.USERPROFILE : process.env.HOME, | ||
env: Object.assign({ TEST: "Environment vars work" }, process.env), | ||
useConpty: true | ||
}); | ||
|
||
ptyProcess.onData(data => console.log(` data: ${data.replace(/\x1b|\n|\r/g, '_')}`)); | ||
|
||
setInterval(() => { | ||
ptyProcess.write('echo foo\r'.repeat(50)); | ||
}, 10); | ||
setTimeout(() => { | ||
console.log(` killing ${ptyProcess.pid}...`); | ||
ptyProcess.kill(); | ||
}, 100); | ||
}, 1200); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters