From 969f57c24a9ab21ea9f02811d95141716e2f679a Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Thu, 23 Sep 2004 04:48:24 +0000
Subject: [PATCH] Added support for epoll() as an alternative to poll() (based
 on the work of Shachar Shemesh and Mike McCormack).

---
 configure           |   4 ++
 configure.ac        |   2 +
 include/config.h.in |   6 +++
 server/fd.c         | 118 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+)

diff --git a/configure b/configure
index f2ea42ef1d3..b09971a57b6 100755
--- a/configure
+++ b/configure
@@ -16275,6 +16275,7 @@ fi
 
 
 
+
 
 
 for ac_func in \
@@ -16289,6 +16290,7 @@ for ac_func in \
 	_vsnprintf \
 	chsize \
 	clone \
+	epoll_create \
 	finite \
 	fpclass \
 	fstatfs \
@@ -16523,6 +16525,7 @@ done
 
 
 
+
 
 
 for ac_header in \
@@ -16575,6 +16578,7 @@ for ac_header in \
 	sys/cdio.h \
 	sys/elf32.h \
 	sys/errno.h \
+	sys/epoll.h \
 	sys/exec_elf.h \
 	sys/file.h \
 	sys/filio.h \
diff --git a/configure.ac b/configure.ac
index aba18168a37..4f16aac18c7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1065,6 +1065,7 @@ AC_CHECK_FUNCS(\
 	_vsnprintf \
 	chsize \
 	clone \
+	epoll_create \
 	finite \
 	fpclass \
 	fstatfs \
@@ -1165,6 +1166,7 @@ AC_CHECK_HEADERS(\
 	sys/cdio.h \
 	sys/elf32.h \
 	sys/errno.h \
+	sys/epoll.h \
 	sys/exec_elf.h \
 	sys/file.h \
 	sys/filio.h \
diff --git a/include/config.h.in b/include/config.h.in
index 694f1527dc9..1a590869f2c 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -80,6 +80,9 @@
 /* Define to 1 if you have the <elf.h> header file. */
 #undef HAVE_ELF_H
 
+/* Define to 1 if you have the `epoll_create' function. */
+#undef HAVE_EPOLL_CREATE
+
 /* Define to 1 if you have the `finite' function. */
 #undef HAVE_FINITE
 
@@ -617,6 +620,9 @@
 /* Define to 1 if you have the <sys/elf32.h> header file. */
 #undef HAVE_SYS_ELF32_H
 
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
 /* Define to 1 if you have the <sys/errno.h> header file. */
 #undef HAVE_SYS_ERRNO_H
 
diff --git a/server/fd.c b/server/fd.c
index 87c530f4b15..534867002b3 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -33,6 +33,12 @@
 #ifdef HAVE_SYS_POLL_H
 #include <sys/poll.h>
 #endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -48,6 +54,10 @@
 #include "winreg.h"
 #include "winternl.h"
 
+#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL_CREATE)
+# define USE_EPOLL
+#endif
+
 /* Because of the stupid Posix locking semantics, we need to keep
  * track of all file descriptors referencing a given file, and not
  * close a single one until all the locks are gone (sigh).
@@ -236,6 +246,58 @@ static int active_users;                    /* current number of active users */
 static int allocated_users;                 /* count of allocated entries in the array */
 static struct fd **freelist;                /* list of free entries in the array */
 
+#ifdef USE_EPOLL
+
+static int epoll_fd;
+static struct epoll_event *epoll_events;
+
+/* set the events that epoll waits for on this fd; helper for set_fd_events */
+static inline void set_fd_epoll_events( struct fd *fd, int user, int events )
+{
+    struct epoll_event ev;
+    int ctl;
+
+    if (epoll_fd == -1) return;
+
+    if (events == -1)  /* stop waiting on this fd completely */
+    {
+        if (pollfd[user].fd == -1) return;  /* already removed */
+        ctl = EPOLL_CTL_DEL;
+    }
+    else if (pollfd[user].fd == -1)
+    {
+        if (pollfd[user].events) return;  /* stopped waiting on it, don't restart */
+        ctl = EPOLL_CTL_ADD;
+    }
+    else
+    {
+        if (pollfd[user].events == events) return;  /* nothing to do */
+        ctl = EPOLL_CTL_MOD;
+    }
+
+    ev.events = events;
+    ev.data.u32 = user;
+
+    if (epoll_ctl( epoll_fd, ctl, fd->unix_fd, &ev ) == -1)
+    {
+        if (errno == ENOMEM)  /* not enough memory, give up on epoll */
+        {
+            close( epoll_fd );
+            epoll_fd = -1;
+        }
+        else perror( "epoll_ctl" );  /* should not happen */
+    }
+}
+
+#else /* USE_EPOLL */
+
+static inline void set_fd_epoll_events( struct fd *fd, int user, int events )
+{
+}
+
+#endif /* USE_EPOLL */
+
+
 /* add a user in the poll array and return its index, or -1 on failure */
 static int add_poll_user( struct fd *fd )
 {
@@ -263,6 +325,16 @@ static int add_poll_user( struct fd *fd )
             }
             poll_users = newusers;
             pollfd = newpoll;
+#ifdef USE_EPOLL
+            if (!allocated_users) epoll_fd = epoll_create( new_count );
+            if (epoll_fd != -1)
+            {
+                struct epoll_event *new_events;
+                if (!(new_events = realloc( epoll_events, new_count * sizeof(*epoll_events) )))
+                    return -1;
+                epoll_events = new_events;
+            }
+#endif
             allocated_users = new_count;
         }
         ret = nb_users++;
@@ -280,6 +352,14 @@ static void remove_poll_user( struct fd *fd, int user )
 {
     assert( user >= 0 );
     assert( poll_users[user] == fd );
+
+#ifdef USE_EPOLL
+    if (epoll_fd != -1 && pollfd[user].fd != -1)
+    {
+        struct epoll_event dummy;
+        epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd->unix_fd, &dummy );
+    }
+#endif
     pollfd[user].fd = -1;
     pollfd[user].events = 0;
     pollfd[user].revents = 0;
@@ -340,6 +420,41 @@ void main_loop(void)
 {
     int i, ret, timeout;
 
+#ifdef USE_EPOLL
+    assert( POLLIN == EPOLLIN );
+    assert( POLLOUT == EPOLLOUT );
+    assert( POLLERR == EPOLLERR );
+    assert( POLLHUP == EPOLLHUP );
+
+    if (epoll_fd != -1)
+    {
+        while (active_users)
+        {
+            timeout = get_next_timeout();
+
+            if (!active_users) break;  /* last user removed by a timeout */
+            if (epoll_fd == -1) break;  /* an error occurred with epoll */
+
+            ret = epoll_wait( epoll_fd, epoll_events, allocated_users, timeout );
+
+            /* put the events into the pollfd array first, like poll does */
+            for (i = 0; i < ret; i++)
+            {
+                int user = epoll_events[i].data.u32;
+                pollfd[user].revents = epoll_events[i].events;
+            }
+
+            /* read events from the pollfd array, as set_fd_events may modify them */
+            for (i = 0; i < ret; i++)
+            {
+                int user = epoll_events[i].data.u32;
+                if (pollfd[user].revents) fd_poll_event( poll_users[user], pollfd[user].revents );
+            }
+        }
+    }
+    /* fall through to normal poll loop */
+#endif  /* USE_EPOLL */
+
     while (active_users)
     {
         timeout = get_next_timeout();
@@ -841,6 +956,9 @@ void set_fd_events( struct fd *fd, int events )
 {
     int user = fd->poll_index;
     assert( poll_users[user] == fd );
+
+    set_fd_epoll_events( fd, user, events );
+
     if (events == -1)  /* stop waiting on this fd completely */
     {
         pollfd[user].fd = -1;
-- 
GitLab