summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Wilkemeyer <fw@f-ws.de>2016-01-10 10:56:22 +0100
committerAndrei Karas <akaras@inbox.ru>2016-07-08 19:57:11 +0300
commitc6f05662899417ad22c4fa3217e6c2ba2d27b5a3 (patch)
tree112a35960141c206025e41abd672b284d4495afe
parentc7b95260fa3f4d77138fa34710f60365f4c86c87 (diff)
downloadhercules-c6f05662899417ad22c4fa3217e6c2ba2d27b5a3.tar.gz
hercules-c6f05662899417ad22c4fa3217e6c2ba2d27b5a3.tar.bz2
hercules-c6f05662899417ad22c4fa3217e6c2ba2d27b5a3.tar.xz
hercules-c6f05662899417ad22c4fa3217e6c2ba2d27b5a3.zip
Added epoll as event-dispatcher on linux (instead of select())
Note: It will be enabled by default if configure detects that the system has support for epoll To enforce usage of select run configure with --enable-epoll=no
-rw-r--r--conf/packet.conf13
-rwxr-xr-xconfigure62
-rw-r--r--configure.ac39
-rw-r--r--src/common/socket.c170
4 files changed, 278 insertions, 6 deletions
diff --git a/conf/packet.conf b/conf/packet.conf
index 3a7ec2c85..1780d1b4c 100644
--- a/conf/packet.conf
+++ b/conf/packet.conf
@@ -10,6 +10,19 @@ debug: no
// How long can a socket stall before closing the connection (in seconds)?
stall_time: 60
+// Linux/Epoll: Maxmimum Events per cycle
+// Default Value:
+// (Maxmimum Supported Connections)/2
+// NOTE: this controls the maximum collected socket-events per-cycle (call to epoll_wait())
+// for example settings this to 32 will allow up to 32 events (incomming data/new connections
+// per server-cycle.
+// NOTE: Recommended Settings is at least half the maxmimum supported connections
+// Settings this to a lower value, may cause lags/delays
+// Depending on available CPU Time
+// NOTE: This Setting is only available on Linux when build using EPoll as event dispatcher!
+//
+//epoll_maxevents: 1024
+
// Maximum allowed size for clients packets in bytes (default: 65535).
// Default Values:
// 24576 (Clients < 20131223)
diff --git a/configure b/configure
index 053dc0df2..eb928cad1 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.ac 1a78266.
+# From configure.ac 7f3fd6d.
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69.
#
@@ -692,6 +692,7 @@ enable_option_checking
enable_manager
enable_packetver
enable_packetver_re
+enable_epoll
with_key1
with_key2
with_key3
@@ -1342,6 +1343,7 @@ Optional Features:
--enable-packetver=ARG Sets the PACKETVER define. (see src/common/mmo.h)
--enable-packetver-re Sets or unsets the PACKETVER_RE define - see
src/common/mmo.h (currently disabled by default)
+ --enable-epoll use epoll(4) on Linux
--enable-debug[=ARG] Compiles extra debug code. (yes by default)
(available options: yes, no, gdb)
--enable-buildbot[=ARG] (available options: yes, no)
@@ -3547,6 +3549,52 @@ fi
#
+# EPOLL (Linux Only)
+#
+# Check whether --enable-epoll was given.
+if test "${enable_epoll+set}" = set; then :
+ enableval=$enable_epoll; enable_epoll=$enableval
+else
+ enable_epoll=auto
+fi
+
+if test x$enable_epoll = xno; then
+ have_linux_epoll=no
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux epoll(4)" >&5
+$as_echo_n "checking for Linux epoll(4)... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #ifndef __linux__
+ #error This is not Linux
+ #endif
+ #include <sys/epoll.h>
+
+int
+main ()
+{
+epoll_create1 (EPOLL_CLOEXEC);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ have_linux_epoll=yes
+else
+ have_linux_epoll=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_linux_epoll" >&5
+$as_echo "$have_linux_epoll" >&6; }
+fi
+if test x$enable_epoll,$have_linux_epoll = xyes,no; then
+ as_fn_error $? "epoll support explicitly enabled but not available" "$LINENO" 5
+fi
+
+
+#
# Obfuscation keys
#
@@ -8134,6 +8182,18 @@ case $enable_packetver_re in
esac
#
+# Epoll
+#
+case $have_linux_epoll in
+ "yes")
+ CPPFLAGS="$CPPFLAGS -DSOCKET_EPOLL"
+ ;;
+ "no")
+ # default value
+ ;;
+esac
+
+#
# Obfuscation keys
#
if test -n "$obfuscationkey1" -a -n "$obfuscationkey2" -a -n "$obfuscationkey3"; then
diff --git a/configure.ac b/configure.ac
index 7b7590813..0e1c6b8bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -143,6 +143,33 @@ AC_ARG_ENABLE(
#
+# Epoll
+#
+AC_ARG_ENABLE([epoll],
+ [AS_HELP_STRING([--enable-epoll],[use epoll(4) on Linux])],
+ [enable_epoll=$enableval], [enable_epoll=auto])
+if test x$enable_epoll = xno; then
+ have_linux_epoll=no
+else
+ AC_MSG_CHECKING([for Linux epoll(4)])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [
+ #ifndef __linux__
+ #error This is not Linux
+ #endif
+ #include <sys/epoll.h>
+ ],
+ [epoll_create1 (EPOLL_CLOEXEC);])],
+ [have_linux_epoll=yes],
+ [have_linux_epoll=no])
+ AC_MSG_RESULT([$have_linux_epoll])
+fi
+if test x$enable_epoll,$have_linux_epoll = xyes,no; then
+ AC_MSG_ERROR([epoll support explicitly enabled but not available])
+fi
+
+
+#
# Obfuscation keys
#
AC_ARG_WITH(
@@ -1139,6 +1166,18 @@ case $enable_packetver_re in
esac
#
+# Epoll
+#
+case $have_linux_epoll in
+ "yes")
+ CPPFLAGS="$CPPFLAGS -DSOCKET_EPOLL"
+ ;;
+ "no")
+ # default value
+ ;;
+esac
+
+#
# Obfuscation keys
#
if test -n "$obfuscationkey1" -a -n "$obfuscationkey2" -a -n "$obfuscationkey3"; then
diff --git a/src/common/socket.c b/src/common/socket.c
index 60759d0bb..b6e73bce5 100644
--- a/src/common/socket.c
+++ b/src/common/socket.c
@@ -38,6 +38,10 @@
#include <stdlib.h>
#include <sys/types.h>
+#ifdef SOCKET_EPOLL
+#include <sys/epoll.h>
+#endif
+
#ifdef WIN32
# include "common/winapi.h"
#else
@@ -251,8 +255,19 @@ char* sErr(int code)
#define MSG_NOSIGNAL 0
#endif
+#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 = NULL;
+
+#endif
+
// Maximum packet size in bytes, which the client is able to handle.
// Larger packets cause a buffer overflow and stack corruption.
#if PACKETVER >= 20131223
@@ -536,9 +551,25 @@ int connect_client(int listen_fd) {
}
#endif
- if( sockt->fd_max <= fd ) sockt->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: New Socket #%d failed to add to epoll event dispatcher: %s\n", fd, error_msg());
+ sClose(fd);
+ return -1;
+ }
+
+#endif
+
+ if( sockt->fd_max <= fd ) sockt->fd_max = fd + 1;
+
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
sockt->session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr);
@@ -586,8 +617,26 @@ int make_listen_bind(uint32 ip, uint16 port)
exit(EXIT_FAILURE);
}
+
+#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(sockt->fd_max <= fd) sockt->fd_max = fd + 1;
- sFD_SET(fd, &readfds);
+
create_session(fd, connect_client, null_send, null_parse);
sockt->session[fd]->client_addr = 0; // just listens
@@ -637,9 +686,26 @@ int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) {
//Now the socket can be made non-blocking. [Skotlex]
sockt->set_nonblocking(fd, 1);
- if (sockt->fd_max <= fd) sockt->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(sockt->fd_max <= fd) sockt->fd_max = fd + 1;
+
create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse);
sockt->session[fd]->client_addr = ntohl(remote_address.sin_addr.s_addr);
@@ -813,8 +879,10 @@ int wfifoset(int fd, size_t len)
int do_sockets(int 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.
@@ -832,6 +900,9 @@ int do_sockets(int next)
}
#endif
+#ifndef SOCKET_EPOLL
+ // Select based Event Dispatcher:
+
// can timeout until the next tick
timeout.tv_sec = next/1000;
timeout.tv_usec = next%1000*1000;
@@ -848,6 +919,20 @@ int do_sockets(int 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
sockt->last_tick = time(NULL);
@@ -859,6 +944,32 @@ int do_sockets(int next)
if( sockt->session[fd] )
sockt->session[fd]->func_recv(fd);
}
+#elif defined(SOCKET_EPOLL)
+ // epoll based selection
+
+ for( i = 0; i < ret; i++ )
+ {
+ struct epoll_event *it = &epevents[i];
+ struct socket_data *sock = sockt->session[ it->data.fd ];
+
+ if(!sock)
+ continue;
+
+ if ((it->events & EPOLLERR) ||
+ (it->events & EPOLLHUP) ||
+ (!(it->events & EPOLLIN)))
+ {
+ // Got Error on this connection
+ sockt->eof( it->data.fd );
+
+ } else if (it->events & EPOLLIN) {
+ // data wainting
+ sock->func_recv( it->data.fd );
+
+ }
+
+ }
+
#else
// otherwise assume that the fd_set is a bit-array and enumerate it in a standard way
for( i = 1; ret && i < sockt->fd_max; ++i )
@@ -1184,7 +1295,15 @@ int socket_config_read(const char* cfgName)
sockt->stall_time = atoi(w2);
if( sockt->stall_time < 3 )
sockt->stall_time = 3;/* a minimum is required to refrain it from killing itself */
+ }
+#ifdef SOCKET_EPOLL
+ else if(!strcmpi(w1, "epoll_maxevents")) {
+ epoll_maxevents = atoi(w2);
+ if(epoll_maxevents < 16){
+ epoll_maxevents = 16; // minimum that seems to be useful
+ }
}
+#endif
#ifndef MINICORE
else if (!strcmpi(w1, "enable_ip_rules")) {
ip_rules = config_switch(w2);
@@ -1255,6 +1374,18 @@ void socket_final(void)
VECTOR_CLEAR(sockt->lan_subnets);
VECTOR_CLEAR(sockt->allowed_ips);
VECTOR_CLEAR(sockt->trusted_ips);
+
+#ifdef SOCKET_EPOLL
+ if(epfd != SOCKET_ERROR){
+ close(epfd);
+ epfd = SOCKET_ERROR;
+ }
+ if(epevents != NULL){
+ aFree(epevents);
+ epevents = NULL;
+ }
+#endif
+
}
/// Closes a socket.
@@ -1264,7 +1395,17 @@ void socket_close(int fd)
return;// invalid
sockt->flush(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 (sockt->session[fd]) delete_session(fd);
@@ -1406,15 +1547,34 @@ void socket_init(void)
// Get initial local ips
sockt->naddr_ = sockt->getips(sockt->addr_,16);
+ socket_config_read(SOCKET_CONF_FILENAME);
+
+#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 = 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
CREATE(sockt->session, struct socket_data *, FD_SETSIZE);
- socket_config_read(SOCKET_CONF_FILENAME);
-
// initialize last send-receive tick
sockt->last_tick = time(NULL);