Shachar Shemesh wrote:
After a discussion with Alexandre on IRC I came to the conclusion that nothing I will do will make this patch go in. Mike, please add it to your list of "uncommitted patches".
Hi Shachar,
I'm not sure which Mike the above is addressed at... :)
I agree epoll is something worth persuing, so I had a go at refining your patch in accordance with what Alexandre recommended (quoted below). Here's what I came up with... it's not complete as yet, but hopefully it's more conformant with Alexandre's wishes ...
Notes: * used direct syscalls as my libc doesn't have epoll_* functions * fixed array of epoll_event structures for starters * do_epoll_remove assert commented as it occasionally triggers * cheated by not declaring init_fd() in a header
It works for a quick test, but I'm sure everybody can find problems with it...
Mike
It needs some cleaning up, you need to find a way to make fewer changes to the poll() side of things, also the epoll stuff should be separated enough that you can put it inside #ifdef instead of having dummy declarations of the epoll functions. Also try to better follow the coding conventions of the rest of the file, for instance use underscores in variable names, get rid of some useless asserts, etc.
? server/mailslot.c Index: server/fd.c =================================================================== RCS file: /home/wine/wine/server/fd.c,v retrieving revision 1.24 diff -u -r1.24 fd.c --- server/fd.c 8 Sep 2004 04:17:31 -0000 1.24 +++ server/fd.c 8 Sep 2004 16:09:18 -0000 @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#define USE_EPOLL
#include "config.h"
@@ -236,6 +237,122 @@ 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 + +#define EPOLL_SIZE 1024 +#define MAX_EVENTS 10 + +static int epoll_fd; + +enum EPOLL_EVENTS +{ + EPOLLIN = 0x001, + EPOLLPRI = 0x002, + EPOLLOUT = 0x004, + EPOLLERR = 0x008, + EPOLLHUP = 0x010 +}; + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +#ifndef __NR_epoll_create +#define __NR_epoll_create 254 +#endif + +#ifndef __NR_epoll_ctl +#define __NR_epoll_ctl 255 +#endif + +#ifndef __NR_epoll_wait +#define __NR_epoll_wait 256 +#endif + +typedef union epoll_data { + void *ptr; + int fd; + __uint32_t u32; + __uint64_t u64; +} epoll_data_t; + +struct epoll_event { + __uint32_t events; + epoll_data_t data; +}; + +static inline int sys_epoll_create( int size ) +{ + return syscall(__NR_epoll_create, size); +} + +static inline int sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + return syscall(__NR_epoll_ctl, epfd, op, fd, event); +} + +static inline int sys_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) +{ + return syscall(__NR_epoll_wait, epfd, events, maxevents, timeout); +} + +#define epoll_create sys_epoll_create +#define epoll_ctl sys_epoll_ctl +#define epoll_wait sys_epoll_wait + +static inline void do_epoll_add( int fd, int events, 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_remove( int fd ) +{ + struct epoll_event eev; + int r; + + if( epoll_fd < 0 ) + return; + if( fd < 0 ) + return; + r = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, &eev ); + + /* get an assert here sometimes removing an fd we never added */ + /* assert( 0 == r ); */ +} + +void init_fd() +{ + epoll_fd = epoll_create(EPOLL_SIZE); +} + +#else + +static inline void do_epoll_add( int fd, int events, int user ) +{ +} + +static inline void do_epoll_remove( int fd ) +{ +} + +void init_fd() +{ +} + +#endif + /* add a user in the poll array and return its index, or -1 on failure */ static int add_poll_user( struct fd *fd ) { @@ -280,6 +397,7 @@ { assert( user >= 0 ); assert( poll_users[user] == fd ); + do_epoll_remove( pollfd[user].fd ); pollfd[user].fd = -1; pollfd[user].events = 0; pollfd[user].revents = 0; @@ -288,56 +406,92 @@ active_users--; }
- -/* server main poll() loop */ -void main_loop(void) +static inline int next_delta() { - int ret; + long diff = -1; + struct list expired_list, *ptr; + struct timeval now;
- while (active_users) + 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) { - long diff = -1; - if (!list_empty( &timeout_list )) + struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry ); + + if (!time_before( &now, &timeout->when )) { - struct list expired_list, *ptr; - struct timeval now; - gettimeofday( &now, NULL ); + list_remove( &timeout->entry ); + list_add_tail( &expired_list, &timeout->entry ); + } + else break; + }
- /* first remove all expired timers from the list */ + /* now call the callback for all the removed timers */
- list_init( &expired_list ); - while ((ptr = list_head( &timeout_list )) != NULL) - { - struct timeout_user *timeout = LIST_ENTRY( ptr, struct timeout_user, entry ); + 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 (!time_before( &now, &timeout->when )) - { - list_remove( &timeout->entry ); - list_add_tail( &expired_list, &timeout->entry ); - } - else break; - } + if (!active_users) return diff; /* last user removed by a timeout */
- /* now call the callback for all the removed timers */ + 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 ((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 ); - } + return diff; +}
- if (!active_users) break; /* last user removed by a timeout */ +#ifdef USE_EPOLL
- 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; - } +/* server main poll() loop using epoll */ +static void epoll_loop(void) +{ + int ret, i; + struct epoll_event ev[MAX_EVENTS]; + + assert(POLLIN == EPOLLIN); + assert(POLLOUT == EPOLLOUT); + assert(POLLERR == EPOLLERR); + assert(POLLHUP == EPOLLHUP); + + while (active_users) + { + int diff = next_delta(); + if (!active_users) break; /* last user removed by a timeout */ + + ret = epoll_wait( epoll_fd, &ev[0], MAX_EVENTS, diff ); + for( i=0; i<ret; i++ ) + { + assert( ev[i].data.u32 < nb_users ); + fd_poll_event( poll_users[ev[i].data.u32], ev[i].events ); } + } +} +#endif + +/* server main poll() loop using select */ +static void select_loop(void) +{ + int ret; + + 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 +509,19 @@ } }
+void main_loop(void) +{ +#ifdef USE_EPOLL + if( epoll_fd >= 0 ) + { + fprintf(stderr,"epoll enabled\n"); + epoll_loop(); + close( epoll_fd ); + return; + } +#endif + select_loop(); +}
/****************************************************************/ /* inode functions */ @@ -837,14 +1004,19 @@ assert( poll_users[user] == fd ); if (events == -1) /* stop waiting on this fd completely */ { + if( pollfd[user].events ) + do_epoll_remove( pollfd[user].fd ); pollfd[user].fd = -1; pollfd[user].events = POLLERR; pollfd[user].revents = 0; } else if (pollfd[user].fd != -1 || !pollfd[user].events) { + if( pollfd[user].events ) + do_epoll_remove( pollfd[user].fd ); pollfd[user].fd = fd->unix_fd; pollfd[user].events = events; + do_epoll_add( pollfd[user].fd, pollfd[user].events, user ); } }
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 8 Sep 2004 16:09:18 -0000 @@ -110,6 +110,7 @@ exit(1); /* make sure atexit functions get called */ }
+void init_fd(); int main( int argc, char *argv[] ) { parse_args( argc, argv ); @@ -122,6 +123,7 @@ signal( SIGTERM, sigterm_handler ); signal( SIGABRT, sigterm_handler );
+ init_fd(); sock_init(); open_master_socket(); sync_namespace = create_namespace( 37, TRUE );