summaryrefslogtreecommitdiff
path: root/lib/cluster.js
diff options
context:
space:
mode:
authorSantiago Gimeno <santiago.gimeno@gmail.com>2015-12-18 19:13:50 +0100
committerRich Trott <rtrott@gmail.com>2016-01-13 20:15:02 -0800
commit9571be12f6e6ebdd097c8a032a872bada9972d56 (patch)
treebfaa7e7f2804c56cda9dab52a0f5db635cc38da4 /lib/cluster.js
parent265e2f557b4ed7f197b424a5f041a8a2a71b140e (diff)
downloadandroid-node-v8-9571be12f6e6ebdd097c8a032a872bada9972d56.tar.gz
android-node-v8-9571be12f6e6ebdd097c8a032a872bada9972d56.tar.bz2
android-node-v8-9571be12f6e6ebdd097c8a032a872bada9972d56.zip
cluster: fix race condition setting suicide prop
There is no guarantee that the `suicide` property of a worker in the master process is going to be set when the `disconnect` and `exit` events are emitted. To fix it, wait for the ACK of the suicide message from the master before disconnecting the worker. Also, there's no need to send the suicide message from the worker if the disconnection has been initiated in the master. Add `test-cluster-disconnect-suicide-race` that forks a lot of workers to consistently reproduce the issue this patch tries to solve. Modify `test-regress-GH-3238` so it checks both the `kill` and `disconnect` cases. Also take into account that the `disconnect` event may be received after the `exit` event. PR-URL: https://github.com/nodejs/node/pull/4349 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib/cluster.js')
-rw-r--r--lib/cluster.js48
1 files changed, 31 insertions, 17 deletions
diff --git a/lib/cluster.js b/lib/cluster.js
index 8356fad88a..c8bf658d5b 100644
--- a/lib/cluster.js
+++ b/lib/cluster.js
@@ -434,7 +434,7 @@ function masterInit() {
else if (message.act === 'listening')
listening(worker, message);
else if (message.act === 'suicide')
- worker.suicide = true;
+ suicide(worker, message);
else if (message.act === 'close')
close(worker, message);
}
@@ -445,6 +445,11 @@ function masterInit() {
cluster.emit('online', worker);
}
+ function suicide(worker, message) {
+ worker.suicide = true;
+ send(worker, { ack: message.seq });
+ }
+
function queryServer(worker, message) {
// Stop processing if worker already disconnecting
if (worker.suicide)
@@ -541,7 +546,7 @@ function workerInit() {
if (message.act === 'newconn')
onconnection(message, handle);
else if (message.act === 'disconnect')
- worker.disconnect();
+ _disconnect.call(worker, true);
}
};
@@ -662,14 +667,36 @@ function workerInit() {
}
Worker.prototype.disconnect = function() {
+ _disconnect.call(this);
+ };
+
+ Worker.prototype.destroy = function() {
+ this.suicide = true;
+ if (!this.isConnected()) process.exit(0);
+ var exit = process.exit.bind(null, 0);
+ send({ act: 'suicide' }, () => process.disconnect());
+ process.once('disconnect', exit);
+ };
+
+ function send(message, cb) {
+ sendHelper(process, message, null, cb);
+ }
+
+ function _disconnect(masterInitiated) {
this.suicide = true;
let waitingCount = 1;
function checkWaitingCount() {
waitingCount--;
if (waitingCount === 0) {
- send({ act: 'suicide' });
- process.disconnect();
+ // If disconnect is worker initiated, wait for ack to be sure suicide
+ // is properly set in the master, otherwise, if it's master initiated
+ // there's no need to send the suicide message
+ if (masterInitiated) {
+ process.disconnect();
+ } else {
+ send({ act: 'suicide' }, () => process.disconnect());
+ }
}
}
@@ -681,19 +708,6 @@ function workerInit() {
}
checkWaitingCount();
- };
-
- Worker.prototype.destroy = function() {
- this.suicide = true;
- if (!this.isConnected()) process.exit(0);
- var exit = process.exit.bind(null, 0);
- send({ act: 'suicide' }, exit);
- process.once('disconnect', exit);
- process.disconnect();
- };
-
- function send(message, cb) {
- sendHelper(process, message, null, cb);
}
}