Change nextTick implementation for the better
Use a prepare and idle watcher to execute the nextTick callback more quickly. Test provided by Matt Ranney.
This commit is contained in:
parent
57edff1f31
commit
4e7e2f8724
58
src/node.cc
58
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<String> 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<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) {
|
||||
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;
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
31
test/simple/test-next-tick-ordering.js
Normal file
31
test/simple/test-next-tick-ordering.js
Normal file
@ -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…
x
Reference in New Issue
Block a user