From ad60db8e4efe78f09c016cae3946d68b3b2cd801 Mon Sep 17 00:00:00 2001 From: Paul Loyd <pavelko95@gmail.com> Date: Tue, 13 Jan 2015 17:53:44 +0300 Subject: [PATCH 1/3] timers: fix `this` for unref'd timers --- lib/timers.js | 8 +++---- test/parallel/test-timers-this.js | 37 +++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/timers.js b/lib/timers.js index e3e64287509a30..c7bac4114bf339 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -198,7 +198,7 @@ exports.setTimeout = function(callback, after) { */ var args = Array.prototype.slice.call(arguments, 2); timer._onTimeout = function() { - callback.apply(timer, args); + callback.apply(this, args); }; } @@ -242,12 +242,12 @@ exports.setInterval = function(callback, repeat) { function wrapper() { callback.apply(this, args); // If callback called clearInterval(). - if (timer._repeat === false) return; + if (this._repeat === false) return; // If timer is unref'd (or was - it's permanently removed from the list.) if (this._handle) { this._handle.start(repeat, 0); } else { - timer._idleTimeout = repeat; + this._idleTimeout = repeat; exports.active(timer); } } @@ -281,7 +281,7 @@ Timeout.prototype.unref = function() { if (delay < 0) delay = 0; exports.unenroll(this); this._handle = new Timer(); - this._handle[kOnTimeout] = this._onTimeout; + this._handle[kOnTimeout] = this._onTimeout && this._onTimeout.bind(this); this._handle.start(delay, 0); this._handle.domain = this.domain; this._handle.unref(); diff --git a/test/parallel/test-timers-this.js b/test/parallel/test-timers-this.js index cacb3f247cc4b8..b79e55da8c3cde 100644 --- a/test/parallel/test-timers-this.js +++ b/test/parallel/test-timers-this.js @@ -1,7 +1,8 @@ var assert = require('assert'); -var immediateThis, intervalThis, timeoutThis, - immediateArgsThis, intervalArgsThis, timeoutArgsThis; +var immediateThis, intervalThis, timeoutThis, intervalUnrefThis, + timeoutUnrefThis, immediateArgsThis, intervalArgsThis, timeoutArgsThis, + intervalUnrefArgsThis, timeoutUnrefArgsThis; var immediateHandler = setImmediate(function () { immediateThis = this; @@ -11,6 +12,7 @@ var immediateArgsHandler = setImmediate(function () { immediateArgsThis = this; }, "args ..."); + var intervalHandler = setInterval(function () { clearInterval(intervalHandler); @@ -23,6 +25,21 @@ var intervalArgsHandler = setInterval(function () { intervalArgsThis = this; }, 0, "args ..."); +var intervalUnrefHandler = setInterval(function () { + clearInterval(intervalUnrefHandler); + + intervalUnrefThis = this; +}); +intervalUnrefHandler.unref(); + +var intervalUnrefArgsHandler = setInterval(function () { + clearInterval(intervalUnrefArgsHandler); + + intervalUnrefArgsThis = this; +}, 0, "args ..."); +intervalUnrefArgsHandler.unref(); + + var timeoutHandler = setTimeout(function () { timeoutThis = this; }); @@ -31,13 +48,29 @@ var timeoutArgsHandler = setTimeout(function () { timeoutArgsThis = this; }, 0, "args ..."); +var timeoutUnrefHandler = setTimeout(function () { + timeoutUnrefThis = this; +}); +timeoutUnrefHandler.unref(); + +var timeoutUnrefArgsHandler = setTimeout(function () { + timeoutUnrefArgsThis = this; +}, 0, "args ..."); +timeoutUnrefArgsHandler.unref(); + +setTimeout(function() {}, 5); + process.once('exit', function () { assert.strictEqual(immediateThis, immediateHandler); assert.strictEqual(immediateArgsThis, immediateArgsHandler); assert.strictEqual(intervalThis, intervalHandler); assert.strictEqual(intervalArgsThis, intervalArgsHandler); + assert.strictEqual(intervalUnrefThis, intervalUnrefHandler); + assert.strictEqual(intervalUnrefArgsThis, intervalUnrefArgsHandler); assert.strictEqual(timeoutThis, timeoutHandler); assert.strictEqual(timeoutArgsThis, timeoutArgsHandler); + assert.strictEqual(timeoutUnrefThis, timeoutUnrefHandler); + assert.strictEqual(timeoutUnrefArgsThis, timeoutUnrefArgsHandler); }); From fe805ef7afa063a1a9c4309def73357501402d4d Mon Sep 17 00:00:00 2001 From: Paul Loyd <pavelko95@gmail.com> Date: Tue, 13 Jan 2015 19:20:20 +0300 Subject: [PATCH 2/3] timers: updating `now` within `listOnTimeout` See joyent/node#8105. --- lib/timers.js | 8 ++++---- test/parallel/test-timers-busy.js | 27 +++++++++++++++++++++++++ test/parallel/test-timers-unref-call.js | 2 -- 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-timers-busy.js diff --git a/lib/timers.js b/lib/timers.js index c7bac4114bf339..31f3c00c0a44a7 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -56,12 +56,12 @@ function listOnTimeout() { debug('timeout callback %d', msecs); - var now = Timer.now(); - debug('now: %s', now); - - var diff, first, threw; + var now, diff, first, threw; while (first = L.peek(list)) { + now = Timer.now(); + debug('now: %s', now); diff = now - first._idleStart; + if (diff < msecs) { list.start(msecs - diff, 0); debug('%d list wait because diff is %d', msecs, diff); diff --git a/test/parallel/test-timers-busy.js b/test/parallel/test-timers-busy.js new file mode 100644 index 00000000000000..c4073a727588fb --- /dev/null +++ b/test/parallel/test-timers-busy.js @@ -0,0 +1,27 @@ +var common = require('../common'); +var assert = require('assert'); + +var DELAY = 50; +var WINDOW = 5; + +function nearly(a, b) { + if (Math.abs(a-b) > WINDOW) + assert.strictEqual(a, b); +} + +function work(ms) { + var start = Date.now(); + while (Date.now() - start < ms); +} + +function test() { + var start = Date.now(); + setTimeout(function() { + work(DELAY + 20); + setTimeout(function() { + nearly(DELAY*3+20, Date.now() - start); + }, DELAY); + }, DELAY); +} + +test(); diff --git a/test/parallel/test-timers-unref-call.js b/test/parallel/test-timers-unref-call.js index 4f7865b8457601..b6ba09eed0db1e 100644 --- a/test/parallel/test-timers-unref-call.js +++ b/test/parallel/test-timers-unref-call.js @@ -1,8 +1,6 @@ var common = require('../common'); var Timer = process.binding('timer_wrap').Timer; -Timer.now = function() { return ++Timer.now.ticks; }; -Timer.now.ticks = 0; var t = setInterval(function() {}, 1); var o = { _idleStart: 0, _idleTimeout: 1 }; From 1c9e35a1669ff48c34e04c681de2156aa107351d Mon Sep 17 00:00:00 2001 From: Paul Loyd <pavelko95@gmail.com> Date: Tue, 13 Jan 2015 19:36:57 +0300 Subject: [PATCH 3/3] timers: now `setInterval` complies with DOM See joyent/node#8066. --- lib/timers.js | 6 ++-- src/timer_wrap.cc | 7 ++++ test/parallel/test-timers-intervals.js | 50 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-timers-intervals.js diff --git a/lib/timers.js b/lib/timers.js index 31f3c00c0a44a7..8d75c8f921f11c 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -240,15 +240,15 @@ exports.setInterval = function(callback, repeat) { return timer; function wrapper() { - callback.apply(this, args); - // If callback called clearInterval(). - if (this._repeat === false) return; // If timer is unref'd (or was - it's permanently removed from the list.) if (this._handle) { this._handle.start(repeat, 0); + callback.apply(this, args); + Timer.updateTime(); } else { this._idleTimeout = repeat; exports.active(timer); + callback.apply(this, args); } } }; diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index d71213f2202984..b52dfaca44af7c 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -35,6 +35,7 @@ class TimerWrap : public HandleWrap { constructor->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), Integer::New(env->isolate(), kOnTimeout)); + env->SetTemplateMethod(constructor, "updateTime", UpdateTime); env->SetTemplateMethod(constructor, "now", Now); env->SetProtoMethod(constructor, "close", HandleWrap::Close); @@ -119,6 +120,12 @@ class TimerWrap : public HandleWrap { wrap->MakeCallback(kOnTimeout, 0, nullptr); } + static void UpdateTime(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + uv_update_time(env->event_loop()); + } + static void Now(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); uv_update_time(env->event_loop()); diff --git a/test/parallel/test-timers-intervals.js b/test/parallel/test-timers-intervals.js new file mode 100644 index 00000000000000..a52a91126da3f7 --- /dev/null +++ b/test/parallel/test-timers-intervals.js @@ -0,0 +1,50 @@ +var common = require('../common'); +var assert = require('assert'); + +var ALLOWABLE_ERROR = 5; // [ms] + +function nearly(a, b) { + if (Math.abs(a-b) > ALLOWABLE_ERROR) + assert.strictEqual(a, b); +} + +function test(period, busyTime, expectedDelta, isUnref, next) { + var res = []; + + var prev = Date.now(); + var interval = setInterval(function() { + var s = Date.now(); + while (Date.now() - s < busyTime); + var e = Date.now(); + + res.push(s-prev); + if (res.length === 5) { + clearInterval(interval); + done(); + } + + prev = e; + }, period); + + if (isUnref) { + interval.unref(); + var blocker = setTimeout(function() { + assert(false, 'Interval works too long'); + }, Math.max(busyTime, period) * 6); + } + + function done() { + if (blocker) clearTimeout(blocker); + nearly(period, res.shift()); + res.forEach(nearly.bind(null, expectedDelta)); + process.nextTick(next); + } +} + +test(50, 10, 40, false, function() { // ref, simple + test(50, 10, 40, true, function() { // unref, simple + test(50, 100, 0, false, function() { // ref, overlay + test(50, 100, 0, true, function() {}); // unref, overlay + }); + }); +});