Skip to content

Commit f06b58c

Browse files
authored
Merge pull request #837 from microsoft/tyriar/833
Remove timeouts and update details in comments
2 parents a0ce44f + 6dda3c4 commit f06b58c

File tree

1 file changed

+20
-23
lines changed

1 file changed

+20
-23
lines changed

src/unixTerminal.ts

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class UnixTerminal extends Terminal {
4444
private _emittedClose: boolean = false;
4545

4646
private readonly _writeQueue: IWriteTask[] = [];
47-
private _writeTimeout: NodeJS.Timeout | undefined;
47+
private _writeImmediate: NodeJS.Immediate | undefined;
4848
private _encoding?: BufferEncoding = undefined;
4949

5050
private _master: net.Socket | undefined;
@@ -191,27 +191,23 @@ export class UnixTerminal extends Terminal {
191191
}
192192

193193
private _processWriteQueue(): void {
194+
this._writeImmediate = undefined;
195+
194196
if (this._writeQueue.length === 0) {
195197
return;
196198
}
197199

198200
const task = this._writeQueue[0];
199201

200202
// Write to the underlying file descriptor and handle it directly, rather
201-
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow the
202-
// errors and cause the thread to block indefinitely.
203+
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow and
204+
// mask errors like EAGAIN and can cause the thread to block indefinitely.
203205
fs.write(this._fd, task.data, task.offset, (err, written) => {
204206
if (err) {
205-
if ((err as any).code === 'EAGAIN') {
206-
// This error appears to get swallowed and translated into
207-
// `ERR_SYSTEM_ERROR` when using tty.WriteStream and not fs.write
208-
// directly.
209-
// The most reliable way to test this is to run `sleep 10` in the
210-
// shell and paste enough data to fill the kernel-level buffer. Once
211-
// the sleep ends, the pty should accept the data again. Re-process
212-
// after a short break.
213-
// TODO: https://github.com/microsoft/node-pty/issues/833#issuecomment-3665099159
214-
this._writeTimeout = setTimeout(() => this._processWriteQueue(), 5);
207+
if ('code' in err && err.code === 'EAGAIN') {
208+
// `setImmediate` is used to yield to the event loop and re-attempt
209+
// the write later.
210+
this._writeImmediate = setImmediate(() => this._processWriteQueue());
215211
} else {
216212
// Stop processing immediately on unexpected error and log
217213
this._writeQueue.length = 0;
@@ -225,16 +221,16 @@ export class UnixTerminal extends Terminal {
225221
this._writeQueue.shift();
226222
}
227223

228-
// Using `setImmediate` here appears to corrupt the data, this may be what
229-
// the interleaving/dropped data comment is about in Node.js' tty module:
230-
// https://github.com/nodejs/node/blob/4cac2b94bed4bf02810be054e8f63c0048c66564/lib/tty.js#L106C1-L111C34
224+
// Since there is more room in the kernel buffer, we can continue to write
225+
// until we hit EAGAIN or exhaust the queue.
231226
//
232-
// Yielding via `setImmediate` also doesn't seem to drain the buffer much
233-
// anyway, so use a short timeout when this happens instead. Note that the `drain`
234-
// event does not appear to happen on `net.Socket`/`tty.WriteStream` when
235-
// writing to ptys.
236-
// TODO: https://github.com/microsoft/node-pty/issues/833#issuecomment-3665099159
237-
this._writeTimeout = setTimeout(() => this._processWriteQueue(), 5);
227+
// Note that old versions of bash, like v3.2 which ships in macOS, appears
228+
// to have a bug in its readline implementation that causes data
229+
// corruption when writes to the pty happens too quickly. Instead of
230+
// trying to workaround that we just accept it so that large pastes are as
231+
// fast as possible.
232+
// Context: https://github.com/microsoft/node-pty/issues/833
233+
this._processWriteQueue();
238234
});
239235
}
240236

@@ -312,7 +308,8 @@ export class UnixTerminal extends Terminal {
312308

313309
this._socket.destroy();
314310

315-
clearTimeout(this._writeTimeout);
311+
clearImmediate(this._writeImmediate);
312+
this._writeImmediate = undefined;
316313
}
317314

318315
public kill(signal?: string): void {

0 commit comments

Comments
 (0)