Showing with 81 additions and 27 deletions.
  1. +4 −3 doc/api/timers.markdown
  2. +15 −24 lib/timers.js
  3. +62 −0 src/node.cc
@@ -49,9 +49,10 @@ request the timer hold the program open. If the timer is already `ref`d calling

## setImmediate(callback, [arg], [...])

To schedule the "immediate" execution of `callback`. Returns an `immediateId`
for possible use with `clearImmediate()`. Optionally you can also pass
arguments to the callback.
To schedule the "immediate" execution of `callback` after I/O events
callbacks and before `setTimeout` and `setInterval` . Returns an
`immediateId` for possible use with `clearImmediate()`. Optionally you
can also pass arguments to the callback.

Immediates are queued in the order created, and are popped off the queue once
per loop iteration. This is different from `process.nextTick` which will
@@ -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;
}
};
@@ -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