core: Add support for eventfd
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>
diff --git a/configure.ac b/configure.ac
index 4d3cfae..5984380 100644
--- a/configure.ac
+++ b/configure.ac
@@ -225,6 +225,44 @@
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],
diff --git a/libusb/os/events_posix.c b/libusb/os/events_posix.c
index 01064af..952ea5f 100644
--- a/libusb/os/events_posix.c
+++ b/libusb/os/events_posix.c
@@ -22,11 +22,22 @@
#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 @@
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 @@
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
diff --git a/libusb/os/events_posix.h b/libusb/os/events_posix.h
index 401de04..d81b5c4 100644
--- a/libusb/os/events_posix.h
+++ b/libusb/os/events_posix.h
@@ -26,12 +26,21 @@
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
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index bf72b26..c972e09 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11532
+#define LIBUSB_NANO 11533