diff --git a/src/node.cc b/src/node.cc index 28b97ce95f..50e4db5088 100644 --- a/src/node.cc +++ b/src/node.cc @@ -71,6 +71,10 @@ static bool use_debug_agent = false; static bool debug_wait_connect = false; static int debug_port=5858; +static ev_prepare next_tick_watcher; +static ev_idle tick_spinner; +static bool need_tick_cb; +static Persistent tick_callback_sym; static ev_async eio_want_poll_notifier; static ev_async eio_done_poll_notifier; @@ -144,6 +148,7 @@ static void Activity(EV_P_ ev_check *watcher, int revents) { pending -= ev_is_pending(&gc_timer); pending -= ev_is_pending(&gc_idle); + pending -= ev_is_pending(&next_tick_watcher); //if (ev_is_pending(&gc_check)) pending--; // This probably never happens? //fprintf(stderr, "activity, pending: %d\n", pending); @@ -162,6 +167,52 @@ static void Activity(EV_P_ ev_check *watcher, int revents) { } +static Handle NeedTickCallback(const Arguments& args) { + HandleScope scope; + need_tick_cb = true; + ev_idle_start(EV_DEFAULT_UC_ &tick_spinner); + return Undefined(); +} + + +static void Spin(EV_P_ ev_idle *watcher, int revents) { + assert(watcher == &tick_spinner); + assert(revents == EV_IDLE); +} + + +static void Tick(EV_P_ ev_prepare *watcher, int revents) { + assert(watcher == &next_tick_watcher); + assert(revents == EV_PREPARE); + + // Avoid entering a V8 scope. + if (!need_tick_cb) return; + + need_tick_cb = false; + ev_idle_stop(EV_DEFAULT_UC_ &tick_spinner); + + HandleScope scope; + + if (tick_callback_sym.IsEmpty()) { + // Lazily set the symbol + tick_callback_sym = + Persistent::New(String::NewSymbol("_tickCallback")); + } + + Local cb_v = process->Get(tick_callback_sym); + if (!cb_v->IsFunction()) return; + Local cb = Local::Cast(cb_v); + + TryCatch try_catch; + + cb->Call(process, 0, NULL); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } +} + + static void DoPoll(EV_P_ ev_idle *watcher, int revents) { assert(watcher == &eio_poller); assert(revents == EV_IDLE); @@ -1356,6 +1407,7 @@ static void Load(int argc, char *argv[]) { NODE_SET_METHOD(process, "evalcx", EvalCX); NODE_SET_METHOD(process, "compile", Compile); NODE_SET_METHOD(process, "_byteLength", ByteLength); + NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback); NODE_SET_METHOD(process, "reallyExit", Exit); NODE_SET_METHOD(process, "chdir", Chdir); NODE_SET_METHOD(process, "cwd", Cwd); @@ -1378,7 +1430,6 @@ static void Load(int argc, char *argv[]) { EventEmitter::constructor_template->GetFunction()); - // Initialize the C++ modules..................filename of module IOWatcher::Initialize(process); // io_watcher.cc IdleWatcher::Initialize(process); // idle_watcher.cc @@ -1534,6 +1585,11 @@ int main(int argc, char *argv[]) { ev_default_loop(EVFLAG_AUTO); #endif + ev_prepare_init(&node::next_tick_watcher, node::Tick); + ev_prepare_start(EV_DEFAULT_UC_ &node::next_tick_watcher); + ev_unref(EV_DEFAULT_UC); + + ev_idle_init(&node::tick_spinner, node::Spin); ev_init(&node::gc_timer, node::CheckIdleness); node::gc_timer.repeat = GC_INTERVAL; diff --git a/src/node.js b/src/node.js index ee7a805a45..f09ef32b80 100644 --- a/src/node.js +++ b/src/node.js @@ -179,22 +179,18 @@ var events = eventsModule.exports; // nextTick() var nextTickQueue = []; -var nextTickWatcher = new process.IdleWatcher(); -// Only debugger has maximum priority. Below that is the nextTickWatcher. -nextTickWatcher.setPriority(process.EVMAXPRI-1); -nextTickWatcher.callback = function () { +process._tickCallback = function () { var l = nextTickQueue.length; while (l--) { var cb = nextTickQueue.shift(); cb(); } - if (nextTickQueue.length == 0) nextTickWatcher.stop(); }; process.nextTick = function (callback) { nextTickQueue.push(callback); - nextTickWatcher.start(); + process._needTickCallback(); }; diff --git a/test/simple/test-next-tick-ordering.js b/test/simple/test-next-tick-ordering.js new file mode 100644 index 0000000000..ee41f055e3 --- /dev/null +++ b/test/simple/test-next-tick-ordering.js @@ -0,0 +1,31 @@ +require('../common'); +var sys = require('sys'), i; + +var N = 30; +var done = []; + +function get_printer(timeout) { + return function () { + sys.puts("Running from setTimeout " + timeout); + done.push(timeout); + }; +} + +process.nextTick(function () { + sys.puts("Running from nextTick"); + done.push('nextTick'); +}) + +for (i = 0; i < N; i += 1) { + setTimeout(get_printer(i), i); +} + +sys.puts("Running from main."); + + +process.addListener('exit', function () { + assert.equal('nextTick', done[0]); + for (i = 0; i < N; i += 1) { + assert.equal(i, done[i+1]); + } +});