]> granicus.if.org Git - libevent/commitdiff
Infrastructure for using faster/fewer syscalls when creating sockets
authorNick Mathewson <nickm@torproject.org>
Fri, 10 Feb 2012 20:55:15 +0000 (15:55 -0500)
committerNick Mathewson <nickm@torproject.org>
Fri, 10 Feb 2012 21:11:47 +0000 (16:11 -0500)
Linux provides some features that allow avoiding extra calls to
fcntl when creating new nonblocking/close-on-exec sockets, so
we can add wrapper functions to emulate those when they are not
available.

Additionally, even when we are emulating those functions, we can
take a fast path that cuts our fcntl calls in half: we don't need to
look up the previous value of a file's flags when we have just
created it.

configure.in
evutil.c
util-internal.h

index b60669baefd17a9e6d29e4662e0bc200ceebe7eb..021f97c07eac10449437794a50f640b4509e9869 100644 (file)
@@ -305,6 +305,7 @@ AC_HEADER_TIME
 
 dnl Checks for library functions.
 AC_CHECK_FUNCS([ \
+  accept4 \
   arc4random \
   arc4random_buf \
   clock_gettime \
@@ -323,6 +324,7 @@ AC_CHECK_FUNCS([ \
   mmap \
   nanosleep \
   pipe \
+  pipe2 \
   putenv \
   sendfile \
   setenv \
index ae90d7017f1576ce9ed0f70ea6f6b3f0268027cc..3280d086ba1f8a6b7fde1642ea21462a4335fe8c 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -302,6 +302,24 @@ evutil_make_socket_nonblocking(evutil_socket_t fd)
        return 0;
 }
 
+/* Faster version of evutil_make_socket_nonblocking for internal use.
+ *
+ * Requires that no F_SETFL flags were previously set on the fd.
+ */
+static int
+evutil_fast_socket_nonblocking(evutil_socket_t fd)
+{
+#ifdef _WIN32
+       return evutil_make_socket_nonblocking(fd);
+#else
+       if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+               event_warn("fcntl(%d, F_SETFL)", fd);
+               return -1;
+       }
+       return 0;
+#endif
+}
+
 int
 evutil_make_listen_socket_reuseable(evutil_socket_t sock)
 {
@@ -334,6 +352,22 @@ evutil_make_socket_closeonexec(evutil_socket_t fd)
        return 0;
 }
 
+/* Faster version of evutil_make_socket_closeonexec for internal use.
+ *
+ * Requires that no F_SETFD flags were previously set on the fd.
+ */
+static int
+evutil_fast_socket_closeonexec(evutil_socket_t fd)
+{
+#if !defined(_WIN32) && defined(_EVENT_HAVE_SETFD)
+       if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+               event_warn("fcntl(%d, F_SETFD)", fd);
+               return -1;
+       }
+#endif
+       return 0;
+}
+
 int
 evutil_closesocket(evutil_socket_t sock)
 {
@@ -2294,3 +2328,168 @@ evutil_usleep(const struct timeval *tv)
        select(0, NULL, NULL, NULL, tv);
 #endif
 }
+
+/* Internal wrapper around 'socket' to provide Linux-style support for
+ * syscall-saving methods where available.
+ *
+ * In addition to regular socket behavior, you can use a bitwise or to set the
+ * flags EVUTIL_SOCK_NONBLOCK and EVUTIL_SOCK_CLOEXEC in the 'type' argument,
+ * to make the socket nonblocking or close-on-exec with as few syscalls as
+ * possible.
+ */
+evutil_socket_t
+evutil_socket(int domain, int type, int protocol)
+{
+       evutil_socket_t r;
+#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
+       r = socket(domain, type, protocol);
+       if (r < 0 && !(type & (SOCK_NONBLOCK|SOCK_CLOEXEC)))
+               return -1;
+#endif
+#define SOCKET_TYPE_MASK (~(EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC))
+       r = socket(domain, type & SOCKET_TYPE_MASK, protocol);
+       if (r < 0)
+               return -1;
+       if (type & EVUTIL_SOCK_NONBLOCK) {
+               if (evutil_fast_socket_nonblocking(r) < 0) {
+                       evutil_closesocket(r);
+                       return -1;
+               }
+       }
+       if (type & EVUTIL_SOCK_CLOEXEC) {
+               if (evutil_fast_socket_closeonexec(r) < 0) {
+                       evutil_closesocket(r);
+                       return -1;
+               }
+       }
+       return r;
+}
+
+/* Internal wrapper around 'accept' or 'accept4' to provide Linux-style
+ * support for syscall-saving methods where available.
+ *
+ * In addition to regular accept behavior, you can set one or more of flags
+ * EVUTIL_SOCK_NONBLOCK and EVUTIL_SOCK_CLOEXEC in the 'flags' argument, to
+ * make the socket nonblocking or close-on-exec with as few syscalls as
+ * possible.
+ */
+evutil_socket_t
+evutil_accept4(evutil_socket_t sockfd, struct sockaddr *addr,
+    socklen_t *addrlen, int flags)
+{
+#if defined(_EVENT_HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+       return accept4(sockfd, addr, addrlen, flags);
+#else
+       evutil_socket_t result = accept(sockfd, addr, addrlen);
+       if (result < 0)
+               return result;
+
+       if (flags & EVUTIL_SOCK_CLOEXEC) {
+               if (evutil_fast_socket_closeonexec(result) < 0) {
+                       evutil_closesocket(result);
+                       return -1;
+               }
+       }
+       if (flags & EVUTIL_SOCK_NONBLOCK) {
+               if (evutil_fast_socket_nonblocking(result) < 0) {
+                       evutil_closesocket(result);
+                       return -1;
+               }
+       }
+       return result;
+#endif
+}
+
+/* Internal function: Set fd[0] and fd[1] to a pair of fds such that writes on
+ * fd[0] get read from fd[1].  Make both fds nonblocking and close-on-exec.
+ * Return 0 on success, -1 on failure.
+ */
+int
+evutil_make_internal_pipe(evutil_socket_t fd[2])
+{
+       /*
+         Making the second socket nonblocking is a bit subtle, given that we
+         ignore any EAGAIN returns when writing to it, and you don't usally
+         do that for a nonblocking socket. But if the kernel gives us EAGAIN,
+         then there's no need to add any more data to the buffer, since
+         the main thread is already either about to wake up and drain it,
+         or woken up and in the process of draining it.
+       */
+
+#if defined(_EVENT_HAVE_PIPE2)
+       if (pipe2(fd, O_NONBLOCK|O_CLOEXEC) == 0)
+               return 0;
+#endif
+#if defined(_EVENT_HAVE_PIPE)
+       if (pipe(fd) == 0) {
+               if (evutil_fast_socket_nonblocking(fd[0]) < 0 ||
+                   evutil_fast_socket_nonblocking(fd[1]) < 0 ||
+                   evutil_fast_socket_closeonexec(fd[0]) < 0 ||
+                   evutil_fast_socket_closeonexec(fd[1]) < 0) {
+                       close(fd[0]);
+                       close(fd[1]);
+                       fd[0] = fd[1] = -1;
+                       return -1;
+               }
+               return 0;
+       } else {
+               event_warn("%s: pipe", __func__);
+       }
+#endif
+
+#ifdef _WIN32
+#define LOCAL_SOCKETPAIR_AF AF_INET
+#else
+#define LOCAL_SOCKETPAIR_AF AF_UNIX
+#endif
+       if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0, fd) == 0) {
+               if (evutil_fast_socket_nonblocking(fd[0]) < 0 ||
+                   evutil_fast_socket_nonblocking(fd[1]) < 0 ||
+                   evutil_fast_socket_closeonexec(fd[0]) < 0 ||
+                   evutil_fast_socket_closeonexec(fd[1]) < 0) {
+                       evutil_closesocket(fd[0]);
+                       evutil_closesocket(fd[1]);
+                       fd[0] = fd[1] = -1;
+                       return -1;
+               }
+               return 0;
+       }
+       fd[0] = fd[1] = -1;
+       return -1;
+}
+
+/* Wrapper around eventfd on systems that provide it.  Unlike the system
+ * eventfd, it always supports EVUTIL_EFD_CLOEXEC and EVUTIL_EFD_NONBLOCK as
+ * flags.  Returns -1 on error or if eventfd is not supported.
+ */
+evutil_socket_t
+evutil_eventfd(unsigned initval, int flags)
+{
+#if defined(_EVENT_HAVE_EVENTFD) && defined(_EVENT_HAVE_SYS_EVENTFD_H)
+       int r;
+#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
+       r = eventfd(initval, flags);
+       if (r >= 0 || flags == 0)
+               return r;
+#endif
+       r = eventfd(initval, 0);
+       if (r < 0)
+               return r;
+       if (flags & EVUTIL_EFD_CLOEXEC) {
+               if (evutil_fast_socket_closeonexec(r) < 0) {
+                       evutil_closesocket(r);
+                       return -1;
+               }
+       }
+       if (flags & EVUTIL_EFD_NONBLOCK) {
+               if (evutil_fast_socket_nonblocking(r) < 0) {
+                       evutil_closesocket(r);
+                       return -1;
+               }
+       }
+       return r;
+#else
+       return -1;
+#endif
+}
+
index d1045e90b163f8ac16bf59da4d95880ba13a773d..8880bf0fd3285cb581ab279efd63d95419e9fdc5 100644 (file)
@@ -38,6 +38,9 @@
 #ifdef _EVENT_HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
+#ifdef _EVENT_HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
 #include "event2/util.h"
 
 #include "ipv6-internal.h"
@@ -311,6 +314,34 @@ HANDLE evutil_load_windows_system_library(const TCHAR *library_name);
 #endif
 #endif
 
+evutil_socket_t evutil_socket(int domain, int type, int protocol);
+evutil_socket_t evutil_accept4(evutil_socket_t sockfd, struct sockaddr *addr,
+    socklen_t *addrlen, int flags);
+int evutil_make_internal_pipe(evutil_socket_t fd[2]);
+evutil_socket_t evutil_eventfd(unsigned initval, int flags);
+
+#ifdef SOCK_NONBLOCK
+#define EVUTIL_SOCK_NONBLOCK SOCK_NONBLOCK
+#else
+#define EVUTIL_SOCK_NONBLOCK 0x4000000
+#endif
+#ifdef SOCK_CLOEXEC
+#define EVUTIL_SOCK_CLOEXEC SOCK_CLOEXEC
+#else
+#define EVUTIL_SOCK_CLOEXEC 0x80000000
+#endif
+#ifdef EFD_NONBLOCK
+#define EVUTIL_EFD_NONBLOCK EFD_NONBLOCK
+#else
+#define EVUTIL_EFD_NONBLOCK 0x4000
+#endif
+#ifdef EFD_CLOEXEC
+#define EVUTIL_EFD_CLOEXEC EFD_CLOEXEC
+#else
+#define EVUTIL_EFD_CLOEXEC 0x8000
+#endif
+
+
 #ifdef __cplusplus
 }
 #endif