summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-10-26 11:56:32 -0700
committerRyan Dahl <ry@tinyclouds.org>2010-10-26 14:53:17 -0700
commit5cc29b80f23bdf3471c9c06e0be9d198ef41ea91 (patch)
treec0b9ca87e6088911f0555b2591279fac28a4ce20
parentdcc4fffe4dc7ccec3db2d1b55ab3a42a492a0c98 (diff)
downloadnode-new-5cc29b80f23bdf3471c9c06e0be9d198ef41ea91.tar.gz
Move idle timers into its own module
-rw-r--r--lib/net.js177
-rw-r--r--lib/timers.js159
2 files changed, 171 insertions, 165 deletions
diff --git a/lib/net.js b/lib/net.js
index 28f8660b85..f25012364d 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -24,7 +24,6 @@ var binding = process.binding('net');
var FreeList = require('freelist').FreeList;
var IOWatcher = process.binding('io_watcher').IOWatcher;
-var Timer = process.binding('timer').Timer;
var constants = process.binding('constants');
var assert = process.assert;
@@ -53,163 +52,6 @@ var EMFILE = constants.EMFILE;
var END_OF_FILE = 42;
var SecureContext, SecureStream; // lazy loaded
-// IDLE TIMEOUTS
-//
-// Because often many sockets will have the same idle timeout we will not
-// use one timeout watcher per socket. It is too much overhead. Instead
-// we'll use a single watcher for all sockets with the same timeout value
-// and a linked list. This technique is described in the libev manual:
-// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts
-
-
-var timeout = new (function () {
- // Object containing all lists, timers
- // key = time in milliseconds
- // value = list
- var lists = {};
-
- // show the most idle socket
- function peek (list) {
- if (list._idlePrev == list) return null;
- return list._idlePrev;
- }
-
-
- // remove the most idle socket from the list
- function shift (list) {
- var first = list._idlePrev;
- remove(first);
- return first;
- }
-
-
- // remove a socket from its list
- function remove (socket) {
- socket._idleNext._idlePrev = socket._idlePrev;
- socket._idlePrev._idleNext = socket._idleNext;
- }
-
-
- // remove a socket from its list and place at the end.
- function append (list, socket) {
- remove(socket);
- socket._idleNext = list._idleNext;
- socket._idleNext._idlePrev = socket;
- socket._idlePrev = list;
- list._idleNext = socket;
- }
-
-
- function normalize (msecs) {
- if (!msecs || msecs <= 0) return 0;
- // round up to one sec
- if (msecs < 1000) return 1000;
- // round down to nearest second.
- return msecs - (msecs % 1000);
- }
-
- // the main function - creates lists on demand and the watchers associated
- // with them.
- function insert (socket, msecs) {
- socket._idleStart = new Date();
- socket._idleTimeout = msecs;
-
- if (!msecs) return;
-
- var list;
-
- if (lists[msecs]) {
- list = lists[msecs];
- } else {
- list = new Timer();
- list._idleNext = list;
- list._idlePrev = list;
-
- lists[msecs] = list;
-
- list.callback = function () {
- debug('timeout callback ' + msecs);
- // TODO - don't stop and start the watcher all the time.
- // just set its repeat
- var now = new Date();
- debug("now: " + now);
- var first;
- while (first = peek(list)) {
- var diff = now - first._idleStart;
- if (diff < msecs) {
- list.again(msecs - diff);
- debug(msecs + ' list wait because diff is ' + diff);
- return;
- } else {
- remove(first);
- assert(first != peek(list));
- first.emit('timeout');
- }
- }
- debug(msecs + ' list empty');
- assert(list._idleNext == list); // list is empty
- list.stop();
- };
- }
-
- if (list._idleNext == list) {
- // if empty (re)start the timer
- list.again(msecs);
- }
-
- append(list, socket);
- assert(list._idleNext != list); // list is not empty
- }
-
-
- var unenroll = this.unenroll = function (socket) {
- if (socket._idleNext) {
- socket._idleNext._idlePrev = socket._idlePrev;
- socket._idlePrev._idleNext = socket._idleNext;
-
- var list = lists[socket._idleTimeout];
- // if empty then stop the watcher
- //debug('unenroll');
- if (list && list._idlePrev == list) {
- //debug('unenroll: list empty');
- list.stop();
- }
- }
- };
-
-
- // Does not start the time, just sets up the members needed.
- this.enroll = function (socket, msecs) {
- // if this socket was already in a list somewhere
- // then we should unenroll it from that
- if (socket._idleNext) unenroll(socket);
-
- socket._idleTimeout = msecs;
- socket._idleNext = socket;
- socket._idlePrev = socket;
- };
-
- // call this whenever the socket is active (not idle)
- // it will reset its timeout.
- this.active = function (socket) {
- var msecs = socket._idleTimeout;
- if (msecs) {
- var list = lists[msecs];
- if (socket._idleNext == socket) {
- insert(socket, msecs);
- } else {
- // inline append
- socket._idleStart = new Date();
- socket._idleNext._idlePrev = socket._idlePrev;
- socket._idlePrev._idleNext = socket._idleNext;
- socket._idleNext = list._idleNext;
- socket._idleNext._idlePrev = socket;
- socket._idlePrev = list;
- list._idleNext = socket;
- }
- }
- };
-})();
var ioWatchers = new FreeList("iowatcher", 100, function () {
return new IOWatcher();
@@ -480,7 +322,7 @@ function initStream (self) {
if (self.onend) self.onend();
} else if (bytesRead > 0) {
- timeout.active(self);
+ require('timers').active(self);
var start = pool.used;
var end = pool.used + bytesRead;
@@ -551,6 +393,11 @@ util.inherits(Stream, stream.Stream);
exports.Stream = Stream;
+Stream.prototype._onTimeout = function () {
+ this.emit('timeout');
+};
+
+
Stream.prototype.setSecure = function (credentials) {
// Do we have openssl crypto?
try {
@@ -774,7 +621,7 @@ Stream.prototype._writeOut = function (data, encoding, fd) {
debug('wrote ' + bytesWritten + ' to socket. [fd, off, len] = ' + JSON.stringify([this.fd, off, len]) + "\n");
- timeout.active(this);
+ require('timers').active(this);
if (bytesWritten == len) {
// awesome. sent to buffer.
@@ -909,7 +756,7 @@ Stream.prototype.connect = function () {
if (self.fd) throw new Error('Stream already opened');
if (!self._readWatcher) throw new Error('No readWatcher');
- timeout.active(socket);
+ require('timers').active(socket);
self._connecting = true; // set false in doConnect
self.writable = true;
@@ -957,10 +804,10 @@ Stream.prototype.setKeepAlive = function (enable, time) {
Stream.prototype.setTimeout = function (msecs) {
if (msecs > 0) {
- timeout.enroll(this, msecs);
- if (this.fd) { timeout.active(this); }
+ require('timers').enroll(this, msecs);
+ if (this.fd) { require('timers').active(this); }
} else if (msecs === 0) {
- timeout.unenroll(this);
+ require('timers').unenroll(this);
}
};
@@ -999,7 +846,7 @@ Stream.prototype.destroy = function (exception) {
this._readWatcher = null;
}
- timeout.unenroll(this);
+ require('timers').unenroll(this);
if (this.secure) {
this.secureStream.close();
diff --git a/lib/timers.js b/lib/timers.js
new file mode 100644
index 0000000000..21e3df85d8
--- /dev/null
+++ b/lib/timers.js
@@ -0,0 +1,159 @@
+var Timer = process.binding('timer').Timer;
+var assert = process.assert;
+
+/*
+ * To enable debug statements for the timers do NODE_DEBUG=8 ./node script.js
+ */
+var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
+function debug () {
+ if (debugLevel & 0x8) {
+ require('util').error.apply(this, arguments);
+ }
+}
+
+// IDLE TIMEOUTS
+//
+// Because often many sockets will have the same idle timeout we will not
+// use one timeout watcher per socket. It is too much overhead. Instead
+// we'll use a single watcher for all sockets with the same timeout value
+// and a linked list. This technique is described in the libev manual:
+// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts
+
+// Object containing all lists, timers
+// key = time in milliseconds
+// value = list
+var lists = {};
+
+// show the most idle socket
+function peek (list) {
+ if (list._idlePrev == list) return null;
+ return list._idlePrev;
+}
+
+
+// remove the most idle socket from the list
+function shift (list) {
+ var first = list._idlePrev;
+ remove(first);
+ return first;
+}
+
+
+// remove a socket from its list
+function remove (socket) {
+ socket._idleNext._idlePrev = socket._idlePrev;
+ socket._idlePrev._idleNext = socket._idleNext;
+}
+
+
+// remove a socket from its list and place at the end.
+function append (list, socket) {
+ remove(socket);
+ socket._idleNext = list._idleNext;
+ socket._idleNext._idlePrev = socket;
+ socket._idlePrev = list;
+ list._idleNext = socket;
+}
+
+
+// the main function - creates lists on demand and the watchers associated
+// with them.
+function insert (socket, msecs) {
+ socket._idleStart = new Date();
+ socket._idleTimeout = msecs;
+
+ if (!msecs) return;
+
+ var list;
+
+ if (lists[msecs]) {
+ list = lists[msecs];
+ } else {
+ list = new Timer();
+ list._idleNext = list;
+ list._idlePrev = list;
+
+ lists[msecs] = list;
+
+ list.callback = function () {
+ debug('timeout callback ' + msecs);
+ // TODO - don't stop and start the watcher all the time.
+ // just set its repeat
+ var now = new Date();
+ debug("now: " + now);
+ var first;
+ while (first = peek(list)) {
+ var diff = now - first._idleStart;
+ if (diff < msecs) {
+ list.again(msecs - diff);
+ debug(msecs + ' list wait because diff is ' + diff);
+ return;
+ } else {
+ remove(first);
+ assert(first != peek(list));
+ if (first._onTimeout) first._onTimeout();
+ }
+ }
+ debug(msecs + ' list empty');
+ assert(list._idleNext == list); // list is empty
+ list.stop();
+ };
+ }
+
+ if (list._idleNext == list) {
+ // if empty (re)start the timer
+ list.again(msecs);
+ }
+
+ append(list, socket);
+ assert(list._idleNext != list); // list is not empty
+}
+
+
+var unenroll = exports.unenroll = function (socket) {
+ if (socket._idleNext) {
+ socket._idleNext._idlePrev = socket._idlePrev;
+ socket._idlePrev._idleNext = socket._idleNext;
+
+ var list = lists[socket._idleTimeout];
+ // if empty then stop the watcher
+ //debug('unenroll');
+ if (list && list._idlePrev == list) {
+ //debug('unenroll: list empty');
+ list.stop();
+ }
+ }
+};
+
+
+// Does not start the time, just sets up the members needed.
+exports.enroll = function (socket, msecs) {
+ // if this socket was already in a list somewhere
+ // then we should unenroll it from that
+ if (socket._idleNext) unenroll(socket);
+
+ socket._idleTimeout = msecs;
+ socket._idleNext = socket;
+ socket._idlePrev = socket;
+};
+
+// call this whenever the socket is active (not idle)
+// it will reset its timeout.
+exports.active = function (socket) {
+ var msecs = socket._idleTimeout;
+ if (msecs) {
+ var list = lists[msecs];
+ if (socket._idleNext == socket) {
+ insert(socket, msecs);
+ } else {
+ // inline append
+ socket._idleStart = new Date();
+ socket._idleNext._idlePrev = socket._idlePrev;
+ socket._idlePrev._idleNext = socket._idleNext;
+ socket._idleNext = list._idleNext;
+ socket._idleNext._idlePrev = socket;
+ socket._idlePrev = list;
+ list._idleNext = socket;
+ }
+ }
+};