From 951168f96c6656d4c7a05dfd63e001badd4dfdfe Mon Sep 17 00:00:00 2001 From: Roman Reiss Date: Sat, 21 Mar 2015 17:39:35 +0100 Subject: [PATCH] timers: assure setTimeout callback only runs once This fixes an edge case where running this.unref() during the callback caused the callback to get executed multiple times. --- lib/timers.js | 5 +++++ test/parallel/test-timers-unref.js | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/lib/timers.js b/lib/timers.js index 102a927a19c6a1..dfba03d97923e7 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -85,6 +85,7 @@ function listOnTimeout() { if (domain) domain.enter(); threw = true; + first._called = true; first._onTimeout(); if (domain) domain.exit(); @@ -305,6 +306,9 @@ Timeout.prototype.unref = function() { if (this._handle) { this._handle.unref(); } else if (typeof(this._onTimeout) === 'function') { + // Prevent running callback multiple times + // when unref() is called during the callback + if (this._called) return; var now = Timer.now(); if (!this._idleStart) this._idleStart = now; var delay = this._idleStart + this._idleTimeout - now; @@ -492,6 +496,7 @@ function unrefTimeout() { if (domain) domain.enter(); threw = true; debug('unreftimer firing timeout'); + first._called = true; first._onTimeout(); threw = false; if (domain) diff --git a/test/parallel/test-timers-unref.js b/test/parallel/test-timers-unref.js index 9434dbbd36ad78..23d4fc4cc5f953 100644 --- a/test/parallel/test-timers-unref.js +++ b/test/parallel/test-timers-unref.js @@ -5,6 +5,7 @@ var interval_fired = false, timeout_fired = false, unref_interval = false, unref_timer = false, + unref_callbacks = 0, interval, check_unref, checks = 0; var LONG_TIME = 10 * 1000; @@ -34,6 +35,11 @@ check_unref = setInterval(function() { checks += 1; }, 100); +setTimeout(function() { + unref_callbacks++; + this.unref(); +}, SHORT_TIME); + // Should not assert on args.Holder()->InternalFieldCount() > 0. See #4261. (function() { var t = setInterval(function() {}, 1); @@ -46,4 +52,5 @@ process.on('exit', function() { assert.strictEqual(timeout_fired, false, 'Timeout should not fire'); assert.strictEqual(unref_timer, true, 'An unrefd timeout should still fire'); assert.strictEqual(unref_interval, true, 'An unrefd interval should still fire'); + assert.strictEqual(unref_callbacks, 1, 'Callback should only run once'); });