node/test/parallel/test-child-process-fork-closed-channel-segfault.js
Gireesh Punathil 397eceb6d8 test: fix worker send error
In test-child-process-fork-closed-channel-segfault.js, race condition
is observed between the server getting closed and the worker sending
a message. Accommodate the potential errors.

Earlier, the same race was observed between the client and server
and was addressed through ignoring the relevant errors through error
handler. The same mechanism is re-used for worker too.

The only difference is that the filter is applied at the callback
instead of at the worker's error listener.

Refs: https://github.com/nodejs/node/issues/3635#issuecomment-157714683
Fixes: https://github.com/nodejs/node/issues/20836
PR-URL: https://github.com/nodejs/node/pull/20973

Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Jon Moss <me@jonathanmoss.me>
Reviewed-By: Matheus Marchini <matheus@sthima.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
2018-05-29 17:51:55 +05:30

89 lines
2.4 KiB
JavaScript

'use strict';
const common = require('../common');
// Before https://github.com/nodejs/node/pull/2847 a child process trying
// (asynchronously) to use the closed channel to it's creator caused a segfault.
const assert = require('assert');
const cluster = require('cluster');
const net = require('net');
if (!cluster.isMaster) {
// Exit on first received handle to leave the queue non-empty in master
process.on('message', function() {
process.exit(1);
});
return;
}
const server = net
.createServer(function(s) {
if (common.isWindows) {
s.on('error', function(err) {
// Prevent possible ECONNRESET errors from popping up
if (err.code !== 'ECONNRESET') throw err;
});
}
setTimeout(function() {
s.destroy();
}, 100);
})
.listen(0, function() {
const worker = cluster.fork();
worker.on('error', function(err) {
if (
err.code !== 'ECONNRESET' &&
err.code !== 'ECONNREFUSED' &&
err.code !== 'EMFILE'
) {
throw err;
}
});
function send(callback) {
const s = net.connect(server.address().port, function() {
worker.send({}, s, callback);
});
// https://github.com/nodejs/node/issues/3635#issuecomment-157714683
// ECONNREFUSED or ECONNRESET errors can happen if this connection is
// still establishing while the server has already closed.
// EMFILE can happen if the worker __and__ the server had already closed.
s.on('error', function(err) {
if (
err.code !== 'ECONNRESET' &&
err.code !== 'ECONNREFUSED' &&
err.code !== 'EMFILE'
) {
throw err;
}
});
}
worker.process.once(
'close',
common.mustCall(function() {
// Otherwise the crash on `channel.fd` access may happen
assert.strictEqual(worker.process.channel, null);
server.close();
})
);
worker.on('online', function() {
send(function(err) {
assert.ifError(err);
send(function(err) {
// Ignore errors when sending the second handle because the worker
// may already have exited.
if (err && err.code !== 'ERR_IPC_CHANNEL_CLOSED' &&
err.code !== 'ECONNRESET' &&
err.code !== 'ECONNREFUSED' &&
err.code !== 'EMFILE') {
throw err;
}
});
});
});
});