From 382f22f22959e686b10a5f42333c468e6654cb97 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Tue, 7 Aug 2012 22:12:01 -0400 Subject: [PATCH] timers: implement setImmediate --- doc/api/timers.markdown | 17 +++++++++ lib/timers.js | 71 ++++++++++++++++++++++++++++++++++++ src/node.js | 10 +++++ test/common.js | 2 + test/simple/test-timers-immediate.js | 53 +++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 test/simple/test-timers-immediate.js diff --git a/doc/api/timers.markdown b/doc/api/timers.markdown index c25ada1..a1e2d47 100644 --- a/doc/api/timers.markdown +++ b/doc/api/timers.markdown @@ -46,3 +46,20 @@ event loop performance -- use wisely. If you had previously `unref()`d a timer you can call `ref()` to explicitly request the timer hold the program open. If the timer is already `ref`d calling `ref` again will have no effect. + +## setImmediate(callback, [arg], [...]) + +To schedule the "immediate" execution of `callback`. Returns an `immediateId` +for possible use with `clearImmediate()`. Optionally you can also pass +arguments to the callback. + +Immediates are queued in the order created, and are popped off the queue once +per loop iteration. This is different from `process.nextTick` which will +execute `process.maxTickDepth` queued callbacks per iteration. `setImmediate` +will yield to the event loop after firing a queued callback to make sure I/O is +not being starved. While order is preserved for execution, other I/O events may +fire between any two scheduled immediate callbacks. + +## clearImmediate(immediateId) + +Stops an immediate from triggering. diff --git a/lib/timers.js b/lib/timers.js index 03db989..2d50cbd 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -277,3 +277,74 @@ Timeout.prototype.close = function() { exports.unenroll(this); } }; + + +var immediateTimer = null; +var immediateQueue = { started: false }; +L.init(immediateQueue); + + +function lazyImmediateInit() { // what's in a name? + if (immediateTimer) return; + immediateTimer = new Timer; + immediateTimer.ontimeout = processImmediate; +} + + +function processImmediate() { + var immediate; + if (L.isEmpty(immediateQueue)) { + immediateTimer.stop(); + immediateQueue.started = false; + } else { + immediate = L.shift(immediateQueue); + + if (immediate.domain) immediate.domain.enter(); + + immediate._onTimeout(); + + if (immediate.domain) immediate.domain.exit(); + } +}; + + +exports.setImmediate = function(callback) { + var immediate = {}, args; + + L.init(immediate); + + immediate._onTimeout = callback; + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments, 1); + immediate._onTimeout = function() { + callback.apply(null, args); + }; + } + + if (!immediateQueue.started) { + lazyImmediateInit(); + immediateTimer.start(0, 1); + immediateQueue.started = true; + } + + if (process.domain) immediate.domain = process.domain; + + L.append(immediateQueue, immediate); + + return immediate; +}; + + +exports.clearImmediate = function(immediate) { + if (!immediate) return; + + immediate._onTimeout = undefined; + + L.remove(immediate); + + if (L.isEmpty(immediateQueue)) { + immediateTimer.stop(); + immediateQueue.started = false; + } +}; diff --git a/src/node.js b/src/node.js index 848c39d..07db3f0 100644 --- a/src/node.js +++ b/src/node.js @@ -184,6 +184,16 @@ var t = NativeModule.require('timers'); return t.clearInterval.apply(this, arguments); }; + + global.setImmediate = function() { + var t = NativeModule.require('timers'); + return t.setImmediate.apply(this, arguments); + }; + + global.clearImmediate = function() { + var t = NativeModule.require('timers'); + return t.clearImmediate.apply(this, arguments); + }; }; startup.globalConsole = function() { diff --git a/test/common.js b/test/common.js index e0ac7a0..62dd0f4 100644 --- a/test/common.js +++ b/test/common.js @@ -81,8 +81,10 @@ process.on('exit', function() { if (!exports.globalCheck) return; var knownGlobals = [setTimeout, setInterval, + setImmediate, clearTimeout, clearInterval, + clearImmediate, console, Buffer, process, diff --git a/test/simple/test-timers-immediate.js b/test/simple/test-timers-immediate.js new file mode 100644 index 0000000..0bd8ae9 --- /dev/null +++ b/test/simple/test-timers-immediate.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var immediateA = false, + immediateB = false, + immediateC = [], + before; + +setImmediate(function() { + try { + immediateA = process.hrtime(before); + } catch(e) { + console.log('failed to get hrtime with offset'); + } + clearImmediate(immediateB); +}); + +before = process.hrtime(); + +immediateB = setImmediate(function() { + immediateB = true; +}); + +setImmediate(function(x, y, z) { + immediateC = [x, y, z]; +}, 1, 2, 3); + +process.on('exit', function() { + assert.ok(immediateA, 'Immediate should happen after normal execution'); + assert.notStrictEqual(immediateB, true, 'immediateB should not fire'); + assert.deepEqual(immediateC, [1, 2, 3], 'immediateC args should match'); +}); -- 2.7.4