Added add_queue/remove_queue to server object operations. Moved select() loop functions to select.c.
diff --git a/include/server/object.h b/include/server/object.h index a968dda..c00fbc0 100644 --- a/include/server/object.h +++ b/include/server/object.h
@@ -20,13 +20,22 @@ struct object; struct object_name; struct thread; +struct wait_queue_entry; struct object_ops { - void (*dump)(struct object *,int); /* dump the object (for debugging) */ - int (*signaled)(struct object *,struct thread *); /* is object signaled? */ - int (*satisfied)(struct object *,struct thread *); /* wait satisfied; return 1 if abandoned */ - void (*destroy)(struct object *); /* destroy on refcount == 0 */ + /* dump the object (for debugging) */ + void (*dump)(struct object *,int); + /* add a thread to the object wait queue */ + void (*add_queue)(struct object *,struct wait_queue_entry *); + /* remove a thread from the object wait queue */ + void (*remove_queue)(struct object *,struct wait_queue_entry *); + /* is object signaled? */ + int (*signaled)(struct object *,struct thread *); + /* wait satisfied; return 1 if abandoned */ + int (*satisfied)(struct object *,struct thread *); + /* destroy on refcount == 0 */ + void (*destroy)(struct object *); }; struct object @@ -65,8 +74,27 @@ extern void trace_reply( struct thread *thread, int type, int pass_fd, struct iovec *vec, int veclen ); +/* select functions */ + +#define READ_EVENT 1 +#define WRITE_EVENT 2 + +struct select_ops +{ + void (*event)( int fd, int event, void *private ); + void (*timeout)( int fd, void *private ); +}; + +extern int add_select_user( int fd, int events, const struct select_ops *ops, void *private ); +extern void remove_select_user( int fd ); +extern void set_select_timeout( int fd, struct timeval *when ); +extern void set_select_events( int fd, int events ); +extern void *get_select_private_data( const struct select_ops *ops, int fd ); +extern void select_loop(void); + /* socket functions */ +extern void server_init( int fd ); extern int add_client( int client_fd, struct thread *self ); extern void remove_client( int client_fd, int exit_code ); extern int get_initial_client_fd(void);
diff --git a/include/server/thread.h b/include/server/thread.h index c9c373a..9931301 100644 --- a/include/server/thread.h +++ b/include/server/thread.h
@@ -52,6 +52,8 @@ struct get_thread_info_reply *reply ); extern int send_reply( struct thread *thread, int pass_fd, int n, ... /* arg_1, len_1, ..., arg_n, len_n */ ); +extern void add_queue( struct object *obj, struct wait_queue_entry *entry ); +extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); extern void kill_thread( struct thread *thread, int exit_code ); extern void thread_killed( struct thread *thread, int exit_code ); extern void thread_timeout(void);
diff --git a/scheduler/client.c b/scheduler/client.c index 1e7aa44..5f819cf 100644 --- a/scheduler/client.c +++ b/scheduler/client.c
@@ -226,7 +226,8 @@ struct new_thread_reply reply; int len, fd[2]; extern BOOL32 THREAD_InitDone; - extern void server_main_loop( int fd ); + extern void server_init( int fd ); + extern void select_loop(void); if (!THREAD_InitDone) /* first thread -> start the server */ { @@ -251,7 +252,8 @@ execl( "/usr/local/bin/wineserver", "wineserver", buffer, NULL ); execl( "./server/wineserver", "wineserver", buffer, NULL ); #endif - server_main_loop( tmpfd[1] ); + server_init( tmpfd[1] ); + select_loop(); exit(0); default: /* parent */ close( tmpfd[1] );
diff --git a/server/Makefile.in b/server/Makefile.in index 7ec46f8..1dfd55d 100644 --- a/server/Makefile.in +++ b/server/Makefile.in
@@ -11,6 +11,7 @@ object.c \ process.c \ request.c \ + select.c \ semaphore.c \ socket.c \ thread.c \
diff --git a/server/event.c b/server/event.c index ef9dd76..2483002 100644 --- a/server/event.c +++ b/server/event.c
@@ -27,6 +27,8 @@ static const struct object_ops event_ops = { dump_event, + add_queue, + remove_queue, event_signaled, event_satisfied, destroy_event
diff --git a/server/main.c b/server/main.c index d7f06c9..c6afb77 100644 --- a/server/main.c +++ b/server/main.c
@@ -16,7 +16,6 @@ int main( int argc, char *argv[] ) { int fd; - extern void server_main_loop( int fd ); if (argc != 2) goto error; if (!isdigit( *argv[1] )) goto error; @@ -27,7 +26,8 @@ debug_level = 1; if (debug_level) printf( "Server: starting (pid=%d)\n", getpid() ); - server_main_loop( fd ); + server_init( fd ); + select_loop(); if (debug_level) printf( "Server: exiting (pid=%d)\n", getpid() ); exit(0);
diff --git a/server/mutex.c b/server/mutex.c index df5fc9f..aee0771 100644 --- a/server/mutex.c +++ b/server/mutex.c
@@ -30,6 +30,8 @@ static const struct object_ops mutex_ops = { dump_mutex, + add_queue, + remove_queue, mutex_signaled, mutex_satisfied, destroy_mutex
diff --git a/server/process.c b/server/process.c index 3fc73fe..79b3833 100644 --- a/server/process.c +++ b/server/process.c
@@ -64,6 +64,8 @@ static const struct object_ops process_ops = { dump_process, + add_queue, + remove_queue, process_signaled, process_satisfied, destroy_process
diff --git a/server/select.c b/server/select.c new file mode 100644 index 0000000..5d3092e --- /dev/null +++ b/server/select.c
@@ -0,0 +1,192 @@ +/* + * Server main select() loop + * + * Copyright (C) 1998 Alexandre Julliard + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "server/object.h" + +/* select user fd */ +struct user +{ + struct timeval when; /* timeout expiry (absolute time) */ + struct user *next; /* next in sorted timeout list */ + struct user *prev; /* prev in sorted timeout list */ + const struct select_ops *ops; /* user operations list */ + int fd; /* user fd */ + void *private; /* user private data */ +}; + +static struct user *users[FD_SETSIZE]; /* users array */ +static fd_set read_set, write_set; /* current select sets */ +static int nb_users; /* current number of users */ +static int max_fd; /* max fd in use */ +static struct user *timeout_head; /* sorted timeouts list head */ +static struct user *timeout_tail; /* sorted timeouts list tail */ + + +/* add a user */ +int add_select_user( int fd, int events, const struct select_ops *ops, void *private ) +{ + int flags; + struct user *user = malloc( sizeof(*user) ); + if (!user) return -1; + assert( !users[fd] ); + + user->ops = ops; + user->when.tv_sec = 0; + user->when.tv_usec = 0; + user->fd = fd; + user->private = private; + + flags = fcntl( fd, F_GETFL, 0 ); + fcntl( fd, F_SETFL, flags | O_NONBLOCK ); + + users[fd] = user; + set_select_events( fd, events ); + if (fd > max_fd) max_fd = fd; + nb_users++; + return fd; +} + +/* remove a user */ +void remove_select_user( int fd ) +{ + struct user *user = users[fd]; + assert( user ); + + set_select_timeout( fd, 0 ); + set_select_events( fd, 0 ); + users[fd] = NULL; + if (max_fd == fd) while (max_fd && !users[max_fd]) max_fd--; + nb_users--; + free( user ); +} + +/* set a user timeout */ +void set_select_timeout( int fd, struct timeval *when ) +{ + struct user *user = users[fd]; + struct user *pos; + assert( user ); + + if (user->when.tv_sec || user->when.tv_usec) + { + /* there is already a timeout */ + if (user->next) user->next->prev = user->prev; + else timeout_tail = user->prev; + if (user->prev) user->prev->next = user->next; + else timeout_head = user->next; + user->when.tv_sec = user->when.tv_usec = 0; + } + if (!when) return; /* no timeout */ + user->when = *when; + + /* Now insert it in the linked list */ + + for (pos = timeout_head; pos; pos = pos->next) + { + if (pos->when.tv_sec > user->when.tv_sec) break; + if ((pos->when.tv_sec == user->when.tv_sec) && + (pos->when.tv_usec > user->when.tv_usec)) break; + } + + if (pos) /* insert it before 'pos' */ + { + if ((user->prev = pos->prev)) user->prev->next = user; + else timeout_head = user; + user->next = pos; + pos->prev = user; + } + else /* insert it at the tail */ + { + user->next = NULL; + if (timeout_tail) timeout_tail->next = user; + else timeout_head = user; + user->prev = timeout_tail; + timeout_tail = user; + } +} + +/* set the events that select waits for on this fd */ +void set_select_events( int fd, int events ) +{ + if (events & READ_EVENT) FD_SET( fd, &read_set ); + else FD_CLR( fd, &read_set ); + if (events & WRITE_EVENT) FD_SET( fd, &write_set ); + else FD_CLR( fd, &write_set ); +} + +/* get a user private data, checking the type */ +void *get_select_private_data( const struct select_ops *ops, int fd ) +{ + struct user *user = users[fd]; + assert( user ); + assert( user->ops == ops ); + return user->private; +} + +/* server main loop */ +void select_loop(void) +{ + int i, ret; + + setsid(); + signal( SIGPIPE, SIG_IGN ); + + while (nb_users) + { + fd_set read = read_set, write = write_set; +#if 0 + printf( "select: " ); + for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' : + (FD_ISSET( i, &write_set ) ? 'w' : '-') ); + printf( "\n" ); +#endif + if (timeout_head) + { + struct timeval tv, now; + gettimeofday( &now, NULL ); + if ((timeout_head->when.tv_sec < now.tv_sec) || + ((timeout_head->when.tv_sec == now.tv_sec) && + (timeout_head->when.tv_usec < now.tv_usec))) + { + timeout_head->ops->timeout( timeout_head->fd, timeout_head->private ); + continue; + } + tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec; + if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0) + { + tv.tv_usec += 1000000; + tv.tv_sec--; + } + ret = select( max_fd + 1, &read, &write, NULL, &tv ); + } + else /* no timeout */ + { + ret = select( max_fd + 1, &read, &write, NULL, NULL ); + } + + if (!ret) continue; + if (ret == -1) perror("select"); + + for (i = 0; i <= max_fd; i++) + { + int event = 0; + if (FD_ISSET( i, &write )) event |= WRITE_EVENT; + if (FD_ISSET( i, &read )) event |= READ_EVENT; + if (event) users[i]->ops->event( i, event, users[i]->private ); + } + } +}
diff --git a/server/semaphore.c b/server/semaphore.c index 9cdedb9..2753d96 100644 --- a/server/semaphore.c +++ b/server/semaphore.c
@@ -27,6 +27,8 @@ static const struct object_ops semaphore_ops = { dump_semaphore, + add_queue, + remove_queue, semaphore_signaled, semaphore_satisfied, destroy_semaphore
diff --git a/server/socket.c b/server/socket.c index 1302467..8d7cc01 100644 --- a/server/socket.c +++ b/server/socket.c
@@ -6,8 +6,6 @@ #include <assert.h> #include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -37,15 +35,6 @@ READING /* reading our reply */ }; -/* client timeout */ -struct timeout -{ - struct timeval when; /* timeout expiry (absolute time) */ - struct timeout *next; /* next in sorted list */ - struct timeout *prev; /* prev in sorted list */ - int client; /* client id */ -}; - /* client structure */ struct client { @@ -56,17 +45,9 @@ int count; /* bytes sent/received so far */ int pass_fd; /* fd to pass to and from the client */ struct thread *self; /* client thread (opaque pointer) */ - struct timeout timeout; /* client timeout */ }; - -static struct client *clients[FD_SETSIZE]; /* clients array */ -static fd_set read_set, write_set; /* current select sets */ -static int nb_clients; /* current number of clients */ -static int max_fd; /* max fd in use */ static int initial_client_fd; /* fd of the first client */ -static struct timeout *timeout_head; /* sorted timeouts list head */ -static struct timeout *timeout_tail; /* sorted timeouts list tail */ /* exit code passed to remove_client */ #define OUT_OF_MEMORY -1 @@ -85,11 +66,9 @@ va_end( args ); } - /* send a message to a client that is ready to receive something */ -static void do_write( int client_fd ) +static void do_write( struct client *client, int client_fd ) { - struct client *client = clients[client_fd]; struct iovec vec[2]; #ifndef HAVE_MSGHDR_ACCRIGHTS struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, @@ -146,15 +125,13 @@ client->count = 0; client->state = RUNNING; client->seq++; - FD_CLR( client_fd, &write_set ); - FD_SET( client_fd, &read_set ); + set_select_events( client_fd, READ_EVENT ); } /* read a message from a client that has something to say */ -static void do_read( int client_fd ) +static void do_read( struct client *client, int client_fd ) { - struct client *client = clients[client_fd]; struct iovec vec; int pass_fd = -1; #ifdef HAVE_MSGHDR_ACCRIGHTS @@ -254,89 +231,47 @@ } } - /* handle a client timeout */ -static void do_timeout( int client_fd ) +static void client_timeout( int client_fd, void *private ) { - struct client *client = clients[client_fd]; - set_timeout( client_fd, 0 ); /* Remove the timeout */ + struct client *client = (struct client *)private; + set_select_timeout( client_fd, 0 ); /* Remove the timeout */ call_timeout_handler( client->self ); } - -/* server main loop */ -void server_main_loop( int fd ) +/* handle a client event */ +static void client_event( int client_fd, int event, void *private ) { - int i, ret; - - setsid(); - signal( SIGPIPE, SIG_IGN ); - - /* special magic to create the initial thread */ - initial_client_fd = fd; - add_client( initial_client_fd, NULL ); - - while (nb_clients) - { - fd_set read = read_set, write = write_set; -#if 0 - printf( "select: " ); - for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' : - (FD_ISSET( i, &write_set ) ? 'w' : '-') ); - printf( "\n" ); -#endif - if (timeout_head) - { - struct timeval tv, now; - gettimeofday( &now, NULL ); - if ((timeout_head->when.tv_sec < now.tv_sec) || - ((timeout_head->when.tv_sec == now.tv_sec) && - (timeout_head->when.tv_usec < now.tv_usec))) - { - do_timeout( timeout_head->client ); - continue; - } - tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec; - if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0) - { - tv.tv_usec += 1000000; - tv.tv_sec--; - } - ret = select( max_fd + 1, &read, &write, NULL, &tv ); - } - else /* no timeout */ - { - ret = select( max_fd + 1, &read, &write, NULL, NULL ); - } - - if (!ret) continue; - if (ret == -1) perror("select"); - - for (i = 0; i <= max_fd; i++) - { - if (FD_ISSET( i, &write )) - { - if (clients[i]) do_write( i ); - } - else if (FD_ISSET( i, &read )) - { - if (clients[i]) do_read( i ); - } - } - } + struct client *client = (struct client *)private; + if (event & WRITE_EVENT) + do_write( client, client_fd ); + if (event & READ_EVENT) + do_read( client, client_fd ); } +static const struct select_ops client_ops = +{ + client_event, + client_timeout +}; /*******************************************************************/ /* server-side exported functions */ +/* server initialization */ +void server_init( int fd ) +{ + /* special magic to create the initial thread */ + initial_client_fd = fd; + add_client( initial_client_fd, NULL ); +} + + /* add a client */ int add_client( int client_fd, struct thread *self ) { - int flags; struct client *client = malloc( sizeof(*client) ); if (!client) return -1; - assert( !clients[client_fd] ); client->state = RUNNING; client->seq = 0; @@ -346,36 +281,26 @@ client->data = NULL; client->self = self; client->pass_fd = -1; - client->timeout.when.tv_sec = 0; - client->timeout.when.tv_usec = 0; - client->timeout.client = client_fd; - flags = fcntl( client_fd, F_GETFL, 0 ); - fcntl( client_fd, F_SETFL, flags | O_NONBLOCK ); - - clients[client_fd] = client; - FD_SET( client_fd, &read_set ); - if (client_fd > max_fd) max_fd = client_fd; - nb_clients++; + if (add_select_user( client_fd, READ_EVENT, &client_ops, client ) == -1) + { + free( client ); + return -1; + } return client_fd; } /* remove a client */ void remove_client( int client_fd, int exit_code ) { - struct client *client = clients[client_fd]; + struct client *client = (struct client *)get_select_private_data( &client_ops, client_fd ); assert( client ); call_kill_handler( client->self, exit_code ); - set_timeout( client_fd, 0 ); - clients[client_fd] = NULL; - FD_CLR( client_fd, &read_set ); - FD_CLR( client_fd, &write_set ); - if (max_fd == client_fd) while (max_fd && !clients[max_fd]) max_fd--; + remove_select_user( client_fd ); if (initial_client_fd == client_fd) initial_client_fd = -1; close( client_fd ); - nb_clients--; /* Purge messages */ if (client->data) free( client->data ); @@ -390,53 +315,6 @@ return initial_client_fd; } -/* set a client timeout */ -void set_timeout( int client_fd, struct timeval *when ) -{ - struct timeout *tm, *pos; - struct client *client = clients[client_fd]; - assert( client ); - - tm = &client->timeout; - if (tm->when.tv_sec || tm->when.tv_usec) - { - /* there is already a timeout */ - if (tm->next) tm->next->prev = tm->prev; - else timeout_tail = tm->prev; - if (tm->prev) tm->prev->next = tm->next; - else timeout_head = tm->next; - tm->when.tv_sec = tm->when.tv_usec = 0; - } - if (!when) return; /* no timeout */ - tm->when = *when; - - /* Now insert it in the linked list */ - - for (pos = timeout_head; pos; pos = pos->next) - { - if (pos->when.tv_sec > tm->when.tv_sec) break; - if ((pos->when.tv_sec == tm->when.tv_sec) && - (pos->when.tv_usec > tm->when.tv_usec)) break; - } - - if (pos) /* insert it before 'pos' */ - { - if ((tm->prev = pos->prev)) tm->prev->next = tm; - else timeout_head = tm; - tm->next = pos; - pos->prev = tm; - } - else /* insert it at the tail */ - { - tm->next = NULL; - if (timeout_tail) timeout_tail->next = tm; - else timeout_head = tm; - tm->prev = timeout_tail; - timeout_tail = tm; - } -} - - /* send a reply to a client */ int send_reply_v( int client_fd, int type, int pass_fd, struct iovec *vec, int veclen ) @@ -444,7 +322,7 @@ int i; unsigned int len; char *p; - struct client *client = clients[client_fd]; + struct client *client = (struct client *)get_select_private_data( &client_ops, client_fd ); assert( client ); assert( client->state == WAITING ); @@ -469,7 +347,6 @@ } client->state = READING; - FD_CLR( client_fd, &read_set ); - FD_SET( client_fd, &write_set ); + set_select_events( client_fd, WRITE_EVENT ); return 0; }
diff --git a/server/thread.c b/server/thread.c index fff2079..0be86a2 100644 --- a/server/thread.c +++ b/server/thread.c
@@ -49,6 +49,8 @@ static const struct object_ops thread_ops = { dump_thread, + add_queue, + remove_queue, thread_signaled, thread_satisfied, destroy_thread @@ -200,8 +202,9 @@ } /* add a thread to an object wait queue; return 1 if OK, 0 on error */ -static void add_queue( struct object *obj, struct wait_queue_entry *entry ) +void add_queue( struct object *obj, struct wait_queue_entry *entry ) { + grab_object( obj ); entry->obj = obj; entry->prev = obj->tail; entry->next = NULL; @@ -211,10 +214,8 @@ } /* remove a thread from an object wait queue */ -static void remove_queue( struct wait_queue_entry *entry ) +void remove_queue( struct object *obj, struct wait_queue_entry *entry ) { - struct object *obj = entry->obj; - if (entry->next) entry->next->prev = entry->prev; else obj->tail = entry->prev; if (entry->prev) entry->prev->next = entry->next; @@ -230,9 +231,9 @@ int i; assert( wait ); - for (i = 0, entry = wait->queues; i < wait->count; i++) - remove_queue( entry++ ); - if (wait->flags & SELECT_TIMEOUT) set_timeout( thread->client_fd, NULL ); + for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) + entry->obj->ops->remove_queue( entry->obj, entry ); + if (wait->flags & SELECT_TIMEOUT) set_select_timeout( thread->client_fd, NULL ); free( wait ); thread->wait = NULL; } @@ -280,7 +281,8 @@ return 0; } entry->thread = thread; - add_queue( obj, entry ); + obj->ops->add_queue( obj, entry ); + release_object( obj ); } return 1; } @@ -342,7 +344,7 @@ { /* we need to wait */ if (flags & SELECT_TIMEOUT) - set_timeout( thread->client_fd, &thread->wait->timeout ); + set_select_timeout( thread->client_fd, &thread->wait->timeout ); return; } end_wait( thread );