libevent's monotonic clock detection is currently broken on all platforms. The first attached patch fixes it. The second takes advantage of it to remove the 5-second idle timeout where possible. This is starting to become a big deal on Linux, where they've removed ticks from the kernel and are now using tools (PowerTOP) to remove unnecessary wakeups from userspace. This effort is greatly extending my battery life.
I've successfully run test/test.sh with these patches on OS X 10.4 (which does not have clock_gettime()) and Linux 2.6 (which does). So it's been tested with all event methods except devpoll (sorry, no Solaris machine handy at the moment) and rtsig (which won't compile with or without these patches). On Linux, I've verified with strace and PowerTOP that it's actually using CLOCK_MONOTONIC and infinite timeouts. Best regards, Scott -- Scott Lamb <http://www.slamb.org/>
>From bbf1036054ba1546682c6c130d4b77e2ee95b264 Mon Sep 17 00:00:00 2001 From: Scott Lamb <[EMAIL PROTECTED]> Date: Wed, 25 Jul 2007 12:06:17 -0700 Subject: [PATCH] use CLOCK_MONOTONIC where available * look for clock_gettime in librt, which is used on Linux * remove check for HAVE_CLOCK_MONOTONIC (there was no configure glue) * detect at runtime - useful if a libevent-based program is run on an older kernel than it is compiled on Signed-off-by: Scott Lamb <[EMAIL PROTECTED]> --- libevent/configure.in | 1 + libevent/event.c | 37 +++++++++++++++++++++++++------------ 2 files changed, 26 insertions(+), 12 deletions(-) mode change 100644 => 100755 libevent/autogen.sh diff --git a/libevent/autogen.sh b/libevent/autogen.sh old mode 100644 new mode 100755 diff --git a/libevent/configure.in b/libevent/configure.in index 507090b..1acfa84 100644 --- a/libevent/configure.in +++ b/libevent/configure.in @@ -37,6 +37,7 @@ AC_ARG_WITH(rtsig, dnl Checks for libraries. AC_CHECK_LIB(socket, socket) AC_CHECK_LIB(resolv, inet_aton) +AC_CHECK_LIB(rt, clock_gettime) dnl Checks for header files. AC_HEADER_STDC diff --git a/libevent/event.c b/libevent/event.c index 2048157..fc54d23 100644 --- a/libevent/event.c +++ b/libevent/event.c @@ -51,6 +51,7 @@ #include <signal.h> #include <string.h> #include <assert.h> +#include <time.h> #include "event.h" #include "event-internal.h" @@ -113,6 +114,7 @@ const struct eventop *eventops[] = { /* Global state */ struct event_base *current_base = NULL; extern struct event_base *evsignal_base; +static int use_monotonic; /* Handle signals - This is a deprecated interface */ int (*event_sigcb)(void); /* Signal callback when gotsig is set */ @@ -143,25 +145,34 @@ compare(struct event *a, struct event *b) return (0); } +static void +detect_monotonic(void) +{ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + use_monotonic = 1; +#endif +} + static int gettime(struct timeval *tp) { -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; -#ifdef HAVE_CLOCK_MONOTONIC - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) -#else - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) -#endif - return (-1); - tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; -#else - gettimeofday(tp, NULL); + if (use_monotonic) { + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + return (-1); + + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; + return (0); + } #endif - return (0); + return (gettimeofday(tp, NULL)); } RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare); @@ -180,6 +191,8 @@ event_init(void) event_sigcb = NULL; event_gotsig = 0; + + detect_monotonic(); gettime(&base->event_tv); RB_INIT(&base->timetree); -- 1.5.3.GIT
>From d7f9e3b60c9bf5b1a9daf9cc9ec027d16c944816 Mon Sep 17 00:00:00 2001 From: Scott Lamb <[EMAIL PROTECTED]> Date: Wed, 25 Jul 2007 11:40:16 -0700 Subject: [PATCH] remove unnecessary wakeups on idle libevent wakes up every 5 seconds to check if the clock has gone backwards. On platforms with CLOCK_MONOTONIC, this is unnecessary. With the new Linux effort to reduce wakeups for laptops (e.g., PowerTOP) and libevent starting to show up in standard system tools (e.g., rpc.idmapd), it's better to avoid this where possible. Signed-off-by: Scott Lamb <[EMAIL PROTECTED]> --- libevent/devpoll.c | 5 +++-- libevent/epoll.c | 6 ++++-- libevent/event.c | 51 +++++++++++++++++++++++++++++++-------------------- libevent/kqueue.c | 9 ++++++--- libevent/poll.c | 9 ++++++--- libevent/rtsig.c | 36 ++++++++++++++++++++++++------------ 6 files changed, 74 insertions(+), 42 deletions(-) diff --git a/libevent/devpoll.c b/libevent/devpoll.c index 96582ab..d1327e5 100644 --- a/libevent/devpoll.c +++ b/libevent/devpoll.c @@ -218,12 +218,13 @@ devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) struct pollfd *events = devpollop->events; struct dvpoll dvp; struct evdevpoll *evdp; - int i, res, timeout; + int i, res, timeout = -1; if (devpollop->nchanges) devpoll_commit(devpollop); - timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + if (tv != NULL) + timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; dvp.dp_fds = devpollop->events; dvp.dp_nfds = devpollop->nevents; diff --git a/libevent/epoll.c b/libevent/epoll.c index 235977d..2be06d6 100644 --- a/libevent/epoll.c +++ b/libevent/epoll.c @@ -187,9 +187,11 @@ epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) struct epollop *epollop = arg; struct epoll_event *events = epollop->events; struct evepoll *evep; - int i, res, timeout; + int i, res, timeout = -1; + + if (tv != NULL) + timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; - timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); if (res == -1) { diff --git a/libevent/event.c b/libevent/event.c index fc54d23..e35bf74 100644 --- a/libevent/event.c +++ b/libevent/event.c @@ -127,7 +127,8 @@ static int event_haveevents(struct event_base *); static void event_process_active(struct event_base *); -static int timeout_next(struct event_base *, struct timeval *); +static int timeout_next(struct event_base *, struct timeval *, + struct timeval **); static void timeout_process(struct event_base *); static void timeout_correct(struct event_base *, struct timeval *); @@ -387,6 +388,7 @@ event_base_loop(struct event_base *base, int flags) const struct eventop *evsel = base->evsel; void *evbase = base->evbase; struct timeval tv; + struct timeval *tv_p; int res, done; if(!TAILQ_EMPTY(&base->sig.signalqueue)) @@ -415,19 +417,11 @@ event_base_loop(struct event_base *base, int flags) } } - /* Check if time is running backwards */ - gettime(&tv); - if (timercmp(&tv, &base->event_tv, <)) { - struct timeval off; - event_debug(("%s: time is running backwards, corrected", - __func__)); - timersub(&base->event_tv, &tv, &off); - timeout_correct(base, &off); - } - base->event_tv = tv; + timeout_correct(base, &tv); + tv_p = &tv; if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) - timeout_next(base, &tv); + timeout_next(base, &tv, &tv_p); else timerclear(&tv); @@ -437,7 +431,7 @@ event_base_loop(struct event_base *base, int flags) return (1); } - res = evsel->dispatch(base, evbase, &tv); + res = evsel->dispatch(base, evbase, tv_p); if (res == -1) @@ -738,16 +732,19 @@ event_active(struct event *ev, int res, short ncalls) event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE); } -int -timeout_next(struct event_base *base, struct timeval *tv) +static int +timeout_next(struct event_base *base, struct timeval *tv, + struct timeval **tv_p) { - struct timeval dflt = TIMEOUT_DEFAULT; - struct timeval now; struct event *ev; + struct timeval dflt = TIMEOUT_DEFAULT; if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) { - *tv = dflt; + if (use_monotonic) + *tv_p = NULL; + else + *tv = dflt; return (0); } @@ -769,16 +766,30 @@ timeout_next(struct event_base *base, struct timeval *tv) } static void -timeout_correct(struct event_base *base, struct timeval *off) +timeout_correct(struct event_base *base, struct timeval *tv) { struct event *ev; + struct timeval off; + + if (use_monotonic) + return; + + /* Check if time is running backwards */ + gettime(tv); + if (timercmp(tv, &base->event_tv, >=)) { + return; + } + + event_debug(("%s: time is running backwards, corrected", + __func__)); + timersub(&base->event_tv, tv, &off); /* * We can modify the key element of the node without destroying * the key, beause we apply it to all in the right order. */ RB_FOREACH(ev, event_tree, &base->timetree) - timersub(&ev->ev_timeout, off, &ev->ev_timeout); + timersub(&ev->ev_timeout, &off, &ev->ev_timeout); } void diff --git a/libevent/kqueue.c b/libevent/kqueue.c index af2e0f1..c547806 100644 --- a/libevent/kqueue.c +++ b/libevent/kqueue.c @@ -212,13 +212,16 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) struct kevent *changes = kqop->changes; struct kevent *events = kqop->events; struct event *ev; - struct timespec ts; + struct timespec ts, *ts_p = NULL; int i, res; - TIMEVAL_TO_TIMESPEC(tv, &ts); + if (tv != NULL) { + TIMEVAL_TO_TIMESPEC(tv, &ts); + ts_p = &ts; + } res = kevent(kqop->kq, changes, kqop->nchanges, - events, kqop->nevents, &ts); + events, kqop->nevents, ts_p); kqop->nchanges = 0; if (res == -1) { if (errno != EINTR) { diff --git a/libevent/poll.c b/libevent/poll.c index 8488598..123d36a 100644 --- a/libevent/poll.c +++ b/libevent/poll.c @@ -148,13 +148,16 @@ poll_check_ok(struct pollop *pop) int poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) { - int res, i, sec, nfds; + int res, i, msec = -1, nfds; struct pollop *pop = arg; poll_check_ok(pop); - sec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + + if (tv != NULL) + msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + nfds = pop->nfds; - res = poll(pop->event_set, nfds, sec); + res = poll(pop->event_set, nfds, msec); if (res == -1) { if (errno != EINTR) { diff --git a/libevent/rtsig.c b/libevent/rtsig.c index 3267af9..e0e55af 100644 --- a/libevent/rtsig.c +++ b/libevent/rtsig.c @@ -750,7 +750,7 @@ rtsig_recalc(struct event_base *base, void *arg, int max) */ static inline int -do_poll(struct rtsigop *op, struct timespec *ts) +do_poll(struct rtsigop *op, struct timespec *ts, struct timespec **ts_p) { int res = 0; int i = 0; @@ -758,7 +758,11 @@ do_poll(struct rtsigop *op, struct timespec *ts) if (op->cur > 1) { /* non-empty poll set (modulo the signalfd) */ if (op->nonsock) { - int timeout = ts->tv_nsec / 1000000 + ts->tv_sec * 1000; + int timeout = -1; + + if (*ts_p != NULL) + timeout = (*ts_p)->tv_nsec / 1000000 + + (*ts_p)->tv_sec * 1000; sigprocmask(SIG_UNBLOCK, &(op->sigs), NULL); @@ -768,6 +772,7 @@ do_poll(struct rtsigop *op, struct timespec *ts) ts->tv_sec = 0; ts->tv_nsec = 0; + *ts_p = ts; } else { res = poll(op->poll, op->cur, 0); } @@ -777,6 +782,7 @@ do_poll(struct rtsigop *op, struct timespec *ts) } else if (res) { ts->tv_sec = 0; ts->tv_nsec = 0; + *ts_p = ts; } i = 0; @@ -894,17 +900,18 @@ do_siginfo_dispatch(struct event_base *base, struct rtsigop *op, * return -1 on error */ static inline int -do_sigwait(struct event_base *base, struct rtsigop *op, struct timespec *ts, - sigset_t *sigs) +do_sigwait(struct event_base *base, struct rtsigop *op, + struct timespec *ts, struct timespec **ts_p, sigset_t *sigs) { for (;;) { siginfo_t info; int signum; - signum = sigtimedwait(sigs, &info, ts); + signum = sigtimedwait(sigs, &info, *ts_p); ts->tv_sec = 0; ts->tv_nsec = 0; + *ts_p = ts; if (signum == -1) { if (errno == EAGAIN || errno == EINTR) @@ -920,7 +927,7 @@ do_sigwait(struct event_base *base, struct rtsigop *op, struct timespec *ts, static inline int do_signals_from_socket(struct event_base *base, struct rtsigop *op, - struct timespec *ts) + struct timespec *ts, struct timespec **ts_p) { int fd = op->signal_recv_fd; siginfo_t info; @@ -937,6 +944,7 @@ do_signals_from_socket(struct event_base *base, struct rtsigop *op, } else { ts->tv_sec = 0; ts->tv_nsec = 0; + *ts_p = ts; if (1 == do_siginfo_dispatch(base, op, &info)) return (1); } @@ -948,17 +956,21 @@ int rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv) { struct rtsigop *op = (struct rtsigop *) arg; - struct timespec ts; + struct timespec ts, *ts_p = NULL; int res; sigset_t sigs; - ts.tv_sec = tv->tv_sec; - ts.tv_nsec = tv->tv_usec * 1000; + if (tv != NULL) { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + *ts_p = ts; + } poll_for_level: - res = do_poll(op, &ts); /* ts can be modified in do_XXX() */ + /* ts and ts_p can be modified in do_XXX() */ + res = do_poll(op, &ts, &ts_p); - res = do_signals_from_socket(base, op, &ts); + res = do_signals_from_socket(base, op, &ts, &ts_p); if (res == 1) goto poll_for_level; else if (res == -1) @@ -973,7 +985,7 @@ rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv) signotset(&sigs); sigorset(&sigs, &sigs, &op->sigs); - res = do_sigwait(base, op, &ts, &sigs); + res = do_sigwait(base, op, &ts, &ts_p, &sigs); if (res == 1) goto poll_for_level; -- 1.5.3.GIT
_______________________________________________ Libevent-users mailing list Libevent-users@monkey.org http://monkey.org/mailman/listinfo/libevent-users