From 649ad50c328851444998e3d2dea66ba5b48fe64b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 6 Jun 2012 05:12:28 +0200 Subject: [PATCH] unix: fix event loop stall Undoes most of the changes made to libev in 7d2ea31 and c9396dd. --- include/uv-private/ev.h | 2 +- src/unix/core.c | 39 +++++---------- src/unix/ev/ev.c | 105 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 36 deletions(-) diff --git a/include/uv-private/ev.h b/include/uv-private/ev.h index 7709bc34..11e81cda 100644 --- a/include/uv-private/ev.h +++ b/include/uv-private/ev.h @@ -623,7 +623,7 @@ enum { }; #if EV_PROTOTYPES -void ev_run (EV_P_ ev_tstamp waittime); +void ev_run (EV_P_ int flags EV_CPP (= 0)); void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)); /* break out of the loop */ /* diff --git a/src/unix/core.c b/src/unix/core.c index 37d03d3e..b33a6f74 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -226,16 +226,6 @@ void uv_loop_delete(uv_loop_t* loop) { } -static void uv__poll(uv_loop_t* loop, unsigned int timeout) { - /* bump the loop's refcount, otherwise libev does - * a zero timeout poll and we end up busy looping - */ - ev_ref(loop->ev); - ev_run(loop->ev, timeout / 1000.); - ev_unref(loop->ev); -} - - static unsigned int uv__poll_timeout(uv_loop_t* loop) { if (!uv__has_active_handles(loop)) return 0; @@ -247,22 +237,22 @@ static unsigned int uv__poll_timeout(uv_loop_t* loop) { } +static void uv__poll(uv_loop_t* loop) { + void ev__run(EV_P_ ev_tstamp waittime); + ev_invoke_pending(loop->ev); + ev__run(loop->ev, uv__poll_timeout(loop) / 1000.); + ev_invoke_pending(loop->ev); +} + + static int uv__run(uv_loop_t* loop) { uv_update_time(loop); uv__run_timers(loop); uv__run_idle(loop); - - if (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) { - uv__run_prepare(loop); - /* Need to poll even if there are no active handles left, otherwise - * uv_work_t reqs won't complete... - */ - uv__poll(loop, uv__poll_timeout(loop)); - uv__run_check(loop); - } - + uv__run_prepare(loop); + uv__poll(loop); + uv__run_check(loop); uv__run_closing_handles(loop); - return uv__has_active_handles(loop) || uv__has_active_reqs(loop); } @@ -604,13 +594,6 @@ static void uv__io_rw(struct ev_loop* ev, ev_io* w, int events) { uv__io_t* handle = container_of(w, uv__io_t, io_watcher); u.data = handle->io_watcher.data; u.cb(loop, handle, events & (EV_READ|EV_WRITE|EV_ERROR)); - - /* The callback may have closed all active handles. Stop libev from entering - * the epoll_wait/kevent/port_getn/etc. syscall if that's the case, it would - * hang indefinitely. - */ - if (loop->active_handles == 0) - ev_break(loop->ev, EVBREAK_ONE); } diff --git a/src/unix/ev/ev.c b/src/unix/ev/ev.c index a722981b..9888277b 100644 --- a/src/unix/ev/ev.c +++ b/src/unix/ev/ev.c @@ -2389,7 +2389,7 @@ time_update (EV_P_ ev_tstamp max_block) } void -ev_run (EV_P_ ev_tstamp waittime) +ev_run (EV_P_ int flags) { #if EV_FEATURE_API ++loop_depth; @@ -2426,6 +2426,15 @@ ev_run (EV_P_ ev_tstamp waittime) } #endif +#if EV_PREPARE_ENABLE + /* queue prepare watchers (and execute them) */ + if (expect_false (preparecnt)) + { + queue_events (EV_A_ (W *)prepares, preparecnt, EV_PREPARE); + EV_INVOKE_PENDING; + } +#endif + if (expect_false (loop_done)) break; @@ -2436,16 +2445,90 @@ ev_run (EV_P_ ev_tstamp waittime) /* update fd-related kernel structures */ fd_reify (EV_A); -#if EV_FEATURE_API - ++loop_count; + /* calculate blocking time */ + { + ev_tstamp waittime = 0.; + ev_tstamp sleeptime = 0.; + + /* remember old timestamp for io_blocktime calculation */ + ev_tstamp prev_mn_now = mn_now; + + /* update time to cancel out callback processing overhead */ + time_update (EV_A_ 1e100); + + if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt))) + { + waittime = MAX_BLOCKTIME; + + if (timercnt) + { + ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now + backend_fudge; + if (waittime > to) waittime = to; + } + +#if EV_PERIODIC_ENABLE + if (periodiccnt) + { + ev_tstamp to = ANHE_at (periodics [HEAP0]) - ev_rt_now + backend_fudge; + if (waittime > to) waittime = to; + } +#endif + + /* don't let timeouts decrease the waittime below timeout_blocktime */ + if (expect_false (waittime < timeout_blocktime)) + waittime = timeout_blocktime; + + /* extra check because io_blocktime is commonly 0 */ + if (expect_false (io_blocktime)) + { + sleeptime = io_blocktime - (mn_now - prev_mn_now); + + if (sleeptime > waittime - backend_fudge) + sleeptime = waittime - backend_fudge; + + if (expect_true (sleeptime > 0.)) + { + ev_sleep (sleeptime); + waittime -= sleeptime; + } + } + } + +#if EV_FEATURE_API + ++loop_count; +#endif + assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */ + backend_poll (EV_A_ waittime); + assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ + + /* update ev_rt_now, do magic */ + time_update (EV_A_ waittime + sleeptime); + } + + /* queue pending timers and reschedule them */ + timers_reify (EV_A); /* relative timers called last */ +#if EV_PERIODIC_ENABLE + periodics_reify (EV_A); /* absolute timers called first */ +#endif + +#if EV_IDLE_ENABLE + /* queue idle watchers unless other events are pending */ + idle_reify (EV_A); +#endif + +#if EV_CHECK_ENABLE + /* queue check watchers, to be executed first */ + if (expect_false (checkcnt)) + queue_events (EV_A_ (W *)checks, checkcnt, EV_CHECK); #endif - assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */ - backend_poll (EV_A_ waittime); - assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ EV_INVOKE_PENDING; } - while (0); + while (expect_true ( + activecnt + && !loop_done + && !(flags & (EVRUN_ONCE | EVRUN_NOWAIT)) + )); if (loop_done == EVBREAK_ONE) loop_done = EVBREAK_CANCEL; @@ -2455,6 +2538,14 @@ ev_run (EV_P_ ev_tstamp waittime) #endif } +/* libuv special */ +void +ev__run (EV_P_ ev_tstamp waittime) +{ + fd_reify (EV_A); + backend_poll (EV_A_ waittime); +} + void ev_break (EV_P_ int how) {