node-pty 1.1.0-beta42 → 1.1.0-beta43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +2 -0
- package/lib/unixTerminal.js +73 -105
- package/lib/unixTerminal.js.map +1 -1
- package/package.json +1 -1
- package/src/unixTerminal.ts +94 -70
package/binding.gyp
CHANGED
package/lib/unixTerminal.js
CHANGED
|
@@ -12,42 +12,6 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
12
12
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
13
13
|
};
|
|
14
14
|
})();
|
|
15
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
16
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
17
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
18
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
19
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
20
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
21
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
25
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
26
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
27
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
28
|
-
function step(op) {
|
|
29
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
30
|
-
while (_) try {
|
|
31
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
32
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
33
|
-
switch (op[0]) {
|
|
34
|
-
case 0: case 1: t = op; break;
|
|
35
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
36
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
37
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
38
|
-
default:
|
|
39
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
40
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
41
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
42
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
43
|
-
if (t[2]) _.ops.pop();
|
|
44
|
-
_.trys.pop(); continue;
|
|
45
|
-
}
|
|
46
|
-
op = body.call(thisArg, _);
|
|
47
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
48
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
16
|
exports.UnixTerminal = void 0;
|
|
53
17
|
/**
|
|
@@ -76,8 +40,6 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
|
76
40
|
var _this = _super.call(this, opt) || this;
|
|
77
41
|
_this._boundClose = false;
|
|
78
42
|
_this._emittedClose = false;
|
|
79
|
-
_this._writeQueue = [];
|
|
80
|
-
_this._writeInProgress = false;
|
|
81
43
|
if (typeof args === 'string') {
|
|
82
44
|
throw new Error('args as a string is not supported on unix.');
|
|
83
45
|
}
|
|
@@ -132,6 +94,7 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
|
132
94
|
if (encoding !== null) {
|
|
133
95
|
_this._socket.setEncoding(encoding);
|
|
134
96
|
}
|
|
97
|
+
_this._writeStream = new CustomWriteStream(term.fd, (encoding || undefined));
|
|
135
98
|
// setup
|
|
136
99
|
_this._socket.on('error', function (err) {
|
|
137
100
|
// NOTE: fs.ReadStream gets EAGAIN twice at first:
|
|
@@ -190,72 +153,7 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
|
190
153
|
configurable: true
|
|
191
154
|
});
|
|
192
155
|
UnixTerminal.prototype._write = function (data) {
|
|
193
|
-
|
|
194
|
-
// backpressure from the kernel buffer.
|
|
195
|
-
this._writeQueue.push(data);
|
|
196
|
-
if (this._writeInProgress) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
this._writeInProgress = true;
|
|
200
|
-
this._processWriteQueue();
|
|
201
|
-
};
|
|
202
|
-
UnixTerminal.prototype._processWriteQueue = function () {
|
|
203
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
204
|
-
var data;
|
|
205
|
-
var _this = this;
|
|
206
|
-
return __generator(this, function (_a) {
|
|
207
|
-
data = this._writeQueue.shift();
|
|
208
|
-
if (!data) {
|
|
209
|
-
this._writeInProgress = false;
|
|
210
|
-
return [2 /*return*/];
|
|
211
|
-
}
|
|
212
|
-
// Write to the underlying file descriptor and handle it directly, rather
|
|
213
|
-
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow the
|
|
214
|
-
// errors and cause the thread to block indefinitely.
|
|
215
|
-
fs.write(this._fd, data, function (err, written) {
|
|
216
|
-
// Requeue any partial writes
|
|
217
|
-
if (written < data.length) {
|
|
218
|
-
_this._writeQueue.unshift(data.slice(written));
|
|
219
|
-
}
|
|
220
|
-
if (err) {
|
|
221
|
-
var errno = err.errno;
|
|
222
|
-
switch (errno) {
|
|
223
|
-
case -35: // EAGAIN (macOS)
|
|
224
|
-
case -11: // EAGAIN (Linux)
|
|
225
|
-
// This error appears to get swallowed and translated into
|
|
226
|
-
// `ERR_SYSTEM_ERROR` when using tty.WriteStream and not fs.write
|
|
227
|
-
// directly.
|
|
228
|
-
// This can happen during a regular partial write, but the most
|
|
229
|
-
// reliable way to test this is to run `sleep 10` in the shell and
|
|
230
|
-
// paste enough data to fill the kernel-level buffer. Once the sleep
|
|
231
|
-
// ends, the pty should accept the data again. Re-process after a
|
|
232
|
-
// short break.
|
|
233
|
-
_this._writeTimeout = setTimeout(function () { return _this._processWriteQueue(); }, 5);
|
|
234
|
-
return;
|
|
235
|
-
case -5: // EIO
|
|
236
|
-
case -32: // EPIPE
|
|
237
|
-
// Stop processing writes immediately as the pty is closed.
|
|
238
|
-
_this._writeInProgress = false;
|
|
239
|
-
return;
|
|
240
|
-
default:
|
|
241
|
-
console.error('Unhandled pty write error', errno, err);
|
|
242
|
-
// Fall through as it's important to finish processing the queue
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
// Using `setImmediate` here appears to corrupt the data, this may be what
|
|
247
|
-
// the interleaving/dropped data comment is about in Node.js' tty module:
|
|
248
|
-
// https://github.com/nodejs/node/blob/4cac2b94bed4bf02810be054e8f63c0048c66564/lib/tty.js#L106C1-L111C34
|
|
249
|
-
//
|
|
250
|
-
// Yielding via `setImmediate` also doesn't seem to drain the buffer much
|
|
251
|
-
// anyway, so use a short timeout when this happens instead. Note that the `drain`
|
|
252
|
-
// event does not appear to happen on `net.Socket`/`tty.WriteStream` when
|
|
253
|
-
// writing to ptys.
|
|
254
|
-
_this._writeTimeout = setTimeout(function () { return _this._processWriteQueue(); }, 5);
|
|
255
|
-
});
|
|
256
|
-
return [2 /*return*/];
|
|
257
|
-
});
|
|
258
|
-
});
|
|
156
|
+
this._writeStream.write(data);
|
|
259
157
|
};
|
|
260
158
|
Object.defineProperty(UnixTerminal.prototype, "fd", {
|
|
261
159
|
/* Accessors */
|
|
@@ -323,7 +221,7 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
|
323
221
|
_this.kill('SIGHUP');
|
|
324
222
|
});
|
|
325
223
|
this._socket.destroy();
|
|
326
|
-
|
|
224
|
+
this._writeStream.dispose();
|
|
327
225
|
};
|
|
328
226
|
UnixTerminal.prototype.kill = function (signal) {
|
|
329
227
|
try {
|
|
@@ -375,4 +273,74 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
|
375
273
|
return UnixTerminal;
|
|
376
274
|
}(terminal_1.Terminal));
|
|
377
275
|
exports.UnixTerminal = UnixTerminal;
|
|
276
|
+
/**
|
|
277
|
+
* A custom write stream that writes directly to a file descriptor with proper
|
|
278
|
+
* handling of backpressure and errors. This avoids some event loop exhaustion
|
|
279
|
+
* issues that can occur when using the standard APIs in Node.
|
|
280
|
+
*/
|
|
281
|
+
var CustomWriteStream = /** @class */ (function () {
|
|
282
|
+
function CustomWriteStream(_fd, _encoding) {
|
|
283
|
+
this._fd = _fd;
|
|
284
|
+
this._encoding = _encoding;
|
|
285
|
+
this._writeQueue = [];
|
|
286
|
+
}
|
|
287
|
+
CustomWriteStream.prototype.dispose = function () {
|
|
288
|
+
clearImmediate(this._writeImmediate);
|
|
289
|
+
this._writeImmediate = undefined;
|
|
290
|
+
};
|
|
291
|
+
CustomWriteStream.prototype.write = function (data) {
|
|
292
|
+
// Writes are put in a queue and processed asynchronously in order to handle
|
|
293
|
+
// backpressure from the kernel buffer.
|
|
294
|
+
var buffer = typeof data === 'string'
|
|
295
|
+
? Buffer.from(data, this._encoding)
|
|
296
|
+
: Buffer.from(data);
|
|
297
|
+
if (buffer.byteLength !== 0) {
|
|
298
|
+
this._writeQueue.push({ buffer: buffer, offset: 0 });
|
|
299
|
+
if (this._writeQueue.length === 1) {
|
|
300
|
+
this._processWriteQueue();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
CustomWriteStream.prototype._processWriteQueue = function () {
|
|
305
|
+
var _this = this;
|
|
306
|
+
this._writeImmediate = undefined;
|
|
307
|
+
if (this._writeQueue.length === 0) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
var task = this._writeQueue[0];
|
|
311
|
+
// Write to the underlying file descriptor and handle it directly, rather
|
|
312
|
+
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow and
|
|
313
|
+
// mask errors like EAGAIN and can cause the thread to block indefinitely.
|
|
314
|
+
fs.write(this._fd, task.buffer, task.offset, function (err, written) {
|
|
315
|
+
if (err) {
|
|
316
|
+
if ('code' in err && err.code === 'EAGAIN') {
|
|
317
|
+
// `setImmediate` is used to yield to the event loop and re-attempt
|
|
318
|
+
// the write later.
|
|
319
|
+
_this._writeImmediate = setImmediate(function () { return _this._processWriteQueue(); });
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Stop processing immediately on unexpected error and log
|
|
323
|
+
_this._writeQueue.length = 0;
|
|
324
|
+
console.error('Unhandled pty write error', err);
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
task.offset += written;
|
|
329
|
+
if (task.offset >= task.buffer.byteLength) {
|
|
330
|
+
_this._writeQueue.shift();
|
|
331
|
+
}
|
|
332
|
+
// Since there is more room in the kernel buffer, we can continue to write
|
|
333
|
+
// until we hit EAGAIN or exhaust the queue.
|
|
334
|
+
//
|
|
335
|
+
// Note that old versions of bash, like v3.2 which ships in macOS, appears
|
|
336
|
+
// to have a bug in its readline implementation that causes data
|
|
337
|
+
// corruption when writes to the pty happens too quickly. Instead of
|
|
338
|
+
// trying to workaround that we just accept it so that large pastes are as
|
|
339
|
+
// fast as possible.
|
|
340
|
+
// Context: https://github.com/microsoft/node-pty/issues/833
|
|
341
|
+
_this._processWriteQueue();
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
return CustomWriteStream;
|
|
345
|
+
}());
|
|
378
346
|
//# sourceMappingURL=unixTerminal.js.map
|
package/lib/unixTerminal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unixTerminal.js","sourceRoot":"","sources":["../src/unixTerminal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"unixTerminal.js","sourceRoot":"","sources":["../src/unixTerminal.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/unixTerminal.ts
CHANGED
|
@@ -9,7 +9,7 @@ import * as path from 'path';
|
|
|
9
9
|
import * as tty from 'tty';
|
|
10
10
|
import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal';
|
|
11
11
|
import { IProcessEnv, IPtyForkOptions, IPtyOpenOptions } from './interfaces';
|
|
12
|
-
import { ArgvOrCommandLine } from './types';
|
|
12
|
+
import { ArgvOrCommandLine, IDisposable } from './types';
|
|
13
13
|
import { assign, loadNativeModule } from './utils';
|
|
14
14
|
|
|
15
15
|
const native = loadNativeModule('pty');
|
|
@@ -36,9 +36,7 @@ export class UnixTerminal extends Terminal {
|
|
|
36
36
|
private _boundClose: boolean = false;
|
|
37
37
|
private _emittedClose: boolean = false;
|
|
38
38
|
|
|
39
|
-
private
|
|
40
|
-
private _writeInProgress: boolean = false;
|
|
41
|
-
private _writeTimeout: NodeJS.Timeout | undefined;
|
|
39
|
+
private _writeStream: CustomWriteStream;
|
|
42
40
|
|
|
43
41
|
private _master: net.Socket | undefined;
|
|
44
42
|
private _slave: net.Socket | undefined;
|
|
@@ -111,6 +109,7 @@ export class UnixTerminal extends Terminal {
|
|
|
111
109
|
if (encoding !== null) {
|
|
112
110
|
this._socket.setEncoding(encoding);
|
|
113
111
|
}
|
|
112
|
+
this._writeStream = new CustomWriteStream(term.fd, (encoding || undefined) as BufferEncoding);
|
|
114
113
|
|
|
115
114
|
// setup
|
|
116
115
|
this._socket.on('error', (err: any) => {
|
|
@@ -168,69 +167,7 @@ export class UnixTerminal extends Terminal {
|
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
protected _write(data: string | Buffer): void {
|
|
171
|
-
|
|
172
|
-
// backpressure from the kernel buffer.
|
|
173
|
-
this._writeQueue.push(data);
|
|
174
|
-
if (this._writeInProgress) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
this._writeInProgress = true;
|
|
178
|
-
this._processWriteQueue();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private async _processWriteQueue(): Promise<void> {
|
|
182
|
-
const data = this._writeQueue.shift();
|
|
183
|
-
if (!data) {
|
|
184
|
-
this._writeInProgress = false;
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Write to the underlying file descriptor and handle it directly, rather
|
|
189
|
-
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow the
|
|
190
|
-
// errors and cause the thread to block indefinitely.
|
|
191
|
-
fs.write(this._fd, data, (err, written) => {
|
|
192
|
-
// Requeue any partial writes
|
|
193
|
-
if (written < data.length) {
|
|
194
|
-
this._writeQueue.unshift(data.slice(written));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (err) {
|
|
198
|
-
const errno = (err as any).errno;
|
|
199
|
-
switch (errno) {
|
|
200
|
-
case -35: // EAGAIN (macOS)
|
|
201
|
-
case -11: // EAGAIN (Linux)
|
|
202
|
-
// This error appears to get swallowed and translated into
|
|
203
|
-
// `ERR_SYSTEM_ERROR` when using tty.WriteStream and not fs.write
|
|
204
|
-
// directly.
|
|
205
|
-
// This can happen during a regular partial write, but the most
|
|
206
|
-
// reliable way to test this is to run `sleep 10` in the shell and
|
|
207
|
-
// paste enough data to fill the kernel-level buffer. Once the sleep
|
|
208
|
-
// ends, the pty should accept the data again. Re-process after a
|
|
209
|
-
// short break.
|
|
210
|
-
this._writeTimeout = setTimeout(() => this._processWriteQueue(), 5);
|
|
211
|
-
return;
|
|
212
|
-
case -5: // EIO
|
|
213
|
-
case -32: // EPIPE
|
|
214
|
-
// Stop processing writes immediately as the pty is closed.
|
|
215
|
-
this._writeInProgress = false;
|
|
216
|
-
return;
|
|
217
|
-
default:
|
|
218
|
-
console.error('Unhandled pty write error', errno, err);
|
|
219
|
-
// Fall through as it's important to finish processing the queue
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Using `setImmediate` here appears to corrupt the data, this may be what
|
|
225
|
-
// the interleaving/dropped data comment is about in Node.js' tty module:
|
|
226
|
-
// https://github.com/nodejs/node/blob/4cac2b94bed4bf02810be054e8f63c0048c66564/lib/tty.js#L106C1-L111C34
|
|
227
|
-
//
|
|
228
|
-
// Yielding via `setImmediate` also doesn't seem to drain the buffer much
|
|
229
|
-
// anyway, so use a short timeout when this happens instead. Note that the `drain`
|
|
230
|
-
// event does not appear to happen on `net.Socket`/`tty.WriteStream` when
|
|
231
|
-
// writing to ptys.
|
|
232
|
-
this._writeTimeout = setTimeout(() => this._processWriteQueue(), 5);
|
|
233
|
-
});
|
|
170
|
+
this._writeStream.write(data);
|
|
234
171
|
}
|
|
235
172
|
|
|
236
173
|
/* Accessors */
|
|
@@ -306,8 +243,7 @@ export class UnixTerminal extends Terminal {
|
|
|
306
243
|
});
|
|
307
244
|
|
|
308
245
|
this._socket.destroy();
|
|
309
|
-
|
|
310
|
-
clearTimeout(this._writeTimeout);
|
|
246
|
+
this._writeStream.dispose();
|
|
311
247
|
}
|
|
312
248
|
|
|
313
249
|
public kill(signal?: string): void {
|
|
@@ -322,7 +258,7 @@ export class UnixTerminal extends Terminal {
|
|
|
322
258
|
public get process(): string {
|
|
323
259
|
if (process.platform === 'darwin') {
|
|
324
260
|
const title = pty.process(this._fd);
|
|
325
|
-
return (title !== 'kernel_task'
|
|
261
|
+
return (title !== 'kernel_task') ? title : this._file;
|
|
326
262
|
}
|
|
327
263
|
|
|
328
264
|
return pty.process(this._fd, this._pty) || this._file;
|
|
@@ -362,3 +298,91 @@ export class UnixTerminal extends Terminal {
|
|
|
362
298
|
delete env['LINES'];
|
|
363
299
|
}
|
|
364
300
|
}
|
|
301
|
+
|
|
302
|
+
interface IWriteTask {
|
|
303
|
+
/** The buffer being written. */
|
|
304
|
+
buffer: Buffer;
|
|
305
|
+
/** The current offset of not yet written data. */
|
|
306
|
+
offset: number;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* A custom write stream that writes directly to a file descriptor with proper
|
|
311
|
+
* handling of backpressure and errors. This avoids some event loop exhaustion
|
|
312
|
+
* issues that can occur when using the standard APIs in Node.
|
|
313
|
+
*/
|
|
314
|
+
class CustomWriteStream implements IDisposable {
|
|
315
|
+
|
|
316
|
+
private readonly _writeQueue: IWriteTask[] = [];
|
|
317
|
+
private _writeImmediate: NodeJS.Immediate | undefined;
|
|
318
|
+
|
|
319
|
+
constructor(
|
|
320
|
+
private readonly _fd: number,
|
|
321
|
+
private readonly _encoding: BufferEncoding
|
|
322
|
+
) {
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
dispose(): void {
|
|
326
|
+
clearImmediate(this._writeImmediate);
|
|
327
|
+
this._writeImmediate = undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
write(data: string | Buffer): void {
|
|
331
|
+
// Writes are put in a queue and processed asynchronously in order to handle
|
|
332
|
+
// backpressure from the kernel buffer.
|
|
333
|
+
const buffer = typeof data === 'string'
|
|
334
|
+
? Buffer.from(data, this._encoding)
|
|
335
|
+
: Buffer.from(data);
|
|
336
|
+
|
|
337
|
+
if (buffer.byteLength !== 0) {
|
|
338
|
+
this._writeQueue.push({ buffer, offset: 0 });
|
|
339
|
+
if (this._writeQueue.length === 1) {
|
|
340
|
+
this._processWriteQueue();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private _processWriteQueue(): void {
|
|
346
|
+
this._writeImmediate = undefined;
|
|
347
|
+
|
|
348
|
+
if (this._writeQueue.length === 0) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const task = this._writeQueue[0];
|
|
353
|
+
|
|
354
|
+
// Write to the underlying file descriptor and handle it directly, rather
|
|
355
|
+
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow and
|
|
356
|
+
// mask errors like EAGAIN and can cause the thread to block indefinitely.
|
|
357
|
+
fs.write(this._fd, task.buffer, task.offset, (err, written) => {
|
|
358
|
+
if (err) {
|
|
359
|
+
if ('code' in err && err.code === 'EAGAIN') {
|
|
360
|
+
// `setImmediate` is used to yield to the event loop and re-attempt
|
|
361
|
+
// the write later.
|
|
362
|
+
this._writeImmediate = setImmediate(() => this._processWriteQueue());
|
|
363
|
+
} else {
|
|
364
|
+
// Stop processing immediately on unexpected error and log
|
|
365
|
+
this._writeQueue.length = 0;
|
|
366
|
+
console.error('Unhandled pty write error', err);
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
task.offset += written;
|
|
372
|
+
if (task.offset >= task.buffer.byteLength) {
|
|
373
|
+
this._writeQueue.shift();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Since there is more room in the kernel buffer, we can continue to write
|
|
377
|
+
// until we hit EAGAIN or exhaust the queue.
|
|
378
|
+
//
|
|
379
|
+
// Note that old versions of bash, like v3.2 which ships in macOS, appears
|
|
380
|
+
// to have a bug in its readline implementation that causes data
|
|
381
|
+
// corruption when writes to the pty happens too quickly. Instead of
|
|
382
|
+
// trying to workaround that we just accept it so that large pastes are as
|
|
383
|
+
// fast as possible.
|
|
384
|
+
// Context: https://github.com/microsoft/node-pty/issues/833
|
|
385
|
+
this._processWriteQueue();
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|