Unverified Commit 5a3de819 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Use accept4 on linux instead of fcntl to make a client socket non-blocking (#9177)



This reduces system calls on linux when a new connection is made / accepted.

Changes:
* Add the SOCK_CLOEXEC option to the accept4() call
  This  ensure that a fork/exec call does not leak a file descriptor.
* Move anetCloexec and connNonBlock info anetGenericAccept
* Moving connNonBlock from accept handlers to anetGenericAccept

Moving connNonBlock from createClient, is safe because createClient is
used in the following ways:
1. without a connection (fake client)
2. on an accepted connection (see above)
3. creating the master client by using connConnect (see below)

The third case, can either use anetTcpNonBlockConnect, or connTLSConnect
which is by default non-blocking.
Co-authored-by: default avatarRajiv Kurian <geetasen@gmail.com>
Co-authored-by: default avatarOran Agra <oran@redislabs.com>
Co-authored-by: default avatarYoav Steinberg <yoav@redislabs.com>
parent ec582cc7
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <stdio.h> #include <stdio.h>
#include "anet.h" #include "anet.h"
#include "config.h"
static void anetSetError(char *err, const char *fmt, ...) static void anetSetError(char *err, const char *fmt, ...)
{ {
...@@ -491,18 +492,39 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog) ...@@ -491,18 +492,39 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
return s; return s;
} }
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
* returns the new socket FD, or -1 on error. */
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {
int fd; int fd;
do { do {
/* Use the accept4() call on linux to simultaneously accept and
* set a socket as non-blocking. */
#ifdef HAVE_ACCEPT4
fd = accept4(s, sa, len, SOCK_NONBLOCK | SOCK_CLOEXEC);
#else
fd = accept(s,sa,len); fd = accept(s,sa,len);
#endif
} while(fd == -1 && errno == EINTR); } while(fd == -1 && errno == EINTR);
if (fd == -1) { if (fd == -1) {
anetSetError(err, "accept: %s", strerror(errno)); anetSetError(err, "accept: %s", strerror(errno));
return ANET_ERR; return ANET_ERR;
} }
#ifndef HAVE_ACCEPT4
if (anetCloexec(fd) == -1) {
anetSetError(err, "anetCloexec: %s", strerror(errno));
close(fd);
return ANET_ERR;
}
if (anetNonBlock(err, fd) != ANET_OK) {
close(fd);
return ANET_ERR;
}
#endif
return fd; return fd;
} }
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
* returns the new socket FD, or -1 on error. */
int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) { int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {
int fd; int fd;
struct sockaddr_storage sa; struct sockaddr_storage sa;
...@@ -522,6 +544,8 @@ int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) { ...@@ -522,6 +544,8 @@ int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {
return fd; return fd;
} }
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
* returns the new socket FD, or -1 on error. */
int anetUnixAccept(char *err, int s) { int anetUnixAccept(char *err, int s) {
int fd; int fd;
struct sockaddr_un sa; struct sockaddr_un sa;
......
...@@ -735,7 +735,6 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -735,7 +735,6 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
connClose(conn); connClose(conn);
return; return;
} }
connNonBlock(conn);
connEnableTcpNoDelay(conn); connEnableTcpNoDelay(conn);
/* Use non-blocking I/O for cluster messages. */ /* Use non-blocking I/O for cluster messages. */
......
...@@ -78,6 +78,11 @@ ...@@ -78,6 +78,11 @@
#define HAVE_EPOLL 1 #define HAVE_EPOLL 1
#endif #endif
/* Test for accept4() */
#ifdef __linux__
#define HAVE_ACCEPT4 1
#endif
#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__) #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1 #define HAVE_KQUEUE 1
#endif #endif
......
...@@ -115,7 +115,6 @@ client *createClient(connection *conn) { ...@@ -115,7 +115,6 @@ client *createClient(connection *conn) {
* in the context of a client. When commands are executed in other * in the context of a client. When commands are executed in other
* contexts (for instance a Lua script) we need a non connected client. */ * contexts (for instance a Lua script) we need a non connected client. */
if (conn) { if (conn) {
connNonBlock(conn);
connEnableTcpNoDelay(conn); connEnableTcpNoDelay(conn);
if (server.tcpkeepalive) if (server.tcpkeepalive)
connKeepAlive(conn,server.tcpkeepalive); connKeepAlive(conn,server.tcpkeepalive);
...@@ -1122,7 +1121,6 @@ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1122,7 +1121,6 @@ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
"Accepting client connection: %s", server.neterr); "Accepting client connection: %s", server.neterr);
return; return;
} }
anetCloexec(cfd);
serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport); serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport);
acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip); acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);
} }
...@@ -1143,7 +1141,6 @@ void acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1143,7 +1141,6 @@ void acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
"Accepting client connection: %s", server.neterr); "Accepting client connection: %s", server.neterr);
return; return;
} }
anetCloexec(cfd);
serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport); serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport);
acceptCommonHandler(connCreateAcceptedTLS(cfd, server.tls_auth_clients),0,cip); acceptCommonHandler(connCreateAcceptedTLS(cfd, server.tls_auth_clients),0,cip);
} }
...@@ -1163,7 +1160,6 @@ void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1163,7 +1160,6 @@ void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
"Accepting client connection: %s", server.neterr); "Accepting client connection: %s", server.neterr);
return; return;
} }
anetCloexec(cfd);
serverLog(LL_VERBOSE,"Accepted connection to %s", server.unixsocket); serverLog(LL_VERBOSE,"Accepted connection to %s", server.unixsocket);
acceptCommonHandler(connCreateAcceptedSocket(cfd),CLIENT_UNIX_SOCKET,NULL); acceptCommonHandler(connCreateAcceptedSocket(cfd),CLIENT_UNIX_SOCKET,NULL);
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment