Added epoll support on linux (#3798)

Added epoll event dispatching support on linux systems.
You can enable it by adding the configure option --enable-epoll

Credits to Hercules.
This commit is contained in:
Lemongrass3110
2018-12-26 23:58:04 +01:00
committed by GitHub
parent 24dad2ee57
commit eb2e40c370
4 changed files with 290 additions and 14 deletions

View File

@@ -9,22 +9,34 @@
#include "winapi.hpp"
#else
#include <errno.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#if defined(__linux__) || defined(__linux)
#include <linux/tcp.h>
#ifdef SOCKET_EPOLL
#include <sys/epoll.h>
#endif
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif
#ifndef SIOCGIFCONF
#include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori]
#include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori]
#endif
#ifndef FIONBIO
#include <sys/filio.h> // FIONBIO on Solaris [FlavioJS]
#include <sys/filio.h> // FIONBIO on Solaris [FlavioJS]
#endif
#ifdef HAVE_SETRLIMIT
#include <sys/resource.h>
#include <sys/resource.h>
#endif
#endif
@@ -203,7 +215,17 @@ char* sErr(int code)
#define MSG_NOSIGNAL 0
#endif
fd_set readfds;
#ifndef SOCKET_EPOLL
// Select based Event Dispatcher
fd_set readfds;
#else
// Epoll based Event Dispatcher
static int epoll_maxevents = (FD_SETSIZE / 2);
static int epfd = SOCKET_ERROR;
static struct epoll_event epevent;
static struct epoll_event *epevents = nullptr;
#endif
int fd_max;
time_t last_tick;
time_t stall_time = 60;
@@ -478,8 +500,22 @@ int connect_client(int listen_fd)
}
#endif
if( fd_max <= fd ) fd_max = fd + 1;
#ifndef SOCKET_EPOLL
// Select Based Event Dispatcher
sFD_SET(fd,&readfds);
#else
// Epoll based Event Dispatcher
epevent.data.fd = fd;
epevent.events = EPOLLIN;
if( epoll_ctl( epfd, EPOLL_CTL_ADD, fd, &epevent ) == SOCKET_ERROR ){
ShowError( "connect_client: Failed to add to epoll event dispatcher for new socket #%d: %s\n", fd, error_msg() );
sClose( fd );
return -1;
}
#endif
if( fd_max <= fd ) fd_max = fd + 1;
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr);
@@ -531,8 +567,22 @@ int make_listen_bind(uint32 ip, uint16 port)
exit(EXIT_FAILURE);
}
if(fd_max <= fd) fd_max = fd + 1;
#ifndef SOCKET_EPOLL
// Select Based Event Dispatcher
sFD_SET(fd, &readfds);
#else
// Epoll based Event Dispatcher
epevent.data.fd = fd;
epevent.events = EPOLLIN;
if( epoll_ctl( epfd, EPOLL_CTL_ADD, fd, &epevent ) == SOCKET_ERROR ){
ShowError( "make_listen_bind: failed to add listener socket #%d to epoll event dispatcher: %s\n", fd, error_msg() );
sClose(fd);
exit(EXIT_FAILURE);
}
#endif
if(fd_max <= fd) fd_max = fd + 1;
create_session(fd, connect_client, null_send, null_parse);
session[fd]->client_addr = 0; // just listens
@@ -643,8 +693,22 @@ int make_connection(uint32 ip, uint16 port, bool silent,int timeout) {
set_nonblocking(fd, 1);
#endif
if (fd_max <= fd) fd_max = fd + 1;
#ifndef SOCKET_EPOLL
// Select Based Event Dispatcher
sFD_SET(fd,&readfds);
#else
// Epoll based Event Dispatcher
epevent.data.fd = fd;
epevent.events = EPOLLIN;
if( epoll_ctl( epfd, EPOLL_CTL_ADD, fd, &epevent ) == SOCKET_ERROR ){
ShowError( "make_connection: failed to add socket #%d to epoll event dispatcher: %s\n", fd, error_msg() );
sClose(fd);
return -1;
}
#endif
if (fd_max <= fd) fd_max = fd + 1;
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
session[fd]->client_addr = ntohl(remote_address.sin_addr.s_addr);
@@ -821,8 +885,10 @@ int WFIFOSET(int fd, size_t len)
int do_sockets(t_tick next)
{
#ifndef SOCKET_EPOLL
fd_set rfd;
struct timeval timeout;
#endif
int ret,i;
// PRESEND Timers are executed before do_sendrecv and can send packets and/or set sessions to eof.
@@ -840,6 +906,9 @@ int do_sockets(t_tick next)
}
#endif
#ifndef SOCKET_EPOLL
// Select based Event Dispatcher
// can timeout until the next tick
timeout.tv_sec = (long)(next/1000);
timeout.tv_usec = (long)(next%1000*1000);
@@ -856,6 +925,20 @@ int do_sockets(t_tick next)
}
return 0; // interrupted by a signal, just loop and try again
}
#else
// Epoll based Event Dispatcher
ret = epoll_wait( epfd, epevents, epoll_maxevents, next );
if( ret == SOCKET_ERROR ){
if( sErrno != S_EINTR ){
ShowFatalError( "do_sockets: epoll_wait() failed, %s!\n", error_msg() );
exit( EXIT_FAILURE );
}
return 0; // interrupted by a signal, just loop and try again
}
#endif
last_tick = time(NULL);
@@ -867,6 +950,26 @@ int do_sockets(t_tick next)
if( session[fd] )
session[fd]->func_recv(fd);
}
#elif defined(SOCKET_EPOLL)
// epoll based selection
for( i = 0; i < ret; i++ ){
struct epoll_event *it = &epevents[i];
int fd = it->data.fd;
struct socket_data *sock = session[fd];
if( !sock ){
continue;
}
if( ( it->events & (EPOLLERR|EPOLLHUP) ) || !( it->events & EPOLLIN ) ){
// Got Error on this connection
set_eof( fd );
}else if( it->events & EPOLLIN ){
// data waiting
sock->func_recv( fd );
}
}
#else
// otherwise assume that the fd_set is a bit-array and enumerate it in a standard way
for( i = 1; ret && i < fd_max; ++i )
@@ -1238,6 +1341,17 @@ int socket_config_read(const char* cfgName)
ddos_autoreset = atoi(w2);
else if (!strcmpi(w1,"debug"))
access_debug = config_switch(w2);
#ifdef SOCKET_EPOLL
else if( !strcmpi( w1, "epoll_maxevents" ) ){
epoll_maxevents = atoi(w2);
// minimum that seems to be useful
if( epoll_maxevents < 16 ){
ShowWarning( "socket_config_read: epoll_maxevents is set too low. Defaulting to 16...\n" );
epoll_maxevents = 16;
}
}
#endif
#endif
else if (!strcmpi(w1, "import"))
socket_config_read(w2);
@@ -1287,6 +1401,16 @@ void socket_final(void)
if( WSACleanup() != 0 ){
ShowError("socket_final: WinSock could not be cleaned up! %s\n", error_msg() );
}
#elif defined(SOCKET_EPOLL)
if( epfd != SOCKET_ERROR ){
sClose(epfd);
epfd = SOCKET_ERROR;
}
if( epevents != nullptr ){
aFree( epevents );
epevents = nullptr;
}
#endif
}
@@ -1297,7 +1421,17 @@ void do_close(int fd)
return;// invalid
flush_fifo(fd); // Try to send what's left (although it might not succeed since it's a nonblocking socket)
#ifndef SOCKET_EPOLL
// Select based Event Dispatcher
sFD_CLR(fd, &readfds);// this needs to be done before closing the socket
#else
// Epoll based Event Dispatcher
epevent.data.fd = fd;
epevent.events = EPOLLIN;
epoll_ctl( epfd, EPOLL_CTL_DEL, fd, &epevent ); // removing the socket from epoll when it's being closed is not required but recommended
#endif
sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes
sClose(fd); // We don't really care if these closing functions return an error, we are just shutting down and not reusing this socket.
if (session[fd]) delete_session(fd);
@@ -1442,7 +1576,25 @@ void socket_init(void)
// Get initial local ips
naddr_ = socket_getips(addr_,16);
#ifndef SOCKET_EPOLL
// Select based Event Dispatcher:
sFD_ZERO(&readfds);
ShowInfo( "Server uses '" CL_WHITE "select" CL_RESET "' as event dispatcher\n" );
#else
// Epoll based Event Dispatcher
epfd = epoll_create( FD_SETSIZE ); // 2.6.8 or newer ignores the expected socket amount argument
if( epfd == SOCKET_ERROR ){
ShowError( "Failed to create epoll event dispatcher: %s\n", error_msg() );
exit( EXIT_FAILURE );
}
memset( &epevent, 0x00, sizeof( struct epoll_event ) );
epevents = (struct epoll_event *)aCalloc( epoll_maxevents, sizeof( struct epoll_event ) );
ShowInfo( "Server uses '" CL_WHITE "epoll" CL_RESET "' with up to " CL_WHITE "%d" CL_RESET " events per cycle as event dispatcher\n", epoll_maxevents );
#endif
#if defined(SEND_SHORTLIST)
memset(send_shortlist_set, 0, sizeof(send_shortlist_set));
#endif