From bc47353bbe1836e4415cb08f1b6619839bff4d82 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 26 Oct 2010 12:52:31 -0700 Subject: [PATCH] Use the timer list for setTimeout --- benchmark/settimeout.js | 15 +++++++ lib/timers.js | 91 ++++++++++++++++++++++++++++++----------- 2 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 benchmark/settimeout.js diff --git a/benchmark/settimeout.js b/benchmark/settimeout.js new file mode 100644 index 0000000000..dd52dc90e9 --- /dev/null +++ b/benchmark/settimeout.js @@ -0,0 +1,15 @@ +console.log("wait..."); +var done = 0; +var N = 5000000; +var begin = new Date(); +for (var i = 0; i < N; i++) { + setTimeout(function () { + if (++done == N) { + var end = new Date(); + console.log("smaller is better"); + console.log("startup: %d", start - begin); + console.log("done: %d", end - start); + } + }, 1000); +} +var start = new Date(); diff --git a/lib/timers.js b/lib/timers.js index 6a82497007..b835ba5864 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -1,7 +1,7 @@ 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); @@ -48,9 +48,8 @@ function remove (item) { // remove a item from its list and place at the end. function append (list, item) { - remove(item); item._idleNext = list._idleNext; - item._idleNext._idlePrev = item; + list._idleNext._idlePrev = item; item._idlePrev = list; list._idleNext = item; } @@ -62,7 +61,7 @@ function insert (item, msecs) { item._idleStart = new Date(); item._idleTimeout = msecs; - if (!msecs) return; + if (msecs < 0) return; var list; @@ -90,23 +89,23 @@ function insert (item, msecs) { return; } else { remove(first); - assert(first != peek(list)); + assert(first !== peek(list)); if (first._onTimeout) first._onTimeout(); } } debug(msecs + ' list empty'); - assert(list._idleNext == list); // list is empty + assert(list._idleNext === list); // list is empty list.stop(); }; } - if (list._idleNext == list) { + if (list._idleNext === list) { // if empty (re)start the timer list.again(msecs); } append(list, item); - assert(list._idleNext != list); // list is not empty + assert(list._idleNext !== list); // list is not empty } @@ -141,7 +140,7 @@ exports.enroll = function (item, msecs) { // it will reset its timeout. exports.active = function (item) { var msecs = item._idleTimeout; - if (msecs) { + if (msecs >= 0) { var list = lists[msecs]; if (item._idleNext == item) { insert(item, msecs); @@ -159,41 +158,83 @@ exports.active = function (item) { }; +/* + * DOM-style timers + */ +exports.setTimeout = function (callback, after) { + var timer; -// Timers -function addTimerListener (callback) { - var timer = this; - // Special case the no param case to avoid the extra object creation. + if (after <= 0) { + // Use the slow case for after == 0 + timer = new Timer(); + timer.callback = callback; + } else { + timer = { _idleTimeout: after, _onTimeout: callback }; + timer._idlePrev = timer; + timer._idleNext = timer; + } + + /* + * Sometimes setTimeout is called with arguments, EG + * + * setTimeout(callback, 2000, "hello", "world") + * + * If that's the case we need to call the callback with + * those args. The overhead of an extra closure is not + * desired in the normal case. + */ if (arguments.length > 2) { var args = Array.prototype.slice.call(arguments, 2); - timer.callback = function () { callback.apply(timer, args); }; - } else { - timer.callback = callback; + var c = function () { + callback.apply(timer, args); + }; + + if (timer instanceof Timer) { + timer.callback = c; + } else { + timer._onTimeout = c; + } } -} + if (timer instanceof Timer) { + timer.start(0, 0); + } else { + exports.active(timer); + } -exports.setTimeout = function (callback, after) { - var timer = new Timer(); - addTimerListener.apply(timer, arguments); - timer.start(after, 0); return timer; }; + +exports.clearTimeout = function (timer) { + timer.callback = timer._onTimeout = null; + exports.unenroll(timer); + if (timer instanceof Timer) timer.stop(); // for after === 0 +}; + + exports.setInterval = function (callback, repeat) { var timer = new Timer(); - addTimerListener.apply(timer, arguments); + + if (arguments.length > 2) { + var args = Array.prototype.slice.call(arguments, 2); + timer.callback = function () { + callback.apply(timer, args); + }; + } else { + timer.callback = callback; + } + timer.start(repeat, repeat ? repeat : 1); return timer; }; -exports.clearTimeout = function (timer) { + +exports.clearInterval = function (timer) { if (timer instanceof Timer) { timer.callback = null; timer.stop(); } }; - -exports.clearInterval = exports.clearTimeout;