mirror of
https://github.com/nodejs/node.git
synced 2025-05-08 09:31:52 +00:00

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>
89 lines
2.4 KiB
JavaScript
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;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|