diff --git a/doc/api/timers.markdown b/doc/api/timers.markdown index a1e2d471a39..6bf14318e84 100644 --- a/doc/api/timers.markdown +++ b/doc/api/timers.markdown @@ -49,9 +49,10 @@ request the timer hold the program open. If the timer is already `ref`d calling ## 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. +To schedule the "immediate" execution of `callback` after I/O events +callbacks and before `setTimeout` and `setInterval` . 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 diff --git a/lib/timers.js b/lib/timers.js index 28c23c147ea..68890ab9f74 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -292,29 +292,21 @@ Timeout.prototype.close = function() { }; -var immediateTimer = null; -var immediateQueue = { started: false }; +var immediateQueue = {}; L.init(immediateQueue); -function lazyImmediateInit() { // what's in a name? - if (immediateTimer) return; - immediateTimer = new Timer; - immediateTimer.ontimeout = processImmediate; -} - - function processImmediate() { - var immediate; + var immediate = L.shift(immediateQueue); + if (L.isEmpty(immediateQueue)) { - immediateTimer.stop(); - immediateQueue.started = false; - } else { - immediate = L.shift(immediateQueue); + process._needImmediateCallback = false; + } + if (immediate._onImmediate) { if (immediate.domain) immediate.domain.enter(); - immediate._onTimeout(); + immediate._onImmediate(); if (immediate.domain) immediate.domain.exit(); } @@ -326,19 +318,19 @@ exports.setImmediate = function(callback) { L.init(immediate); - immediate._onTimeout = callback; + immediate._onImmediate = callback; if (arguments.length > 1) { args = Array.prototype.slice.call(arguments, 1); - immediate._onTimeout = function() { + + immediate._onImmediate = function() { callback.apply(null, args); }; } - if (!immediateQueue.started) { - lazyImmediateInit(); - immediateTimer.start(0, 1); - immediateQueue.started = true; + if (!process._needImmediateCallback) { + process._needImmediateCallback = true; + process._immediateCallback = processImmediate; } if (process.domain) immediate.domain = process.domain; @@ -352,12 +344,11 @@ exports.setImmediate = function(callback) { exports.clearImmediate = function(immediate) { if (!immediate) return; - immediate._onTimeout = undefined; + immediate._onImmediate = undefined; L.remove(immediate); if (L.isEmpty(immediateQueue)) { - immediateTimer.stop(); - immediateQueue.started = false; + process._needImmediateCallback = false; } }; diff --git a/src/node.cc b/src/node.cc index b88639f7061..3dc2508a01e 100644 --- a/src/node.cc +++ b/src/node.cc @@ -134,6 +134,11 @@ static uv_idle_t tick_spinner; static bool need_tick_cb; static Persistent tick_callback_sym; +static uv_check_t check_immediate_watcher; +static uv_idle_t idle_immediate_dummy; +static bool need_immediate_cb; +static Persistent immediate_callback_sym; + #ifdef OPENSSL_NPN_NEGOTIATED static bool use_npn = true; @@ -211,6 +216,27 @@ static Handle NeedTickCallback(const Arguments& args) { } +static void CheckImmediate(uv_check_t* handle, int status) { + assert(handle == &check_immediate_watcher); + assert(status == 0); + + HandleScope scope; + + if (immediate_callback_sym.IsEmpty()) { + immediate_callback_sym = NODE_PSYMBOL("_immediateCallback"); + } + + MakeCallback(process, immediate_callback_sym, 0, NULL); +} + + +static void IdleImmediateDummy(uv_idle_t* handle, int status) { + // Do nothing. Only for maintaining event loop + assert(handle == &idle_immediate_dummy); + assert(status == 0); +} + + static inline const char *errno_string(int errorno) { #define ERRNO_CASE(e) case e: return #e; switch (errorno) { @@ -2184,6 +2210,35 @@ static Handle DebugProcess(const Arguments& args); static Handle DebugPause(const Arguments& args); static Handle DebugEnd(const Arguments& args); + +Handle NeedImmediateCallbackGetter(Local property, + const AccessorInfo& info) { + return Boolean::New(need_immediate_cb); +} + + +static void NeedImmediateCallbackSetter(Local property, + Local value, + const AccessorInfo& info) { + HandleScope scope; + + bool bool_value = value->BooleanValue(); + + if (need_immediate_cb == bool_value) return; + + need_immediate_cb = bool_value; + + if (need_immediate_cb) { + uv_check_start(&check_immediate_watcher, node::CheckImmediate); + // idle handle is needed only to maintain event loop + uv_idle_start(&idle_immediate_dummy, node::IdleImmediateDummy); + } else { + uv_check_stop(&check_immediate_watcher); + uv_idle_stop(&idle_immediate_dummy); + } +} + + Handle SetupProcessObject(int argc, char *argv[]) { HandleScope scope; @@ -2277,6 +2332,9 @@ Handle SetupProcessObject(int argc, char *argv[]) { process->Set(String::NewSymbol("pid"), Integer::New(getpid(), node_isolate)); process->Set(String::NewSymbol("features"), GetFeatures()); + process->SetAccessor(String::New("_needImmediateCallback"), + NeedImmediateCallbackGetter, + NeedImmediateCallbackSetter); // -e, --eval if (eval_string) { @@ -2859,6 +2917,10 @@ char** Init(int argc, char *argv[]) { uv_idle_init(uv_default_loop(), &tick_spinner); + uv_check_init(uv_default_loop(), &check_immediate_watcher); + uv_unref((uv_handle_t*) &check_immediate_watcher); + uv_idle_init(uv_default_loop(), &idle_immediate_dummy); + V8::SetFatalErrorHandler(node::OnFatalError); // Fetch a reference to the main isolate, so we have a reference to it