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:
parent
2cad7a69ce
commit
f46ad012bc
108
lib/timers.js
108
lib/timers.js
@ -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);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user