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 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;
|
||||||
|
@ -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
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