Use eventfd for main-thread notification where available (i.e., linux).
authorNick Mathewson <nickm@torproject.org>
Mon, 19 Jan 2009 20:37:24 +0000 (20:37 +0000)
committerNick Mathewson <nickm@torproject.org>
Mon, 19 Jan 2009 20:37:24 +0000 (20:37 +0000)
svn:r1023

ChangeLog
configure.in
event-internal.h
event.c

index cba9b9db038a74dad9e2b5f7b566044cd56cb994..229d69a283e2c7b989c99cce811e09f6fd14c9f9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -136,6 +136,7 @@ Changes in current version:
  o Add generic implementations for parsing and emiting IPv6 addresses on platforms that do not have inet_ntop and/or inet_pton.
  o Allow DNS servers that have IPv6 addresses.
  o Add an evbuffer_write_atmost() function to write a limited number of bytes to an fd.
+ o Refactor internal notify-main-thread logic to prefer eventfd to pipe, then pipe to socketpair, and only use socketpairs as a last resort.
 
 Changes in 1.4.0:
  o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
index 6518659fcac7d39e2ff4b0d4ba8d8729bc99f103..081e8605980b5acaa02dfde91358f3aad593c7f1 100644 (file)
@@ -53,7 +53,7 @@ AM_CONDITIONAL(ZLIB_REGRESS, [test "$have_zlib" != "no"])
 
 dnl Checks for header files.
 AC_HEADER_STDC
-AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h)
+AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h)
 if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
        AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
        AC_EGREP_CPP(yes,
@@ -146,7 +146,7 @@ AC_C_INLINE
 AC_HEADER_TIME
 
 dnl Checks for library functions.
-AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe)
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd)
 
 AC_CHECK_SIZEOF(long)
 
index 9e879baf538538f337091527a088e9f12bbd7963..3d0134f3d2ec96346148cf3a9fc4e3a0709012a4 100644 (file)
@@ -111,7 +111,7 @@ struct event_base {
        struct min_heap timeheap;
 
        struct timeval tv_cache;
-       
+
        /* threading support */
        unsigned long th_owner_id;
        unsigned long (*th_get_id)(void);
@@ -123,8 +123,11 @@ struct event_base {
 
        /* lock or unlock a lock */
        void (*th_lock)(int mode, void *lock);
+
+       /* Notify main thread to wake up break, etc. */
        int th_notify_fd[2];
        struct event th_notify;
+       int (*th_notify_fn)(struct event_base *base);
 };
 
 struct event_config_entry {
diff --git a/event.c b/event.c
index 2cc7b9865fd95a2a788e5835a976168f99080d70..bc9e88b2d0884da0906008d54d2a36547224e963 100644 (file)
--- a/event.c
+++ b/event.c
@@ -51,6 +51,9 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
 #include <ctype.h>
 #include <errno.h>
 #include <signal.h>
@@ -316,6 +319,8 @@ event_base_free(struct event_base *base)
                event_del(&base->th_notify);
                EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);
                EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);
+               base->th_notify_fd[0] = -1;
+               base->th_notify_fd[1] = -1;
        }
 
        if (base->th_base_lock != NULL)
@@ -1039,7 +1044,7 @@ event_add(struct event *ev, const struct timeval *tv)
 }
 
 static int
-evthread_notify_base(struct event_base *base)
+evthread_notify_base_default(struct event_base *base)
 {
        char buf[1];
        int r;
@@ -1052,6 +1057,28 @@ evthread_notify_base(struct event_base *base)
        return (r < 0) ? -1 : 0;
 }
 
+#if defined(HAVE_EVENTFD) && defined(HAVE_SYS_EVENTFD_H)
+static int
+evthread_notify_base_eventfd(struct event_base *base)
+{
+       ev_uint64_t msg = 1;
+       int r;
+       do {
+               r = write(base->th_notify_fd[0], (void*) &msg, sizeof(msg));
+       } while (r < 0 && errno == EAGAIN);
+
+       return (r < 0) ? -1 : 0;
+}
+#endif
+
+static int
+evthread_notify_base(struct event_base *base)
+{
+       if (!base->th_notify_fn)
+               return -1;
+       return base->th_notify_fn(base);
+}
+
 static inline int
 event_add_internal(struct event *ev, const struct timeval *tv)
 {
@@ -1503,13 +1530,25 @@ evthread_set_locking_callback(struct event_base *base,
        base->th_lock = locking_fn;
 }
 
+#if defined(HAVE_EVENTFD) && defined(HAVE_SYS_EVENTFD_H)
 static void
-evthread_notification_callback(int fd, short what, void *arg)
+evthread_notify_drain_eventfd(int fd, short what, void *arg)
 {
        struct event_base *base = arg;
-       unsigned char buf[128];
+       ev_uint64_t msg;
+
+       read(fd, (void*) &msg, sizeof(msg));
+
+       /* XXX Why not make th_notify EV_PERSIST? */
+       event_add(&base->th_notify, NULL);
+}
+#endif
 
-       /* Drain the socket or pipe. */
+static void
+evthread_notify_drain_default(evutil_socket_t fd, short what, void *arg)
+{
+       struct event_base *base = arg;
+       unsigned char buf[128];
 #ifdef WIN32
        while (recv(fd, (char*)buf, sizeof(buf), 0) > 0)
                ;
@@ -1518,6 +1557,7 @@ evthread_notification_callback(int fd, short what, void *arg)
                ;
 #endif
 
+       /* XXX Why not make th_notify EV_PERSIST? */
        event_add(&base->th_notify, NULL);
 }
 
@@ -1546,16 +1586,28 @@ evthread_set_id_callback(struct event_base *base,
 int
 evthread_make_base_notifiable(struct event_base *base)
 {
+       void (*cb)(evutil_socket_t, short, void *) = evthread_notify_drain_default;
+       int (*notify)(struct event_base *) = evthread_notify_base_default;
+
        if (!base)
                return -1;
 
        if (base->th_notify_fd[0] >= 0)
                return 0;
 
+#if defined(HAVE_EVENTFD) && defined(HAVE_SYS_EVENTFD_H)
+       base->th_notify_fd[0] = eventfd(0, 0);
+       if (base->th_notify_fd[0] >= 0) {
+               notify = evthread_notify_base_eventfd;
+               cb = evthread_notify_drain_eventfd;
+       } else
+#endif
 #if defined(_EVENT_HAVE_PIPE)
-       if ((base->evsel->features & EV_FEATURE_FDS)) {
-               if (pipe(base->th_notify_fd) < 0)
-                       event_warn("%s: pipe", __func__);
+       {
+               if ((base->evsel->features & EV_FEATURE_FDS)) {
+                       if (pipe(base->th_notify_fd) < 0)
+                               event_warn("%s: pipe", __func__);
+               }
        }
        if (base->th_notify_fd[0] < 0)
 #endif
@@ -1569,13 +1621,15 @@ evthread_make_base_notifiable(struct event_base *base)
 
        evutil_make_socket_nonblocking(base->th_notify_fd[0]);
 
+       base->th_notify_fn = notify;
+
        // This can't be right, can it?  We want writes to this socket to
        // just succeed.
        // evutil_make_socket_nonblocking(base->th_notify_fd[1]);
 
        /* prepare an event that we can use for wakeup */
        event_assign(&base->th_notify, base, base->th_notify_fd[0], EV_READ,
-           evthread_notification_callback, base);
+                                cb, base);
 
        /* we need to mark this as internal event */
        base->th_notify.ev_flags |= EVLIST_INTERNAL;