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 );