lib: fix cluster handle leak
authorRich Trott <rtrott@gmail.com>
Sat, 24 Oct 2015 18:51:10 +0000 (11:51 -0700)
committerJames M Snell <jasnell@gmail.com>
Thu, 29 Oct 2015 15:38:43 +0000 (08:38 -0700)
It is possible to cause a resource leak in SharedHandle. This commit
fixes the leak.

Fixes: https://github.com/nodejs/node/issues/2510
PR-URL: https://github.com/nodejs/node/pull/3510
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
lib/cluster.js
test/parallel/test-cluster-shared-leak.js [new file with mode: 0644]

index 602cc8d60b9200b0933d8d854c6a12602bfd354c..eef8bd25639dd22bc07c44d43157dcc31a44530f 100644 (file)
@@ -345,7 +345,10 @@ function masterInit() {
        * if it has disconnected, otherwise we might
        * still want to access it.
        */
-      if (!worker.isConnected()) removeWorker(worker);
+      if (!worker.isConnected()) {
+        removeHandlesForWorker(worker);
+        removeWorker(worker);
+      }
 
       worker.suicide = !!worker.suicide;
       worker.state = 'dead';
diff --git a/test/parallel/test-cluster-shared-leak.js b/test/parallel/test-cluster-shared-leak.js
new file mode 100644 (file)
index 0000000..a4de1d3
--- /dev/null
@@ -0,0 +1,53 @@
+// In Node 4.2.1 on operating systems other than Linux, this test triggers an
+// assertion in cluster.js. The assertion protects against memory leaks.
+// https://github.com/nodejs/node/pull/3510
+
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const net = require('net');
+const cluster = require('cluster');
+cluster.schedulingPolicy = cluster.SCHED_NONE;
+
+if (cluster.isMaster) {
+  var conn, worker1, worker2;
+
+  worker1 = cluster.fork();
+  worker1.on('message', common.mustCall(function() {
+    worker2 = cluster.fork();
+    conn = net.connect(common.PORT, common.mustCall(function() {
+      worker1.send('die');
+      worker2.send('die');
+    }));
+    conn.on('error', function(e) {
+      // ECONNRESET is OK
+      if (e.code !== 'ECONNRESET')
+        throw e;
+    });
+  }));
+
+  cluster.on('exit', function(worker, exitCode, signalCode) {
+    assert(worker === worker1 || worker === worker2);
+    assert.strictEqual(exitCode, 0);
+    assert.strictEqual(signalCode, null);
+    if (Object.keys(cluster.workers).length === 0)
+      conn.destroy();
+  });
+
+  return;
+}
+
+var server = net.createServer(function(c) {
+  c.end('bye');
+});
+
+server.listen(common.PORT, function() {
+  process.send('listening');
+});
+
+process.on('message', function(msg) {
+  if (msg !== 'die') return;
+  server.close(function() {
+    setImmediate(() => process.disconnect());
+  });
+});