~ubuntu-branches/ubuntu/saucy/nodejs/saucy

« back to all changes in this revision

Viewing changes to lib/timers.js

  • Committer: Package Import Robot
  • Author(s): Jérémy Lal
  • Date: 2013-08-14 00:16:46 UTC
  • mfrom: (7.1.40 sid)
  • Revision ID: package-import@ubuntu.com-20130814001646-bzlysfh8sd6mukbo
Tags: 0.10.15~dfsg1-4
* Update 2005 patch, adding a handful of tests that can fail on
  slow platforms.
* Add 1004 patch to fix test failures when writing NaN to buffer
  on mipsel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
var L = require('_linklist');
24
24
var assert = require('assert').ok;
25
25
 
 
26
// Timeout values > TIMEOUT_MAX are set to 1.
 
27
var TIMEOUT_MAX = 2147483647; // 2^31-1
 
28
 
26
29
var debug;
27
30
if (process.env.NODE_DEBUG && /timer/.test(process.env.NODE_DEBUG)) {
28
31
  debug = function() { require('util').error.apply(this, arguments); };
44
47
// value = list
45
48
var lists = {};
46
49
 
47
 
 
48
50
// the main function - creates lists on demand and the watchers associated
49
51
// with them.
50
52
function insert(item, msecs) {
51
 
  item._idleStart = new Date();
 
53
  item._idleStart = Date.now();
52
54
  item._idleTimeout = msecs;
53
55
 
54
56
  if (msecs < 0) return;
64
66
    L.init(list);
65
67
 
66
68
    lists[msecs] = list;
67
 
 
68
 
    list.ontimeout = function() {
69
 
      debug('timeout callback ' + msecs);
70
 
 
71
 
      var now = new Date();
72
 
      debug('now: ' + now);
73
 
 
74
 
      var first;
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);
80
 
          return;
81
 
        } else {
82
 
          L.remove(first);
83
 
          assert(first !== L.peek(list));
84
 
 
85
 
          if (!first._onTimeout) continue;
86
 
 
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.
91
 
          //
92
 
          // https://github.com/joyent/node/issues/2631
93
 
          try {
94
 
            first._onTimeout();
95
 
          } catch (e) {
96
 
            if (!process.listeners('uncaughtException').length) throw e;
97
 
            process.emit('uncaughtException', e);
98
 
          }
99
 
        }
100
 
      }
101
 
 
102
 
      debug(msecs + ' list empty');
103
 
      assert(L.isEmpty(list));
104
 
      list.close();
105
 
      delete lists[msecs];
106
 
    };
 
69
    list.msecs = msecs;
 
70
    list.ontimeout = listOnTimeout;
107
71
  }
108
72
 
109
73
  L.append(list, item);
110
74
  assert(!L.isEmpty(list)); // list is not empty
111
75
}
112
76
 
 
77
function listOnTimeout() {
 
78
  var msecs = this.msecs;
 
79
  var list = this;
 
80
 
 
81
  debug('timeout callback ' + msecs);
 
82
 
 
83
  var now = Date.now();
 
84
  debug('now: ' + now);
 
85
 
 
86
  var first;
 
87
  while (first = L.peek(list)) {
 
88
    var diff = now - first._idleStart;
 
89
    if (diff < msecs) {
 
90
      list.start(msecs - diff, 0);
 
91
      debug(msecs + ' list wait because diff is ' + diff);
 
92
      return;
 
93
    } else {
 
94
      L.remove(first);
 
95
      assert(first !== L.peek(list));
 
96
 
 
97
      if (!first._onTimeout) continue;
 
98
 
 
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.
 
102
      //
 
103
      // https://github.com/joyent/node/issues/2631
 
104
      var domain = first.domain;
 
105
      if (domain && domain._disposed) continue;
 
106
      try {
 
107
        if (domain)
 
108
          domain.enter();
 
109
        var threw = true;
 
110
        first._onTimeout();
 
111
        if (domain)
 
112
          domain.exit();
 
113
        threw = false;
 
114
      } finally {
 
115
        if (threw) {
 
116
          process.nextTick(function() {
 
117
            list.ontimeout();
 
118
          });
 
119
        }
 
120
      }
 
121
    }
 
122
  }
 
123
 
 
124
  debug(msecs + ' list empty');
 
125
  assert(L.isEmpty(list));
 
126
  list.close();
 
127
  delete lists[msecs];
 
128
}
 
129
 
113
130
 
114
131
var unenroll = exports.unenroll = function(item) {
115
132
  L.remove(item);
133
150
  // then we should unenroll it from that
134
151
  if (item._idleNext) unenroll(item);
135
152
 
 
153
  // Ensure that msecs fits into signed int32
 
154
  if (msecs > 0x7fffffff) {
 
155
    msecs = 0x7fffffff;
 
156
  }
 
157
 
136
158
  item._idleTimeout = msecs;
137
159
  L.init(item);
138
160
};
143
165
exports.active = function(item) {
144
166
  var msecs = item._idleTimeout;
145
167
  if (msecs >= 0) {
 
168
 
146
169
    var list = lists[msecs];
147
170
    if (!list || L.isEmpty(list)) {
148
171
      insert(item, msecs);
149
172
    } else {
150
 
      item._idleStart = new Date();
 
173
      item._idleStart = Date.now();
151
174
      L.append(list, item);
152
175
    }
153
176
  }
162
185
exports.setTimeout = function(callback, after) {
163
186
  var timer;
164
187
 
165
 
  if (after <= 0) {
166
 
    // Use the slow case for after == 0
167
 
    timer = new Timer();
168
 
    timer._callback = callback;
169
 
 
170
 
    if (arguments.length <= 2) {
171
 
      timer._onTimeout = function() {
172
 
        this._callback();
173
 
        this.close();
174
 
      }
175
 
    } else {
176
 
      var args = Array.prototype.slice.call(arguments, 2);
177
 
      timer._onTimeout = function() {
178
 
        this._callback.apply(timer, args);
179
 
        this.close();
180
 
      }
181
 
    }
182
 
 
183
 
    timer.ontimeout = timer._onTimeout;
184
 
    timer.start(0, 0);
 
188
  after *= 1; // coalesce to number or NaN
 
189
 
 
190
  if (!(after >= 1 && after <= TIMEOUT_MAX)) {
 
191
    after = 1; // schedule on next tick, follows browser behaviour
 
192
  }
 
193
 
 
194
  timer = new Timeout(after);
 
195
 
 
196
  if (arguments.length <= 2) {
 
197
    timer._onTimeout = callback;
185
198
  } else {
186
 
    timer = { _idleTimeout: after };
187
 
    timer._idlePrev = timer;
188
 
    timer._idleNext = timer;
189
 
 
190
 
    if (arguments.length <= 2) {
191
 
      timer._onTimeout = callback;
192
 
    } else {
193
 
      var args = Array.prototype.slice.call(arguments, 2);
194
 
      timer._onTimeout = function() {
195
 
        callback.apply(timer, args);
196
 
      }
 
199
    /*
 
200
     * Sometimes setTimeout is called with arguments, EG
 
201
     *
 
202
     *   setTimeout(callback, 2000, "hello", "world")
 
203
     *
 
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.
 
207
     */
 
208
    var args = Array.prototype.slice.call(arguments, 2);
 
209
    timer._onTimeout = function() {
 
210
      callback.apply(timer, args);
197
211
    }
198
 
 
199
 
    exports.active(timer);
200
212
  }
201
213
 
 
214
  if (process.domain) timer.domain = process.domain;
 
215
 
 
216
  exports.active(timer);
 
217
 
202
218
  return timer;
203
219
};
204
220
 
206
222
exports.clearTimeout = function(timer) {
207
223
  if (timer && (timer.ontimeout || timer._onTimeout)) {
208
224
    timer.ontimeout = timer._onTimeout = null;
209
 
    if (timer instanceof Timer) {
 
225
    if (timer instanceof Timeout) {
210
226
      timer.close(); // for after === 0
211
227
    } else {
212
228
      exports.unenroll(timer);
216
232
 
217
233
 
218
234
exports.setInterval = function(callback, repeat) {
219
 
  var timer = new Timer();
220
 
 
 
235
  repeat *= 1; // coalesce to number or NaN
 
236
 
 
237
  if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
 
238
    repeat = 1; // schedule on next tick, follows browser behaviour
 
239
  }
 
240
 
 
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;
 
245
 
 
246
  if (process.domain) timer.domain = process.domain;
 
247
  exports.active(timer);
 
248
 
 
249
  return timer;
 
250
 
 
251
  function wrapper() {
 
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.)
 
256
    if (this._handle) {
 
257
      this._handle.start(repeat, 0);
 
258
    } else {
 
259
      timer._idleTimeout = repeat;
 
260
      exports.active(timer);
 
261
    }
224
262
  }
225
 
 
226
 
  timer.start(repeat, repeat ? repeat : 1);
227
 
  return timer;
228
263
};
229
264
 
230
265
 
231
266
exports.clearInterval = function(timer) {
232
 
  if (timer instanceof Timer) {
233
 
    timer.ontimeout = null;
234
 
    timer.close();
235
 
  }
 
267
  if (timer && timer._repeat) {
 
268
    timer._repeat = false;
 
269
    clearTimeout(timer);
 
270
  }
 
271
};
 
272
 
 
273
 
 
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;
 
281
};
 
282
 
 
283
Timeout.prototype.unref = function() {
 
284
  if (!this._handle) {
 
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();
 
295
  } else {
 
296
    this._handle.unref();
 
297
  }
 
298
};
 
299
 
 
300
Timeout.prototype.ref = function() {
 
301
  if (this._handle)
 
302
    this._handle.ref();
 
303
};
 
304
 
 
305
Timeout.prototype.close = function() {
 
306
  this._onTimeout = null;
 
307
  if (this._handle) {
 
308
    this._handle.ontimeout = null;
 
309
    this._handle.close();
 
310
  } else {
 
311
    exports.unenroll(this);
 
312
  }
 
313
};
 
314
 
 
315
 
 
316
var immediateQueue = {};
 
317
L.init(immediateQueue);
 
318
 
 
319
 
 
320
function processImmediate() {
 
321
  var immediate = L.shift(immediateQueue);
 
322
 
 
323
  if (L.isEmpty(immediateQueue)) {
 
324
    process._needImmediateCallback = false;
 
325
  }
 
326
 
 
327
  if (immediate._onImmediate) {
 
328
    if (immediate.domain) immediate.domain.enter();
 
329
 
 
330
    immediate._onImmediate();
 
331
 
 
332
    if (immediate.domain) immediate.domain.exit();
 
333
  }
 
334
}
 
335
 
 
336
 
 
337
exports.setImmediate = function(callback) {
 
338
  var immediate = {}, args;
 
339
 
 
340
  L.init(immediate);
 
341
 
 
342
  immediate._onImmediate = callback;
 
343
 
 
344
  if (arguments.length > 1) {
 
345
    args = Array.prototype.slice.call(arguments, 1);
 
346
 
 
347
    immediate._onImmediate = function() {
 
348
      callback.apply(immediate, args);
 
349
    };
 
350
  }
 
351
 
 
352
  if (!process._needImmediateCallback) {
 
353
    process._needImmediateCallback = true;
 
354
    process._immediateCallback = processImmediate;
 
355
  }
 
356
 
 
357
  if (process.domain) immediate.domain = process.domain;
 
358
 
 
359
  L.append(immediateQueue, immediate);
 
360
 
 
361
  return immediate;
 
362
};
 
363
 
 
364
 
 
365
exports.clearImmediate = function(immediate) {
 
366
  if (!immediate) return;
 
367
 
 
368
  immediate._onImmediate = undefined;
 
369
 
 
370
  L.remove(immediate);
 
371
 
 
372
  if (L.isEmpty(immediateQueue)) {
 
373
    process._needImmediateCallback = false;
 
374
  }
 
375
};
 
376
 
 
377
 
 
378
// Internal APIs that need timeouts should use timers._unrefActive isntead of
 
379
// timers.active as internal timeouts shouldn't hold the loop open
 
380
 
 
381
var unrefList, unrefTimer;
 
382
 
 
383
 
 
384
function unrefTimeout() {
 
385
  var now = Date.now();
 
386
 
 
387
  debug('unrefTimer fired');
 
388
 
 
389
  var first;
 
390
  while (first = L.peek(unrefList)) {
 
391
    var diff = now - first._idleStart;
 
392
 
 
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');
 
398
      return;
 
399
    }
 
400
 
 
401
    L.remove(first);
 
402
 
 
403
    var domain = first.domain;
 
404
 
 
405
    if (!first._onTimeout) continue;
 
406
    if (domain && domain._disposed) continue;
 
407
 
 
408
    try {
 
409
      if (domain) domain.enter();
 
410
      var threw = true;
 
411
      debug('unreftimer firing timeout');
 
412
      first._onTimeout();
 
413
      threw = false;
 
414
      if (domain) domain.exit();
 
415
    } finally {
 
416
      if (threw) process.nextTick(unrefTimeout);
 
417
    }
 
418
  }
 
419
 
 
420
  debug('unrefList is empty');
 
421
  unrefTimer.when = -1;
 
422
}
 
423
 
 
424
 
 
425
exports._unrefActive = function(item) {
 
426
  var msecs = item._idleTimeout;
 
427
  if (!msecs || msecs < 0) return;
 
428
  assert(msecs >= 0);
 
429
 
 
430
  L.remove(item);
 
431
 
 
432
  if (!unrefList) {
 
433
    debug('unrefList initialized');
 
434
    unrefList = {};
 
435
    L.init(unrefList);
 
436
 
 
437
    debug('unrefTimer initialized');
 
438
    unrefTimer = new Timer();
 
439
    unrefTimer.unref();
 
440
    unrefTimer.when = -1;
 
441
    unrefTimer.ontimeout = unrefTimeout;
 
442
  }
 
443
 
 
444
  var now = Date.now();
 
445
  item._idleStart = now;
 
446
 
 
447
  if (L.isEmpty(unrefList)) {
 
448
    debug('unrefList empty');
 
449
    L.append(unrefList, item);
 
450
 
 
451
    unrefTimer.start(msecs, 0);
 
452
    unrefTimer.when = now + msecs;
 
453
    debug('unrefTimer scheduled');
 
454
    return;
 
455
  }
 
456
 
 
457
  var when = now + msecs;
 
458
 
 
459
  debug('unrefList find where we can insert');
 
460
 
 
461
  var cur, them;
 
462
 
 
463
  for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) {
 
464
    them = cur._idleStart + cur._idleTimeout;
 
465
 
 
466
    if (when < them) {
 
467
      debug('unrefList inserting into middle of list');
 
468
 
 
469
      L.append(cur, item);
 
470
 
 
471
      if (unrefTimer.when > when) {
 
472
        debug('unrefTimer is scheduled to fire too late, reschedule');
 
473
        unrefTimer.start(msecs, 0);
 
474
        unrefTimer.when = when;
 
475
      }
 
476
 
 
477
      return;
 
478
    }
 
479
  }
 
480
 
 
481
  debug('unrefList append to end');
 
482
  L.append(unrefList, item);
236
483
};