Non-perfect epoll patch

Shachar Shemesh wine-devel at shemesh.biz
Tue Aug 24 16:36:39 CDT 2004


Hi all,

Attached is a non-perfect patch for review. This is a migration of the 
wineserver to use epoll instead of poll (if it's available).

current known issue with this patch:
1. Will not compile if HAVE_SYS_EPOLL_H is not 1 (i.e. - won't compile 
if epoll not available at compile time)
2. Segfaults on wine exit.
3. Lots of debug asserts.

Comments welcome.
             Shachar

-- 
Shachar Shemesh
Lingnu Open Source Consulting ltd.
http://www.lingnu.com/

-------------- next part --------------
Index: configure
===================================================================
RCS file: /home/sun/sources/cvs/wine/configure,v
retrieving revision 1.573
diff -u -r1.573 configure
--- configure	17 Jul 2004 00:52:37 -0000	1.573
+++ configure	24 Aug 2004 12:03:32 -0000
@@ -6727,6 +6727,79 @@
 fi
 
 
+echo "$as_me:$LINENO: checking for epoll_create in -lepoll" >&5
+echo $ECHO_N "checking for epoll_create in -lepoll... $ECHO_C" >&6
+if test "${ac_cv_lib_epoll_epoll_create+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lepoll  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char epoll_create ();
+int
+main ()
+{
+epoll_create ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_epoll_epoll_create=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_epoll_epoll_create=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_epoll_epoll_create" >&5
+echo "${ECHO_T}$ac_cv_lib_epoll_epoll_create" >&6
+if test $ac_cv_lib_epoll_epoll_create = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBEPOLL 1
+_ACEOF
+
+  LIBS="-lepoll $LIBS"
+
+fi
+
+
 echo "$as_me:$LINENO: checking for res_9_init in -lresolv" >&5
 echo $ECHO_N "checking for res_9_init in -lresolv... $ECHO_C" >&6
 if test "${ac_cv_lib_resolv_res_9_init+set}" = set; then
@@ -16190,6 +16263,7 @@
 
 
 
+
 for ac_func in \
 	_lwp_create \
 	_lwp_self \
@@ -16202,6 +16276,7 @@
 	_vsnprintf \
 	chsize \
 	clone \
+        epoll_create \
 	finite \
 	fpclass \
 	fstatfs \
@@ -16435,6 +16510,7 @@
 
 
 
+
 for ac_header in \
 	arpa/inet.h \
 	arpa/nameser.h \
@@ -16495,6 +16571,7 @@
 	sys/msg.h \
 	sys/param.h \
 	sys/poll.h \
+	sys/epoll.h \
 	sys/ptrace.h \
 	sys/reg.h \
 	sys/scsiio.h \
Index: configure.ac
===================================================================
RCS file: /home/sun/sources/cvs/wine/configure.ac,v
retrieving revision 1.285
diff -u -r1.285 configure.ac
--- configure.ac	6 Jul 2004 21:01:19 -0000	1.285
+++ configure.ac	24 Aug 2004 12:01:22 -0000
@@ -140,6 +140,8 @@
 AC_CHECK_LIB(xpg4,_xpg4_setrunelocale)
 dnl Check for -lpoll for Mac OS X/Darwin
 AC_CHECK_LIB(poll,poll)
+dnl Check for -lepoll
+AC_CHECK_LIB(epoll,epoll_create)
 dnl Check for -lresolv for Mac OS X/Darwin
 AC_CHECK_LIB(resolv,res_9_init)
 dnl Check for -lpthread
@@ -1036,6 +1038,7 @@
 	_vsnprintf \
 	chsize \
 	clone \
+        epoll_create \
 	finite \
 	fpclass \
 	fstatfs \
@@ -1146,6 +1149,7 @@
 	sys/msg.h \
 	sys/param.h \
 	sys/poll.h \
+	sys/epoll.h \
 	sys/ptrace.h \
 	sys/reg.h \
 	sys/scsiio.h \
Index: include/config.h.in
===================================================================
RCS file: /home/sun/sources/cvs/wine/include/config.h.in,v
retrieving revision 1.190
diff -u -r1.190 config.h.in
--- include/config.h.in	18 Jun 2004 19:36:26 -0000	1.190
+++ include/config.h.in	24 Aug 2004 12:05:42 -0000
@@ -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
 
@@ -248,6 +251,9 @@
 /* Define if you have the curses library (-lcurses) */
 #undef HAVE_LIBCURSES
 
+/* Define to 1 if you have the `epoll' library (-lepoll). */
+#undef HAVE_LIBEPOLL
+
 /* Define if you have libgif/libungif including devel headers */
 #undef HAVE_LIBGIF
 
@@ -608,6 +614,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
 
Index: server/fd.c
===================================================================
RCS file: /home/sun/sources/cvs/wine/server/fd.c,v
retrieving revision 1.21
diff -u -r1.21 fd.c
--- server/fd.c	22 May 2004 03:15:04 -0000	1.21
+++ server/fd.c	24 Aug 2004 21:20:53 -0000
@@ -33,6 +33,12 @@
 #ifdef HAVE_SYS_POLL_H
 #include <sys/poll.h>
 #endif
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -48,6 +54,9 @@
 #include "winreg.h"
 #include "winternl.h"
 
+#undef NDEBUG
+#include <assert.h>
+
 /* 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).
@@ -255,51 +264,107 @@
 
 
 /****************************************************************/
-/* poll support */
+/* poll and epoll support */
+
+/* Vars used by both */
+static int active_users;                    /* current number of active users */
 
+/* Vars used by poll */
 static struct fd **poll_users;              /* users array */
 static struct pollfd *pollfd;               /* poll fd array */
 static int nb_users;                        /* count of array entries actually in use */
-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 */
 
+/* Vars used by epoll */
+static int epoll_fd=-1;                     /* File descriptor for the epoll */
+static const int EPOLL_SIZE=1024;           /* "Maximal size" passed to epoll_create. This is an optimization, not a real maximum */
+static struct epoll_event *epoll_revents;   /* Array for event results */
+static int epoll_revents_size;              /* Number of elements in epoll_revents */
+
+#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_SYSCALL_H)
+/* To prevent wine from not loading on non-epoll supporting platforms,
+ * we need to dynamically call the epoll functions */
+static inline int _epoll_create( int size )
+{
+    return syscall(__NR_epoll_create, size);
+}
+
+static inline int _epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+    return syscall(__NR_epoll_ctl, epfd, op, fd, event);
+}
+
+static inline int _epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+    return syscall(__NR_epoll_wait, epfd, events, maxevents, timeout);
+}
+#else
+static inline int _epoll_create( int size )
+{
+    errno=ENOSYS;
+    return -1;
+}
+
+static inline int _epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+    errno=ENOSYS;
+    return -1;
+}
+
+static inline int _epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+    errno=ENOSYS;
+    return -1;
+}
+#endif
+
+
 /* add a user in the poll array and return its index, or -1 on failure */
 static int add_poll_user( struct fd *fd )
 {
     int ret;
-    if (freelist)
+    if( epoll_fd>=0 ) /* We are using the epoll interface */
     {
-        ret = freelist - poll_users;
-        freelist = (struct fd **)poll_users[ret];
+        /* No poll array when using epoll - always return 1 */
+        ret=1;
     }
     else
-    {
-        if (nb_users == allocated_users)
+    { /* We are using the poll interface */
+        if (freelist)
         {
-            struct fd **newusers;
-            struct pollfd *newpoll;
-            int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
-            if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
-            if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
+            ret = freelist - poll_users;
+            freelist = (struct fd **)poll_users[ret];
+        }
+        else
+        {
+            if (nb_users == allocated_users)
             {
-                if (allocated_users)
-                    poll_users = newusers;
-                else
-                    free( newusers );
-                return -1;
+                struct fd **newusers;
+                struct pollfd *newpoll;
+                int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
+                if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
+                if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
+                {
+                    if (allocated_users)
+                        poll_users = newusers;
+                    else
+                        free( newusers );
+                    return -1;
+                }
+                poll_users = newusers;
+                pollfd = newpoll;
+                allocated_users = new_count;
             }
-            poll_users = newusers;
-            pollfd = newpoll;
-            allocated_users = new_count;
-        }
-        ret = nb_users++;
-    }
-    pollfd[ret].fd = -1;
-    pollfd[ret].events = 0;
-    pollfd[ret].revents = 0;
-    poll_users[ret] = fd;
+            ret = nb_users++;
+        }
+        pollfd[ret].fd = -1;
+        pollfd[ret].events = 0;
+        pollfd[ret].revents = 0;
+        poll_users[ret] = fd;
+    }
     active_users++;
+
     return ret;
 }
 
@@ -307,15 +372,37 @@
 static void remove_poll_user( struct fd *fd, int user )
 {
     assert( user >= 0 );
-    assert( poll_users[user] == fd );
-    pollfd[user].fd = -1;
-    pollfd[user].events = 0;
-    pollfd[user].revents = 0;
-    poll_users[user] = (struct fd *)freelist;
-    freelist = &poll_users[user];
+    if( epoll_fd>=0 )
+    {
+        if( fd->unix_fd!=-1 )
+        {
+            struct epoll_event event; /* This struct needn't actually contain anything meaningful, as we are merely calling DEL */
+            if(!(_epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd->unix_fd, &event)==0 || errno==ENOENT))
+            {
+                int err;
+                err=errno;
+            }
+        }
+    }
+    else
+    {
+        assert( poll_users[user] == fd );
+        pollfd[user].fd = -1;
+        pollfd[user].events = 0;
+        pollfd[user].revents = 0;
+        poll_users[user] = (struct fd *)freelist;
+        freelist = &poll_users[user];
+    }
     active_users--;
 }
 
+/* initialize the fd related state */
+void init_fd()
+{
+    /* If this function fails, epoll_fd will be -1, which will signal
+     * the rest of the functions to use poll instead */
+    epoll_fd=_epoll_create(EPOLL_SIZE);
+}
 
 /* server main poll() loop */
 void main_loop(void)
@@ -341,16 +428,44 @@
             }
             if (!active_users) break;  /* last user removed by a timeout */
         }
-        ret = poll( pollfd, nb_users, diff );
-        if (ret > 0)
+        if( epoll_fd>=0 )
         {
-            int i;
-            for (i = 0; i < nb_users; i++)
+            /* if the event handlers added a lot of new events, we may want to increase the returned
+             * events space */
+            if( epoll_revents_size<active_users )
             {
-                if (pollfd[i].revents)
+                /* If epoll_revents_size is below active_users, there is a (very slim) potential for starvation.
+                 * It is not, strictly speaking, an error though. As such, if the allocation fails, we just go on
+                 * with the smaller array size. */
+                struct epoll_event *new_revents=malloc(sizeof(struct epoll_event)*active_users);
+                if( new_revents!=NULL )
                 {
-                    fd_poll_event( poll_users[i], pollfd[i].revents );
-                    if (!--ret) break;
+                    free(epoll_revents);
+                    epoll_revents=new_revents;
+                    epoll_revents_size=active_users;
+                }
+            }
+            
+            ret = epoll_wait(epoll_fd, epoll_revents, epoll_revents_size, diff );
+            while( (ret--)>0 )
+            {
+                fd_poll_event((struct fd *)epoll_revents[ret].data.ptr, epoll_revents[ret].events);
+            }
+        }
+        else
+        {
+            /* No epoll support - use poll */
+            ret = poll( pollfd, nb_users, diff );
+            if (ret > 0)
+            {
+                int i;
+                for (i = 0; i < nb_users; i++)
+                {
+                    if (pollfd[i].revents)
+                    {
+                        fd_poll_event( poll_users[i], pollfd[i].revents );
+                        if (!--ret) break;
+                    }
                 }
             }
         }
@@ -835,18 +950,46 @@
 /* set the events that select waits for on this fd */
 void set_fd_events( struct fd *fd, int events )
 {
-    int user = fd->poll_index;
-    assert( poll_users[user] == fd );
-    if (events == -1)  /* stop waiting on this fd completely */
+    if( epoll_fd>=0 )
     {
-        pollfd[user].fd = -1;
-        pollfd[user].events = POLLERR;
-        pollfd[user].revents = 0;
+        struct epoll_event event;
+        event.events=events;
+        event.data.ptr=fd;
+
+        assert(fd->unix_fd>=0);
+
+        if( events != -1 )
+        {
+            int epoll_res;
+            if( (epoll_res=_epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd->unix_fd, &event )!=0) && errno==ENOENT )
+            {
+                /* For one reason or another this file descriptor isn't being watched at the moment. Just add it */
+                assert(_epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd->unix_fd, &event )==0);
+            }
+            else
+                assert(epoll_res==0);
+        }
+        else
+        {
+            /* Stop waiting on this fd completely */
+            assert(_epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd->unix_fd, &event )==0);
+        }
     }
-    else if (pollfd[user].fd != -1 || !pollfd[user].events)
+    else
     {
-        pollfd[user].fd = fd->unix_fd;
-        pollfd[user].events = events;
+        int user = fd->poll_index;
+        assert( poll_users[user] == fd );
+        if (events == -1)  /* stop waiting on this fd completely */
+        {
+            pollfd[user].fd = -1;
+            pollfd[user].events = POLLERR;
+            pollfd[user].revents = 0;
+        }
+        else if (pollfd[user].fd != -1 || !pollfd[user].events)
+        {
+            pollfd[user].fd = fd->unix_fd;
+            pollfd[user].events = events;
+        }
     }
 }
 
Index: server/file.h
===================================================================
RCS file: /home/sun/sources/cvs/wine/server/file.h,v
retrieving revision 1.16
diff -u -r1.16 file.h
--- server/file.h	16 Apr 2004 04:31:35 -0000	1.16
+++ server/file.h	24 Aug 2004 13:34:21 -0000
@@ -44,6 +44,7 @@
 
 /* file descriptor functions */
 
+extern void init_fd();
 extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
 extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
                            unsigned int access, unsigned int sharing, unsigned int options );
Index: server/main.c
===================================================================
RCS file: /home/sun/sources/cvs/wine/server/main.c,v
retrieving revision 1.31
diff -u -r1.31 main.c
--- server/main.c	26 Mar 2003 01:32:18 -0000	1.31
+++ server/main.c	24 Aug 2004 15:49:33 -0000
@@ -122,6 +122,7 @@
     signal( SIGTERM, sigterm_handler );
     signal( SIGABRT, sigterm_handler );
 
+    init_fd();
     sock_init();
     open_master_socket();
     sync_namespace = create_namespace( 37, TRUE );


More information about the wine-devel mailing list