timer: move setImmediate from timer to uv_check
authorShigeki Ohtsu <ohtsu@iij.ad.jp>
Wed, 6 Feb 2013 02:13:02 +0000 (11:13 +0900)
committerisaacs <i@izs.me>
Sat, 16 Feb 2013 00:11:05 +0000 (16:11 -0800)
uv_check is the robust place to invoke setImmediate callbacks after
process.nextTick and before timers(setTimeout/setInterval)

lib/timers.js
src/node.cc

index 28c23c147eac4f1a064c4b226468063e317f435c..68890ab9f7420e052b0f1b0c164d7b09849cf88f 100644 (file)
@@ -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;
   }
 };
index 21d778abceef898ee88b6fa5740691051ea4fd47..84af978f22382e687f7a1d87610fbaef3440b84f 100644 (file)
@@ -134,6 +134,11 @@ static uv_idle_t tick_spinner;
 static bool need_tick_cb;
 static Persistent<String> tick_callback_sym;
 
+static uv_check_t check_immediate_watcher;
+static uv_idle_t idle_immediate_dummy;
+static bool need_immediate_cb;
+static Persistent<String> immediate_callback_sym;
+
 
 #ifdef OPENSSL_NPN_NEGOTIATED
 static bool use_npn = true;
@@ -211,6 +216,27 @@ static Handle<Value> 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) {
@@ -2176,6 +2202,35 @@ static Handle<Value> DebugProcess(const Arguments& args);
 static Handle<Value> DebugPause(const Arguments& args);
 static Handle<Value> DebugEnd(const Arguments& args);
 
+
+Handle<Value> NeedImmediateCallbackGetter(Local<String> property,
+                                          const AccessorInfo& info) {
+  return Boolean::New(need_immediate_cb);
+}
+
+
+static void NeedImmediateCallbackSetter(Local<String> property,
+                                        Local<Value> 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<Object> SetupProcessObject(int argc, char *argv[]) {
   HandleScope scope;
 
@@ -2269,6 +2324,9 @@ Handle<Object> 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) {
@@ -2851,6 +2909,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