Module-level EMFILE handling
authorJonas Pfenniger <jonas@stvs.ch>
Thu, 28 Oct 2010 10:02:44 +0000 (12:02 +0200)
committerRyan Dahl <ry@tinyclouds.org>
Thu, 28 Oct 2010 18:12:59 +0000 (11:12 -0700)
All net servers now share the same dummy socket. The ulimit warning is
throttled for all servers.

lib/net.js

index e409545..d7c747d 100644 (file)
@@ -958,8 +958,8 @@ function Server (/* [ options, ] listener */) {
   self.watcher.host = self;
   self.watcher.callback = function () {
     // Just in case we don't have a dummy fd.
-    if (!self._dummyFD) self._getDummyFD();
-
+    getDummyFD();
+    
     if (self._acceptTimer) {
       // Somehow the watcher got started again. Need to wait until
       // the timer finishes.
@@ -972,20 +972,10 @@ function Server (/* [ options, ] listener */) {
       } catch (e) {
         if (e.errno != EMFILE) throw e;
 
-        // Output a warning, but only at most every 5 seconds.
-        var now = new Date();
-        if (now - self._lastEMFILEWarning > 5000) {
-          console.error("(node) Hit max file limit. Increase 'ulimit -n'.");
-          self._lastEMFILEWarning = now;
-        }
-
-        var acceptCount = 0;
-
         // Gracefully reject pending clients by freeing up a file
         // descriptor.
-        if (self._dummyFD) {
-          close(self._dummyFD); // Free up an fd
-          self._dummyFD = null;
+        rescueEMFILE(function() {
+          var acceptCount = 0;
           // Accept and close the waiting clients one at a time.
           // Single threaded programming ftw.
           while (true) {
@@ -1006,9 +996,8 @@ function Server (/* [ options, ] listener */) {
               break;
             }
           }
-          // Reacquire the dummy fd
-          self._getDummyFD();
-        }
+        });
+        
         return;
       }
       if (!peerInfo) return;
@@ -1131,22 +1120,11 @@ Server.prototype._startWatcher = function () {
   this.emit("listening");
 };
 
-
-Server.prototype._getDummyFD = function () {
-  try {
-    this._dummyFD = socket("tcp");
-  } catch (e) {
-    this._dummyFD = null;
-  }
-};
-
-
 Server.prototype._doListen = function () {
   var self = this;
-
-  // Grab a dummy fd for EMFILE conditions.
-  self._getDummyFD();
-  self._lastEMFILEWarning = 0;
+  
+  // Ensure we have a dummy fd for EMFILE conditions.
+  getDummyFD();
 
   try {
     bind(self.fd, arguments[0], arguments[1]);
@@ -1189,11 +1167,6 @@ Server.prototype.close = function () {
   close(self.fd);
   self.fd = null;
 
-  if (this._dummyFD) {
-    close(this._dummyFD);
-    this._dummyFD = null;
-  }
-
   if (self.type === "unix") {
     fs.unlink(self.path, function () {
       self.emit("close");
@@ -1202,3 +1175,33 @@ Server.prototype.close = function () {
     self.emit("close");
   }
 };
+
+var dummyFD = null;
+var lastEMFILEWarning = 0;
+// Ensures to have at least on free file-descriptor free.
+// callback should only use 1 file descriptor and close it before end of call
+function rescueEMFILE(callback) {
+  // Output a warning, but only at most every 5 seconds.
+  var now = new Date();
+  if (now - lastEMFILEWarning > 5000) {
+    console.error("(node) Hit max file limit. Increase 'ulimit -n'.");
+    lastEMFILEWarning = now;
+  }
+  
+  if (dummyFD) {
+    close(dummyFD);
+    dummyFD = null;
+    callback();
+    getDummyFD();
+  }
+}
+
+function getDummyFD() {
+  if (!dummyFD) {
+    try {
+      dummyFD = socket("tcp");
+    } catch (e) {
+      dummyFD = null;
+    }
+  }
+}
\ No newline at end of file