66
68
lists[msecs] = list;
68
list.ontimeout = function() {
69
debug('timeout callback ' + msecs);
75
while (first = L.peek(list)) {
76
var diff = now - first._idleStart;
77
if (diff + 1 < msecs) {
78
list.start(msecs - diff, 0);
79
debug(msecs + ' list wait because diff is ' + diff);
83
assert(first !== L.peek(list));
85
if (!first._onTimeout) continue;
87
// v0.4 compatibility: if the timer callback throws and the user's
88
// uncaughtException handler ignores the exception, other timers that
89
// expire on this tick should still run. If #2582 goes through, this
90
// hack should be removed.
92
// https://github.com/joyent/node/issues/2631
96
if (!process.listeners('uncaughtException').length) throw e;
97
process.emit('uncaughtException', e);
102
debug(msecs + ' list empty');
103
assert(L.isEmpty(list));
70
list.ontimeout = listOnTimeout;
109
73
L.append(list, item);
110
74
assert(!L.isEmpty(list)); // list is not empty
77
function listOnTimeout() {
78
var msecs = this.msecs;
81
debug('timeout callback ' + msecs);
87
while (first = L.peek(list)) {
88
var diff = now - first._idleStart;
90
list.start(msecs - diff, 0);
91
debug(msecs + ' list wait because diff is ' + diff);
95
assert(first !== L.peek(list));
97
if (!first._onTimeout) continue;
99
// v0.4 compatibility: if the timer callback throws and the
100
// domain or uncaughtException handler ignore the exception,
101
// other timers that expire on this tick should still run.
103
// https://github.com/joyent/node/issues/2631
104
var domain = first.domain;
105
if (domain && domain._disposed) continue;
116
process.nextTick(function() {
124
debug(msecs + ' list empty');
125
assert(L.isEmpty(list));
114
131
var unenroll = exports.unenroll = function(item) {
162
185
exports.setTimeout = function(callback, after) {
166
// Use the slow case for after == 0
168
timer._callback = callback;
170
if (arguments.length <= 2) {
171
timer._onTimeout = function() {
176
var args = Array.prototype.slice.call(arguments, 2);
177
timer._onTimeout = function() {
178
this._callback.apply(timer, args);
183
timer.ontimeout = timer._onTimeout;
188
after *= 1; // coalesce to number or NaN
190
if (!(after >= 1 && after <= TIMEOUT_MAX)) {
191
after = 1; // schedule on next tick, follows browser behaviour
194
timer = new Timeout(after);
196
if (arguments.length <= 2) {
197
timer._onTimeout = callback;
186
timer = { _idleTimeout: after };
187
timer._idlePrev = timer;
188
timer._idleNext = timer;
190
if (arguments.length <= 2) {
191
timer._onTimeout = callback;
193
var args = Array.prototype.slice.call(arguments, 2);
194
timer._onTimeout = function() {
195
callback.apply(timer, args);
200
* Sometimes setTimeout is called with arguments, EG
202
* setTimeout(callback, 2000, "hello", "world")
204
* If that's the case we need to call the callback with
205
* those args. The overhead of an extra closure is not
206
* desired in the normal case.
208
var args = Array.prototype.slice.call(arguments, 2);
209
timer._onTimeout = function() {
210
callback.apply(timer, args);
199
exports.active(timer);
214
if (process.domain) timer.domain = process.domain;
216
exports.active(timer);
218
234
exports.setInterval = function(callback, repeat) {
219
var timer = new Timer();
235
repeat *= 1; // coalesce to number or NaN
237
if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
238
repeat = 1; // schedule on next tick, follows browser behaviour
241
var timer = new Timeout(repeat);
221
242
var args = Array.prototype.slice.call(arguments, 2);
222
timer.ontimeout = function() {
223
callback.apply(timer, args);
243
timer._onTimeout = wrapper;
244
timer._repeat = true;
246
if (process.domain) timer.domain = process.domain;
247
exports.active(timer);
252
callback.apply(this, args);
253
// If callback called clearInterval().
254
if (timer._repeat === false) return;
255
// If timer is unref'd (or was - it's permanently removed from the list.)
257
this._handle.start(repeat, 0);
259
timer._idleTimeout = repeat;
260
exports.active(timer);
226
timer.start(repeat, repeat ? repeat : 1);
231
266
exports.clearInterval = function(timer) {
232
if (timer instanceof Timer) {
233
timer.ontimeout = null;
267
if (timer && timer._repeat) {
268
timer._repeat = false;
274
var Timeout = function(after) {
275
this._idleTimeout = after;
276
this._idlePrev = this;
277
this._idleNext = this;
278
this._idleStart = null;
279
this._onTimeout = null;
280
this._repeat = false;
283
Timeout.prototype.unref = function() {
285
var now = Date.now();
286
if (!this._idleStart) this._idleStart = now;
287
var delay = this._idleStart + this._idleTimeout - now;
288
if (delay < 0) delay = 0;
289
exports.unenroll(this);
290
this._handle = new Timer();
291
this._handle.ontimeout = this._onTimeout;
292
this._handle.start(delay, 0);
293
this._handle.domain = this.domain;
294
this._handle.unref();
296
this._handle.unref();
300
Timeout.prototype.ref = function() {
305
Timeout.prototype.close = function() {
306
this._onTimeout = null;
308
this._handle.ontimeout = null;
309
this._handle.close();
311
exports.unenroll(this);
316
var immediateQueue = {};
317
L.init(immediateQueue);
320
function processImmediate() {
321
var immediate = L.shift(immediateQueue);
323
if (L.isEmpty(immediateQueue)) {
324
process._needImmediateCallback = false;
327
if (immediate._onImmediate) {
328
if (immediate.domain) immediate.domain.enter();
330
immediate._onImmediate();
332
if (immediate.domain) immediate.domain.exit();
337
exports.setImmediate = function(callback) {
338
var immediate = {}, args;
342
immediate._onImmediate = callback;
344
if (arguments.length > 1) {
345
args = Array.prototype.slice.call(arguments, 1);
347
immediate._onImmediate = function() {
348
callback.apply(immediate, args);
352
if (!process._needImmediateCallback) {
353
process._needImmediateCallback = true;
354
process._immediateCallback = processImmediate;
357
if (process.domain) immediate.domain = process.domain;
359
L.append(immediateQueue, immediate);
365
exports.clearImmediate = function(immediate) {
366
if (!immediate) return;
368
immediate._onImmediate = undefined;
372
if (L.isEmpty(immediateQueue)) {
373
process._needImmediateCallback = false;
378
// Internal APIs that need timeouts should use timers._unrefActive isntead of
379
// timers.active as internal timeouts shouldn't hold the loop open
381
var unrefList, unrefTimer;
384
function unrefTimeout() {
385
var now = Date.now();
387
debug('unrefTimer fired');
390
while (first = L.peek(unrefList)) {
391
var diff = now - first._idleStart;
393
if (diff < first._idleTimeout) {
394
diff = first._idleTimeout - diff;
395
unrefTimer.start(diff, 0);
396
unrefTimer.when = now + diff;
397
debug('unrefTimer rescheudling for later');
403
var domain = first.domain;
405
if (!first._onTimeout) continue;
406
if (domain && domain._disposed) continue;
409
if (domain) domain.enter();
411
debug('unreftimer firing timeout');
414
if (domain) domain.exit();
416
if (threw) process.nextTick(unrefTimeout);
420
debug('unrefList is empty');
421
unrefTimer.when = -1;
425
exports._unrefActive = function(item) {
426
var msecs = item._idleTimeout;
427
if (!msecs || msecs < 0) return;
433
debug('unrefList initialized');
437
debug('unrefTimer initialized');
438
unrefTimer = new Timer();
440
unrefTimer.when = -1;
441
unrefTimer.ontimeout = unrefTimeout;
444
var now = Date.now();
445
item._idleStart = now;
447
if (L.isEmpty(unrefList)) {
448
debug('unrefList empty');
449
L.append(unrefList, item);
451
unrefTimer.start(msecs, 0);
452
unrefTimer.when = now + msecs;
453
debug('unrefTimer scheduled');
457
var when = now + msecs;
459
debug('unrefList find where we can insert');
463
for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) {
464
them = cur._idleStart + cur._idleTimeout;
467
debug('unrefList inserting into middle of list');
471
if (unrefTimer.when > when) {
472
debug('unrefTimer is scheduled to fire too late, reschedule');
473
unrefTimer.start(msecs, 0);
474
unrefTimer.when = when;
481
debug('unrefList append to end');
482
L.append(unrefList, item);