mirror of https://github.com/nodejs/node.git
libeio bugfix part 3
Finally (hopefully) fix the issue that Felix reported. It's only appearing
on macintosh (test/mjsunit/test-eio-race3.js)
The trick/hack is to call eio_poll() again before reentering the event loop.
Additionally this commit implements a more complex method of calling
eio_poll(), occasionally dropping to an ev_idle watcher.
See also:
3f39772834
http://lists.schmorp.de/pipermail/libev/2010q1/000855.html
http://groups.google.com/group/nodejs/browse_thread/thread/9f8db11c792a68bb/a89705f68971f53c
pull/22966/head
parent
3f39772834
commit
f80cc69c23
96
src/node.cc
96
src/node.cc
|
@ -60,6 +60,52 @@ static Persistent<String> emit_symbol;
|
||||||
static int dash_dash_index = 0;
|
static int dash_dash_index = 0;
|
||||||
static bool use_debug_agent = false;
|
static bool use_debug_agent = false;
|
||||||
|
|
||||||
|
|
||||||
|
static ev_async eio_want_poll_notifier;
|
||||||
|
static ev_async eio_done_poll_notifier;
|
||||||
|
static ev_idle eio_poller;
|
||||||
|
|
||||||
|
|
||||||
|
static void DoPoll(EV_P_ ev_idle *watcher, int revents) {
|
||||||
|
assert(watcher == &eio_poller);
|
||||||
|
assert(revents == EV_IDLE);
|
||||||
|
|
||||||
|
if (eio_poll() != -1) ev_idle_stop(EV_DEFAULT_UC_ watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called from the main thread.
|
||||||
|
static void WantPollNotifier(EV_P_ ev_async *watcher, int revents) {
|
||||||
|
assert(watcher == &eio_want_poll_notifier);
|
||||||
|
assert(revents == EV_ASYNC);
|
||||||
|
|
||||||
|
if (eio_poll() == -1) ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void DonePollNotifier(EV_P_ ev_async *watcher, int revents) {
|
||||||
|
assert(watcher == &eio_done_poll_notifier);
|
||||||
|
assert(revents == EV_ASYNC);
|
||||||
|
|
||||||
|
ev_idle_stop(EV_DEFAULT_UC_ &eio_poller);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EIOWantPoll() is called from the EIO thread pool each time an EIO
|
||||||
|
// request (that is, one of the node.fs.* functions) has completed.
|
||||||
|
static void EIOWantPoll(void) {
|
||||||
|
// Signal the main thread that eio_poll need to be processed.
|
||||||
|
ev_async_send(EV_DEFAULT_UC_ &eio_want_poll_notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void EIODonePoll(void) {
|
||||||
|
// Signal the main thread that we should stop calling eio_poll().
|
||||||
|
// from the idle watcher.
|
||||||
|
ev_async_send(EV_DEFAULT_UC_ &eio_done_poll_notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
|
enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
|
@ -336,6 +382,9 @@ static Handle<Value> ByteLength(const Arguments& args) {
|
||||||
|
|
||||||
static Handle<Value> Loop(const Arguments& args) {
|
static Handle<Value> Loop(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
|
if (eio_poll() == -1) ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
|
||||||
|
|
||||||
ev_loop(EV_DEFAULT_UC_ 0);
|
ev_loop(EV_DEFAULT_UC_ 0);
|
||||||
return Undefined();
|
return Undefined();
|
||||||
}
|
}
|
||||||
|
@ -713,27 +762,6 @@ void FatalException(TryCatch &try_catch) {
|
||||||
uncaught_exception_counter--;
|
uncaught_exception_counter--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ev_async eio_watcher;
|
|
||||||
|
|
||||||
// Called from the main thread.
|
|
||||||
static void EIOCallback(EV_P_ ev_async *watcher, int revents) {
|
|
||||||
assert(watcher == &eio_watcher);
|
|
||||||
assert(revents == EV_ASYNC);
|
|
||||||
// Give control to EIO to process responses. In nearly every case
|
|
||||||
// EIOPromise::After() (file.cc) is called once EIO receives the response.
|
|
||||||
if (-1 == eio_poll() && !ev_async_pending(&eio_watcher)) {
|
|
||||||
ev_async_send(EV_DEFAULT_UC_ &eio_watcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EIOWantPoll() is called from the EIO thread pool each time an EIO
|
|
||||||
// request (that is, one of the node.fs.* functions) has completed.
|
|
||||||
static void EIOWantPoll(void) {
|
|
||||||
// Signal the main thread that EIO callbacks need to be processed.
|
|
||||||
ev_async_send(EV_DEFAULT_UC_ &eio_watcher);
|
|
||||||
// EIOCallback() will be called from the main thread in the next event
|
|
||||||
// loop.
|
|
||||||
}
|
|
||||||
|
|
||||||
static ev_async debug_watcher;
|
static ev_async debug_watcher;
|
||||||
|
|
||||||
|
@ -943,21 +971,23 @@ int main(int argc, char *argv[]) {
|
||||||
// Initialize the default ev loop.
|
// Initialize the default ev loop.
|
||||||
ev_default_loop(EVFLAG_AUTO);
|
ev_default_loop(EVFLAG_AUTO);
|
||||||
|
|
||||||
// Start the EIO thread pool:
|
// Setup the EIO thread pool
|
||||||
// 1. Initialize the ev_async watcher which allows for notification from
|
{ // It requires 3, yes 3, watchers.
|
||||||
// the thread pool (in node::EIOWantPoll) to poll for updates (in
|
ev_idle_init(&node::eio_poller, node::DoPoll);
|
||||||
// node::EIOCallback).
|
|
||||||
ev_async_init(&node::eio_watcher, node::EIOCallback);
|
ev_async_init(&node::eio_want_poll_notifier, node::WantPollNotifier);
|
||||||
// 2. Actaully start the thread pool.
|
ev_async_start(EV_DEFAULT_UC_ &node::eio_want_poll_notifier);
|
||||||
eio_init(node::EIOWantPoll, NULL);
|
ev_unref(EV_DEFAULT_UC);
|
||||||
|
|
||||||
|
ev_async_init(&node::eio_done_poll_notifier, node::DonePollNotifier);
|
||||||
|
ev_async_start(EV_DEFAULT_UC_ &node::eio_done_poll_notifier);
|
||||||
|
ev_unref(EV_DEFAULT_UC);
|
||||||
|
|
||||||
|
eio_init(node::EIOWantPoll, node::EIODonePoll);
|
||||||
// Don't handle more than 10 reqs on each eio_poll(). This is to avoid
|
// Don't handle more than 10 reqs on each eio_poll(). This is to avoid
|
||||||
// race conditions. See test/mjsunit/test-eio-race.js
|
// race conditions. See test/mjsunit/test-eio-race.js
|
||||||
eio_set_max_poll_reqs(10);
|
eio_set_max_poll_reqs(10);
|
||||||
// 3. Start watcher.
|
}
|
||||||
ev_async_start(EV_DEFAULT_UC_ &node::eio_watcher);
|
|
||||||
// 4. Remove a reference to the async watcher. This means we'll drop out
|
|
||||||
// of the ev_loop even though eio_watcher is active.
|
|
||||||
ev_unref(EV_DEFAULT_UC);
|
|
||||||
|
|
||||||
V8::Initialize();
|
V8::Initialize();
|
||||||
HandleScope handle_scope;
|
HandleScope handle_scope;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
process.mixin(require("./common"));
|
||||||
|
|
||||||
|
puts('first stat ...');
|
||||||
|
|
||||||
|
posix.stat(__filename)
|
||||||
|
.addCallback( function(stats) {
|
||||||
|
puts('second stat ...');
|
||||||
|
posix.stat(__filename)
|
||||||
|
.timeout(1000)
|
||||||
|
.wait();
|
||||||
|
|
||||||
|
puts('test passed');
|
||||||
|
})
|
||||||
|
.addErrback(function() {
|
||||||
|
throw new Exception();
|
||||||
|
});
|
Loading…
Reference in New Issue