epoll patch (rev 5)

Mike McCormack mike at codeweavers.com
Fri Sep 10 02:21:40 CDT 2004


This patch uses epoll in the wineserver if it is available.  The only 
remaining issue I'm aware of is that we may want to dynamically allocate 
the struct epoll_event array in epoll_loop to make sure we can always 
receive events for all fds.

If anybody has any more comments on this patch or test results, let me know.

The epoll library is available from
http://www.xmailserver.org/linux-patches/nio-improve.html

Mike


ChangeLog:
Shachar Shemesh <wine-devel at shemesh.biz>
Mike McCormack <mike at codeweavers.com>
* use epoll in the server if it is available

-------------- next part --------------
Index: server/fd.c
===================================================================
RCS file: /home/wine/wine/server/fd.c,v
retrieving revision 1.25
diff -u -r1.25 fd.c
--- server/fd.c	9 Sep 2004 00:28:36 -0000	1.25
+++ server/fd.c	10 Sep 2004 05:38:00 -0000
@@ -2,6 +2,8 @@
  * Server-side file descriptor management
  *
  * Copyright (C) 2000, 2003 Alexandre Julliard
+ * Copyright (C) 2004 Shachar Shemesh for Lingnu Open Source Consulting ltd.
+ * Copyright (C) 2004 Mike McCormack
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,6 +35,10 @@
 #ifdef HAVE_SYS_POLL_H
 #include <sys/poll.h>
 #endif
+#include <stdint.h>
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -227,6 +233,103 @@
 
 
 /****************************************************************/
+/* epoll support */
+
+#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_LIBEPOLL)
+
+#define EPOLL_SIZE 1024
+#define MAX_EVENTS 10
+
+static int epoll_fd;
+
+/* this should optimize away if the poll constants are the same as the epoll ones */
+static inline int epoll_events( int events )
+{
+    int r = 0;
+
+    if ((POLLIN == EPOLLIN) && (POLLOUT == EPOLLOUT) &&
+        (POLLERR == EPOLLERR) && (POLLHUP == EPOLLHUP))
+        return events;
+    if (POLLIN&events) r |= EPOLLIN;
+    if (POLLOUT&events) r |= EPOLLOUT;
+    if (POLLERR&events) r |= EPOLLERR;
+    if (POLLHUP&events) r |= EPOLLHUP;
+    return r;
+}
+
+static inline void do_epoll_add( int fd, int events, unsigned int user )
+{
+    struct epoll_event eev;
+    int r;
+
+    if (epoll_fd < 0)
+        return;
+    if (fd < 0)
+        return;
+    if (!events)
+        return;
+    eev.events = events;
+    eev.data.u32 = user;
+    r = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &eev );
+    assert( 0 == r );
+}
+
+static inline void do_epoll_mod( int fd, int events, unsigned int user )
+{
+    struct epoll_event eev;
+    int r;
+
+    if (epoll_fd < 0)
+        return;
+    if (fd < 0)
+        return;
+    eev.events = events;
+    eev.data.u32 = user;
+    r = epoll_ctl( epoll_fd, EPOLL_CTL_MOD, fd, &eev );
+    assert( 0 == r );
+}
+
+static inline void do_epoll_remove( int fd, int old_events, unsigned int user )
+{
+    struct epoll_event eev;
+    int r;
+
+    if (epoll_fd < 0)
+        return;
+    if (fd < 0)
+        return;
+    if (!old_events)
+        return;
+    r = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, &eev );
+    assert( 0 == r );
+}
+
+void init_fd(void)
+{
+    epoll_fd = epoll_create(EPOLL_SIZE);
+}
+
+#else
+
+static inline void do_epoll_add( int fd, int events, unsigned int user )
+{
+}
+
+static inline void do_epoll_mod( int fd, int events, unsigned int user )
+{
+}
+
+static inline void do_epoll_remove( int fd, int old_events, unsigned int user )
+{
+}
+
+void init_fd(void)
+{
+}
+
+#endif
+
+/****************************************************************/
 /* poll support */
 
 static struct fd **poll_users;              /* users array */
@@ -280,6 +383,7 @@
 {
     assert( user >= 0 );
     assert( poll_users[user] == fd );
+    do_epoll_remove( pollfd[user].fd, pollfd[user].events, user );
     pollfd[user].fd = -1;
     pollfd[user].events = 0;
     pollfd[user].revents = 0;
@@ -288,56 +392,111 @@
     active_users--;
 }
 
+static inline int next_delta()
+{
+    long diff = -1;
+    struct list expired_list, *ptr;
+    struct timeval now;
 
-/* server main poll() loop */
-void main_loop(void)
+    if (list_empty(&timeout_list))
+        return diff;
+
+    gettimeofday( &now, NULL );
+
+    /* first remove all expired timers from the list */
+
+    list_init( &expired_list );
+    while ((ptr = list_head( &timeout_list )) != NULL)
+    {
+        struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
+
+        if (!time_before( &now, &timeout->when ))
+        {
+            list_remove( &timeout->entry );
+            list_add_tail( &expired_list, &timeout->entry );
+        }
+        else break;
+    }
+
+    /* now call the callback for all the removed timers */
+
+    while ((ptr = list_head( &expired_list )) != NULL)
+    {
+        struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
+        list_remove( &timeout->entry );
+        timeout->callback( timeout->private );
+        free( timeout );
+    }
+
+    if (!active_users) return diff;  /* last user removed by a timeout */
+
+    if ((ptr = list_head( &timeout_list )) != NULL)
+    {
+        struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
+        diff = (timeout->when.tv_sec - now.tv_sec) * 1000
+             + (timeout->when.tv_usec - now.tv_usec) / 1000;
+        if (diff < 0) diff = 0;
+    }
+
+    return diff;
+}
+
+#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_LIBEPOLL)
+
+/* server main poll() loop using epoll */
+static int epoll_loop(void)
 {
-    int ret;
+    struct epoll_event ev[MAX_EVENTS];
+
+    if( epoll_fd < 0 )
+        return 0;
 
     while (active_users)
     {
-        long diff = -1;
-        if (!list_empty( &timeout_list ))
+        int ret, i, user, diff = next_delta();
+        if (!active_users) break;  /* last user removed by a timeout */
+
+        ret = epoll_wait( epoll_fd, &ev[0], MAX_EVENTS, diff );
+
+        /* put the events into the pollfd array first, like select does */
+        for (i = 0; i < ret; i++)
         {
-            struct list expired_list, *ptr;
-            struct timeval now;
-            gettimeofday( &now, NULL );
+            user = ev[i].data.u32;
+            assert( user < nb_users );
+            pollfd[user].revents = ev[i].events;
+        }
 
-            /* first remove all expired timers from the list */
+        /* read events from the pollfd array, as set_fd_events may modify them */
+        for (i = 0; i < ret; i++)
+        {
+            user = ev[i].data.u32;
+            if (pollfd[user].revents)
+                fd_poll_event( poll_users[user], pollfd[user].revents );
+        }
+    }
+    close( epoll_fd );
 
-            list_init( &expired_list );
-            while ((ptr = list_head( &timeout_list )) != NULL)
-            {
-                struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
+    return 1;
+}
 
-                if (!time_before( &now, &timeout->when ))
-                {
-                    list_remove( &timeout->entry );
-                    list_add_tail( &expired_list, &timeout->entry );
-                }
-                else break;
-            }
+#else
 
-            /* now call the callback for all the removed timers */
+static int epoll_loop(void)
+{
+    return 0;
+}
 
-            while ((ptr = list_head( &expired_list )) != NULL)
-            {
-                struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
-                list_remove( &timeout->entry );
-                timeout->callback( timeout->private );
-                free( timeout );
-            }
+#endif
 
-            if (!active_users) break;  /* last user removed by a timeout */
+/* server main poll() loop using select */
+static void select_loop(void)
+{
+    int ret;
 
-            if ((ptr = list_head( &timeout_list )) != NULL)
-            {
-                struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry );
-                diff = (timeout->when.tv_sec - now.tv_sec) * 1000
-                     + (timeout->when.tv_usec - now.tv_usec) / 1000;
-                if (diff < 0) diff = 0;
-            }
-        }
+    while (active_users)
+    {
+        int diff = next_delta();
+        if (!active_users) break;  /* last user removed by a timeout */
 
         ret = poll( pollfd, nb_users, diff );
         if (ret > 0)
@@ -355,6 +514,12 @@
     }
 }
 
+void main_loop(void)
+{
+    if (epoll_loop())
+        return;
+    select_loop();
+}
 
 /****************************************************************/
 /* inode functions */
@@ -837,12 +1002,24 @@
     assert( poll_users[user] == fd );
     if (events == -1)  /* stop waiting on this fd completely */
     {
+        do_epoll_remove( pollfd[user].fd, pollfd[user].events, user );
         pollfd[user].fd = -1;
         pollfd[user].events = POLLERR;
         pollfd[user].revents = 0;
     }
     else if (pollfd[user].fd != -1 || !pollfd[user].events)
     {
+        /* remove the old fd */
+        if (fd->unix_fd != pollfd[user].fd || !events)
+            do_epoll_remove( pollfd[user].fd, pollfd[user].events, user );
+        if (events)
+        {
+            /* add the new fd or update the old one */
+            if ( pollfd[user].fd == -1 || !pollfd[user].events)
+                do_epoll_add( fd->unix_fd, events, user );
+            else if (pollfd[user].events != events)
+                do_epoll_mod( fd->unix_fd, events, user );
+        }
         pollfd[user].fd = fd->unix_fd;
         pollfd[user].events = events;
     }
Index: server/main.c
===================================================================
RCS file: /home/wine/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	10 Sep 2004 05:38:00 -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 );
Index: server/file.h
===================================================================
RCS file: /home/wine/wine/server/file.h,v
retrieving revision 1.17
diff -u -r1.17 file.h
--- server/file.h	18 Aug 2004 00:04:58 -0000	1.17
+++ server/file.h	10 Sep 2004 05:38:01 -0000
@@ -44,6 +44,7 @@
 
 /* file descriptor functions */
 
+extern void init_fd(void);
 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/Makefile.in
===================================================================
RCS file: /home/wine/wine/server/Makefile.in,v
retrieving revision 1.51
diff -u -r1.51 Makefile.in
--- server/Makefile.in	23 Jun 2004 20:44:58 -0000	1.51
+++ server/Makefile.in	10 Sep 2004 05:38:01 -0000
@@ -5,6 +5,8 @@
 VPATH     = @srcdir@
 MODULE    = none
 
+LIBEPOLL  = @LIBEPOLL@
+
 C_SRCS = \
 	async.c \
 	atom.c \
@@ -52,7 +54,7 @@
 @MAKE_RULES@
 
 wineserver: $(OBJS)
-	$(CC) -o $(PROGRAMS) $(OBJS) $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS) $(LIBS)
+	$(CC) -o $(PROGRAMS) $(OBJS) $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(LIBEPOLL)
 
 install:: $(PROGRAMS)
 	$(MKINSTALLDIRS) $(bindir)
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.307
diff -u -r1.307 configure.ac
--- configure.ac	7 Sep 2004 22:44:34 -0000	1.307
+++ configure.ac	10 Sep 2004 05:38:01 -0000
@@ -132,6 +132,10 @@
 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,
+              [AC_SUBST(LIBEPOLL,-lepoll)
+               AC_DEFINE(HAVE_LIBEPOLL)])
 dnl Check for -lresolv for Mac OS X/Darwin
 AC_CHECK_LIB(resolv,res_9_init)
 dnl Check for -lpthread
@@ -1146,6 +1150,7 @@
 	sys/cdio.h \
 	sys/elf32.h \
 	sys/errno.h \
+	sys/epoll.h \
 	sys/exec_elf.h \
 	sys/file.h \
 	sys/filio.h \
Index: include/config.h.in
===================================================================
RCS file: /home/wine/wine/include/config.h.in,v
retrieving revision 1.195
diff -u -r1.195 config.h.in
--- include/config.h.in	7 Sep 2004 22:44:34 -0000	1.195
+++ include/config.h.in	10 Sep 2004 05:38:01 -0000
@@ -248,6 +248,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 to 1 if you have the `i386' library (-li386). */
 #undef HAVE_LIBI386
 
@@ -613,6 +616,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


More information about the wine-patches mailing list