Browse Source

Change nextTick implementation for the better

Use a prepare and idle watcher to execute the nextTick callback more
quickly. Test provided by Matt Ranney.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
4e7e2f8724
  1. 58
      src/node.cc
  2. 8
      src/node.js
  3. 31
      test/simple/test-next-tick-ordering.js

58
src/node.cc

@ -71,6 +71,10 @@ static bool use_debug_agent = false;
static bool debug_wait_connect = false; static bool debug_wait_connect = false;
static int debug_port=5858; static int debug_port=5858;
static ev_prepare next_tick_watcher;
static ev_idle tick_spinner;
static bool need_tick_cb;
static Persistent<String> tick_callback_sym;
static ev_async eio_want_poll_notifier; static ev_async eio_want_poll_notifier;
static ev_async eio_done_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_timer);
pending -= ev_is_pending(&gc_idle); pending -= ev_is_pending(&gc_idle);
pending -= ev_is_pending(&next_tick_watcher);
//if (ev_is_pending(&gc_check)) pending--; // This probably never happens? //if (ev_is_pending(&gc_check)) pending--; // This probably never happens?
//fprintf(stderr, "activity, pending: %d\n", pending); //fprintf(stderr, "activity, pending: %d\n", pending);
@ -162,6 +167,52 @@ static void Activity(EV_P_ ev_check *watcher, int revents) {
} }
static Handle<Value> 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<String>::New(String::NewSymbol("_tickCallback"));
}
Local<Value> cb_v = process->Get(tick_callback_sym);
if (!cb_v->IsFunction()) return;
Local<Function> cb = Local<Function>::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) { static void DoPoll(EV_P_ ev_idle *watcher, int revents) {
assert(watcher == &eio_poller); assert(watcher == &eio_poller);
assert(revents == EV_IDLE); 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, "evalcx", EvalCX);
NODE_SET_METHOD(process, "compile", Compile); NODE_SET_METHOD(process, "compile", Compile);
NODE_SET_METHOD(process, "_byteLength", ByteLength); NODE_SET_METHOD(process, "_byteLength", ByteLength);
NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback);
NODE_SET_METHOD(process, "reallyExit", Exit); NODE_SET_METHOD(process, "reallyExit", Exit);
NODE_SET_METHOD(process, "chdir", Chdir); NODE_SET_METHOD(process, "chdir", Chdir);
NODE_SET_METHOD(process, "cwd", Cwd); NODE_SET_METHOD(process, "cwd", Cwd);
@ -1378,7 +1430,6 @@ static void Load(int argc, char *argv[]) {
EventEmitter::constructor_template->GetFunction()); EventEmitter::constructor_template->GetFunction());
// Initialize the C++ modules..................filename of module // Initialize the C++ modules..................filename of module
IOWatcher::Initialize(process); // io_watcher.cc IOWatcher::Initialize(process); // io_watcher.cc
IdleWatcher::Initialize(process); // idle_watcher.cc IdleWatcher::Initialize(process); // idle_watcher.cc
@ -1534,6 +1585,11 @@ int main(int argc, char *argv[]) {
ev_default_loop(EVFLAG_AUTO); ev_default_loop(EVFLAG_AUTO);
#endif #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); ev_init(&node::gc_timer, node::CheckIdleness);
node::gc_timer.repeat = GC_INTERVAL; node::gc_timer.repeat = GC_INTERVAL;

8
src/node.js

@ -179,22 +179,18 @@ var events = eventsModule.exports;
// nextTick() // nextTick()
var nextTickQueue = []; 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; var l = nextTickQueue.length;
while (l--) { while (l--) {
var cb = nextTickQueue.shift(); var cb = nextTickQueue.shift();
cb(); cb();
} }
if (nextTickQueue.length == 0) nextTickWatcher.stop();
}; };
process.nextTick = function (callback) { process.nextTick = function (callback) {
nextTickQueue.push(callback); nextTickQueue.push(callback);
nextTickWatcher.start(); process._needTickCallback();
}; };

31
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]);
}
});
Loading…
Cancel
Save