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