From 0ac2ef924f2564ab967be22c7b4ae18c20ff4fc2 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sat, 23 Oct 2010 11:59:32 -0700 Subject: [PATCH] Do not spin on aceept() with EMFILE When a server hit EMFILE it would continue to try to accept new connections from the queue. This patch introduces a timeout of one second where it will stop trying to accept new files. After the second is over it tries again. This is a rather serious bug that has been effecting many highly concurrent programs. It was introduced in 4593c0, version v0.2.0. TODO: A test for this situation. Currently I test it like this termA% cd projects/node termA% ulimit -n 256 termA% ./node benchmark/idle_server.js termB% cd projects/node termB% ./node benchmark/idle_clients.js And watch how the server process behaves. --- TODO | 1 + lib/net.js | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 9fc5c0b1baf..58def8d6b65 100644 --- a/TODO +++ b/TODO @@ -25,3 +25,4 @@ based on size but rather read until EOF into a chain of buffers, then concat them together. - process object should be defined in src/node.js not in c++ +- Test for EMFILE accept spin bug. diff --git a/lib/net.js b/lib/net.js index 97c88a8ef5c..3edce338c84 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1049,6 +1049,25 @@ function Server (listener) { self.connections = 0; + self.paused = false; + self.pauseTimeout = 1000; + + function pause () { + // We've hit the maximum file limit. What to do? + // Let's try again in 1 second. + self.watcher.stop(); + + // If we're already paused, don't do another timeout. + if (self.paused) return; + + setTimeout(function () { + self.paused = false; + // Make sure we haven't closed in the interim + if (typeof self.fd != 'number') return; + self.watcher.start(); + }, self.pauseTimeout); + } + self.watcher = new IOWatcher(); self.watcher.host = self; self.watcher.callback = function () { @@ -1056,7 +1075,10 @@ function Server (listener) { try { var peerInfo = accept(self.fd); } catch (e) { - if (e.errno == EMFILE) return; + if (e.errno == EMFILE) { + pause(); + return; + } throw e; } if (!peerInfo) return;