Skip to content

Commit

Permalink
timers: reuse timer in setTimeout().unref()
Browse files Browse the repository at this point in the history
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: #3407
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
  • Loading branch information
indutny authored and jasnell committed Oct 29, 2015
1 parent 7cad182 commit 8d78d68
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 7 deletions.
29 changes: 22 additions & 7 deletions lib/timers.js
Expand Up @@ -119,16 +119,27 @@ function listOnTimeoutNT(list) {
}


const unenroll = exports.unenroll = function(item) {
function reuse(item) {
L.remove(item);

var list = lists[item._idleTimeout];
// if empty then stop the watcher
debug('unenroll');
// if empty - reuse the watcher
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');
list.close();
delete lists[item._idleTimeout];
}
// if active is called later, then we want to make sure not to insert again
item._idleTimeout = -1;
Expand Down Expand Up @@ -312,12 +323,16 @@ Timeout.prototype.unref = function() {
if (!this._idleStart) this._idleStart = now;
var delay = this._idleStart + this._idleTimeout - now;
if (delay < 0) delay = 0;
exports.unenroll(this);

// 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[kOnTimeout] = unrefdHandle;
this._handle.start(delay, 0);
Expand Down
20 changes: 20 additions & 0 deletions 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);
});

0 comments on commit 8d78d68

Please sign in to comment.