From eca229a119e987e89a30c84b033cd48f4ceaa6df Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 7 Nov 2015 00:26:44 +0100 Subject: [PATCH] unix: delay signal handling until after normal i/o It was reported that some node.js tests fail on AIX because the exit event sometimes comes before the final stdio output of a child process. Work around that by deferring the signal watcher that is used for process management until after the dispatch of regular i/o watchers. Fixes: https://github.com/libuv/libuv/issues/610 --- src/unix/aix.c | 17 ++++++++++++++++- src/unix/kqueue.c | 18 +++++++++++++++++- src/unix/linux-core.c | 18 +++++++++++++++++- src/unix/sunos.c | 18 +++++++++++++++++- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/unix/aix.c b/src/unix/aix.c index 47c80b61694..27736590b0c 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -118,6 +118,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { uv__io_t* w; uint64_t base; uint64_t diff; + int have_signals; int nevents; int count; int nfds; @@ -225,6 +226,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { goto update_timeout; } + have_signals = 0; nevents = 0; assert(loop->watchers != NULL); @@ -255,13 +257,26 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } - w->cb(loop, w, pe->revents); + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) + have_signals = 1; + else + w->cb(loop, w, pe->revents); + nevents++; } + if (have_signals != 0) + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 588666d354b..fffd4626f17 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -78,6 +78,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { sigset_t set; uint64_t base; uint64_t diff; + int have_signals; int filter; int fflags; int count; @@ -192,6 +193,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { goto update_timeout; } + have_signals = 0; nevents = 0; assert(loop->watchers != NULL); @@ -265,12 +267,26 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (revents == 0) continue; - w->cb(loop, w, revents); + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) + have_signals = 1; + else + w->cb(loop, w, revents); + nevents++; } + + if (have_signals != 0) + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index cdb107e6df0..b48a1111701 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -188,6 +188,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { sigset_t sigset; uint64_t sigmask; uint64_t base; + int have_signals; int nevents; int count; int nfds; @@ -315,6 +316,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { goto update_timeout; } + have_signals = 0; nevents = 0; assert(loop->watchers != NULL); @@ -369,13 +371,27 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { pe->events |= w->pevents & (POLLIN | POLLOUT); if (pe->events != 0) { - w->cb(loop, w, pe->events); + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) + have_signals = 1; + else + w->cb(loop, w, pe->events); + nevents++; } } + + if (have_signals != 0) + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 7426a102c5b..e67be8fcf81 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -140,6 +140,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { unsigned int nfds; unsigned int i; int saved_errno; + int have_signals; int nevents; int count; int err; @@ -230,6 +231,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { return; } + have_signals = 0; nevents = 0; assert(loop->watchers != NULL); @@ -252,7 +254,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w == NULL) continue; - w->cb(loop, w, pe->portev_events); + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) + have_signals = 1; + else + w->cb(loop, w, pe->portev_events); + nevents++; if (w != loop->watchers[fd]) @@ -262,9 +271,16 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } + + if (have_signals != 0) + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */