mirror of https://github.com/lukechilds/node.git
Ryan Dahl
14 years ago
2 changed files with 171 additions and 165 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
|
}; |
Loading…
Reference in new issue