core: Add support for eventfd
authorChris Dickens <christopher.a.dickens@gmail.com>
Thu, 13 Aug 2020 01:44:31 +0000 (18:44 -0700)
committerChris Dickens <christopher.a.dickens@gmail.com>
Thu, 13 Aug 2020 01:44:31 +0000 (18:44 -0700)
On systems that support it, using an eventfd for simple signalling is
much more efficient than using a pipe. Add detection of eventfd to the
configure script and wire it up in the POSIX event abstraction.

Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
configure.ac
libusb/os/events_posix.c
libusb/os/events_posix.h
libusb/version_nano.h

index 4d3cfae..5984380 100644 (file)
@@ -225,6 +225,44 @@ elif test "x$backend" != xdarwin && test "x$backend" != xwindows; then
        AC_MSG_ERROR([clock_gettime() is required on this platform])
 fi
 
+dnl eventfd support
+if test "x$backend" = xlinux || test "x$backend" = xsunos; then
+       AC_ARG_ENABLE([eventfd],
+               [AS_HELP_STRING([--enable-eventfd], [use eventfd for signalling [default=auto]])],
+               [use_eventfd=$enableval],
+               [use_eventfd=auto])
+       if test "x$use_eventfd" != xno; then
+               AC_CHECK_HEADER([sys/eventfd.h], [eventfd_h=yes], [eventfd_h=])
+               if test "x$eventfd_h" = xyes; then
+                       AC_CHECK_DECLS([EFD_NONBLOCK, EFD_CLOEXEC], [eventfd_h_ok=yes], [eventfd_h_ok=], [[#include <sys/eventfd.h>]])
+                       if test "x$eventfd_h_ok" = xyes; then
+                               AC_CHECK_FUNC([eventfd], [eventfd_ok=yes], [eventfd_ok=])
+                               if test "x$eventfd_ok" = xyes; then
+                                       AC_DEFINE([HAVE_EVENTFD], [1], [Define to 1 if the system has eventfd functionality.])
+                               elif test "x$use_eventfd" = xyes; then
+                                       AC_MSG_ERROR([eventfd() function not found; glibc 2.9+ required])
+                               fi
+                       elif test "x$use_eventfd" = xyes; then
+                               AC_MSG_ERROR([eventfd header not usable; glibc 2.9+ required])
+                       fi
+               elif test "x$use_eventfd" = xyes; then
+                       AC_MSG_ERROR([eventfd header not available; glibc 2.9+ required])
+               fi
+       fi
+       AC_MSG_CHECKING([whether to use eventfd for signalling])
+       if test "x$use_eventfd" = xno; then
+               AC_MSG_RESULT([no (disabled by user)])
+       elif test "x$eventfd_h" != xyes; then
+               AC_MSG_RESULT([no (header not available)])
+       elif test "x$eventfd_h_ok" != xyes; then
+               AC_MSG_RESULT([no (header not usable)])
+       elif test "x$eventfd_ok" != xyes; then
+               AC_MSG_RESULT([no (functions not available)])
+       else
+               AC_MSG_RESULT([yes])
+       fi
+fi
+
 dnl timerfd support
 if test "x$backend" = xlinux || test "x$backend" = xsunos; then
        AC_ARG_ENABLE([timerfd],
index 01064af..952ea5f 100644 (file)
 
 #include <errno.h>
 #include <fcntl.h>
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
 #ifdef HAVE_TIMERFD
 #include <sys/timerfd.h>
 #endif
 #include <unistd.h>
 
+#ifdef HAVE_EVENTFD
+#define EVENT_READ_FD(e)       ((e)->eventfd)
+#define EVENT_WRITE_FD(e)      ((e)->eventfd)
+#else
+#define EVENT_READ_FD(e)       ((e)->pipefd[0])
+#define EVENT_WRITE_FD(e)      ((e)->pipefd[1])
+#endif
+
 #ifdef HAVE_NFDS_T
 typedef nfds_t usbi_nfds_t;
 #else
@@ -35,6 +46,15 @@ typedef unsigned int usbi_nfds_t;
 
 int usbi_create_event(usbi_event_t *event)
 {
+#ifdef HAVE_EVENTFD
+       event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+       if (event->eventfd == -1) {
+               usbi_err(NULL, "failed to create eventfd, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+#else
 #if defined(HAVE_PIPE2)
        int ret = pipe2(event->pipefd, O_CLOEXEC);
 #else
@@ -87,34 +107,40 @@ err_close_pipe:
        close(event->pipefd[1]);
        close(event->pipefd[0]);
        return LIBUSB_ERROR_OTHER;
+#endif
 }
 
 void usbi_destroy_event(usbi_event_t *event)
 {
+#ifdef HAVE_EVENTFD
+       if (close(event->eventfd) == -1)
+               usbi_warn(NULL, "failed to close eventfd, errno=%d", errno);
+#else
        if (close(event->pipefd[1]) == -1)
                usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno);
        if (close(event->pipefd[0]) == -1)
                usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno);
+#endif
 }
 
 void usbi_signal_event(usbi_event_t *event)
 {
-       unsigned char dummy = 1;
+       uint64_t dummy = 1;
        ssize_t r;
 
-       r = write(event->pipefd[1], &dummy, sizeof(dummy));
+       r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
        if (r != sizeof(dummy))
-               usbi_warn(NULL, "pipe write failed");
+               usbi_warn(NULL, "event write failed");
 }
 
 void usbi_clear_event(usbi_event_t *event)
 {
-       unsigned char dummy;
+       uint64_t dummy;
        ssize_t r;
 
-       r = read(event->pipefd[0], &dummy, sizeof(dummy));
+       r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
        if (r != sizeof(dummy))
-               usbi_warn(NULL, "pipe read failed");
+               usbi_warn(NULL, "event read failed");
 }
 
 #ifdef HAVE_TIMERFD
index 401de04..d81b5c4 100644 (file)
 typedef int usbi_os_handle_t;
 #define USBI_OS_HANDLE_FORMAT_STRING   "fd %d"
 
+#ifdef HAVE_EVENTFD
+typedef struct usbi_event {
+       int eventfd;
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e)        ((e)->eventfd)
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT     { -1 }
+#else
 typedef struct usbi_event {
        int pipefd[2];
 } usbi_event_t;
 #define USBI_EVENT_OS_HANDLE(e)        ((e)->pipefd[0])
 #define USBI_EVENT_POLL_EVENTS POLLIN
 #define USBI_INVALID_EVENT     { { -1, -1 } }
+#endif
 
 #ifdef HAVE_TIMERFD
 #define HAVE_OS_TIMER 1
index bf72b26..c972e09 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11532
+#define LIBUSB_NANO 11533