Skip to content

Commit

Permalink
Buffer IPC sends to avoid crashes
Browse files Browse the repository at this point in the history
Workaround for nodejs/node#34797
  • Loading branch information
novemberborn committed Aug 16, 2020
1 parent 5fbe45a commit 286d344
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
9 changes: 5 additions & 4 deletions lib/fork.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const childProcess = require('child_process');
const path = require('path');
const fs = require('fs');
const Emittery = require('emittery');
const {controlFlow} = require('./ipc-flow-control');

if (fs.realpathSync(__filename) !== __filename) {
console.warn('WARNING: `npm link ava` and the `--preserve-symlink` flag are incompatible. We have detected that AVA is linked via `npm link`, and that you are using either an early version of Node 6, or the `--preserve-symlink` flag. This breaks AVA. You should upgrade to Node 6.2.0+, avoid the `--preserve-symlink` flag, or avoid using `npm link ava`.');
Expand Down Expand Up @@ -52,12 +53,12 @@ module.exports = (file, options, execArgv = process.execArgv) => {
emitStateChange({type: 'worker-stderr', chunk});
});

const bufferedSend = controlFlow(subprocess);

let forcedExit = false;
const send = evt => {
if (subprocess.connected && !finished && !forcedExit) {
subprocess.send({ava: evt}, () => {
// Disregard errors.
});
if (!finished && !forcedExit) {
bufferedSend({ava: evt});
}
};

Expand Down
40 changes: 40 additions & 0 deletions lib/ipc-flow-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Manage how quickly messages are delivered to the channel. In theory, we
// should be able to call `send()` until it returns `false` but this leads to
// crashes with advanced serialization, see
// <https://github.com/nodejs/node/issues/34797>.
//
// Even if that's fixed (and the Node.js versions with the fixes are the
// minimally supported versions) we need flow control based on `send()`'s return
// value.

function controlFlow(channel) {
let sending = false;

const buffer = [];
const deliverNext = () => {
if (!channel.connected) {
buffer.length = 0;
}

if (buffer.length === 0) {
sending = false;
return;
}

channel.send(buffer.shift(), deliverNext);
};

return message => {
if (!channel.connected) {
return;
}

buffer.push(message);
if (!sending) {
sending = true;
setImmediate(deliverNext);
}
};
}

exports.controlFlow = controlFlow;
6 changes: 3 additions & 3 deletions lib/worker/ipc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';
const Emittery = require('emittery');
const {controlFlow} = require('../ipc-flow-control');

const emitter = new Emittery();
process.on('message', message => {
Expand All @@ -25,10 +26,9 @@ process.on('message', message => {
exports.options = emitter.once('options');
exports.peerFailed = emitter.once('peerFailed');

const bufferedSend = controlFlow(process);
function send(evt) {
if (process.connected) {
process.send({ava: evt});
}
bufferedSend({ava: evt});
}

exports.send = send;
Expand Down

0 comments on commit 286d344

Please sign in to comment.