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

Reply via email to