timers: internal unref'd timer for api timeouts

When an internal api needs a timeout, they should use
timers._unrefActive since that won't hold the loop open. This solves
the problem where you might have unref'd the socket handle but the
timeout for the socket was still active.
This commit is contained in:
Timothy J Fontaine 2013-05-17 15:04:24 -07:00
parent 2cad7a69ce
commit f46ad012bc

View File

@ -373,3 +373,111 @@ exports.clearImmediate = function(immediate) {
process._needImmediateCallback = false;
}
};
// Internal APIs that need timeouts should use timers._unrefActive isntead of
// timers.active as internal timeouts shouldn't hold the loop open
var unrefList, unrefTimer;
function unrefTimeout() {
var now = Date.now();
debug('unrefTimer fired');
var first;
while (first = L.peek(unrefList)) {
var diff = now - first._idleStart;
if (diff < first._idleTimeout) {
diff = first._idleTimeout - diff;
unrefTimer.start(diff, 0);
unrefTimer.when = now + diff;
debug('unrefTimer rescheudling for later');
return;
}
L.remove(first);
var domain = first.domain;
if (!first._onTimeout) continue;
if (domain && domain._disposed) continue;
try {
if (domain) domain.enter();
var threw = true;
debug('unreftimer firing timeout');
first._onTimeout();
threw = false;
if (domain) domain.exit();
} finally {
if (threw) process.nextTick(unrefTimeout);
}
}
debug('unrefList is empty');
unrefTimer.when = -1;
}
exports._unrefActive = function(item) {
var msecs = item._idleTimeout;
if (!msecs || msecs < 0) return;
assert(msecs >= 0);
L.remove(item);
if (!unrefList) {
debug('unrefList initialized');
unrefList = {};
L.init(unrefList);
debug('unrefTimer initialized');
unrefTimer = new Timer();
unrefTimer.unref();
unrefTimer.when = -1;
unrefTimer.ontimeout = unrefTimeout;
}
var now = Date.now();
item._idleStart = now;
if (L.isEmpty(unrefList)) {
debug('unrefList empty');
L.append(unrefList, item);
unrefTimer.start(msecs, 0);
unrefTimer.when = now + msecs;
debug('unrefTimer scheduled');
return;
}
var when = now + msecs;
debug('unrefList find where we can insert');
var cur, them;
for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) {
them = cur._idleStart + cur._idleTimeout;
if (when < them) {
debug('unrefList inserting into middle of list');
L.append(cur, item);
if (unrefTimer.when > when) {
debug('unrefTimer is scheduled to fire too late, reschedule');
unrefTimer.start(msecs, 0);
unrefTimer.when = when;
}
return;
}
}
debug('unrefList append to end');
L.append(unrefList, item);
};