Browse Source

timers: reuse timer in `setTimeout().unref()`

Instead of creating new timer - reuse the timer from the freelist. This
won't make the freelist timer active for the duration of `uv_close()`,
and will let the event-loop exit properly.

Fix: #1264
PR-URL: https://github.com/nodejs/node/pull/3407
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
process-exit-stdio-flushing
Fedor Indutny 9 years ago
parent
commit
3eecdf9f14
  1. 29
      lib/timers.js
  2. 20
      test/parallel/test-timers-unrefed-in-beforeexit.js

29
lib/timers.js

@ -119,16 +119,27 @@ function listOnTimeoutNT(list) {
} }
const unenroll = exports.unenroll = function(item) { function reuse(item) {
L.remove(item); L.remove(item);
var list = lists[item._idleTimeout]; var list = lists[item._idleTimeout];
// if empty then stop the watcher // if empty - reuse the watcher
debug('unenroll');
if (list && L.isEmpty(list)) { if (list && L.isEmpty(list)) {
debug('reuse hit');
list.stop();
delete lists[item._idleTimeout];
return list;
}
return null;
}
const unenroll = exports.unenroll = function(item) {
var list = reuse(item);
if (list) {
debug('unenroll: list empty'); debug('unenroll: list empty');
list.close(); list.close();
delete lists[item._idleTimeout];
} }
// if active is called later, then we want to make sure not to insert again // if active is called later, then we want to make sure not to insert again
item._idleTimeout = -1; item._idleTimeout = -1;
@ -312,12 +323,16 @@ Timeout.prototype.unref = function() {
if (!this._idleStart) this._idleStart = now; if (!this._idleStart) this._idleStart = now;
var delay = this._idleStart + this._idleTimeout - now; var delay = this._idleStart + this._idleTimeout - now;
if (delay < 0) delay = 0; if (delay < 0) delay = 0;
exports.unenroll(this);
// Prevent running cb again when unref() is called during the same cb // Prevent running cb again when unref() is called during the same cb
if (this._called && !this._repeat) return; if (this._called && !this._repeat) {
exports.unenroll(this);
return;
}
var handle = reuse(this);
this._handle = new Timer(); this._handle = handle || new Timer();
this._handle.owner = this; this._handle.owner = this;
this._handle[kOnTimeout] = unrefdHandle; this._handle[kOnTimeout] = unrefdHandle;
this._handle.start(delay, 0); this._handle.start(delay, 0);

20
test/parallel/test-timers-unrefed-in-beforeexit.js

@ -0,0 +1,20 @@
'use strict';
require('../common');
const assert = require('assert');
var once = 0;
process.on('beforeExit', () => {
if (once > 1)
throw new RangeError('beforeExit should only have been called once!');
setTimeout(() => {}, 1).unref();
once++;
});
process.on('exit', (code) => {
if (code !== 0) return;
assert.strictEqual(once, 1);
});
Loading…
Cancel
Save