Changed select interface, separated timeouts from file descriptors.
diff --git a/server/select.c b/server/select.c
index 013ba71..645f02a 100644
--- a/server/select.c
+++ b/server/select.c
@@ -17,81 +17,82 @@
#include "object.h"
-/* select user fd */
-struct user
+struct timeout_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 */
+ struct timeout_user *next; /* next in sorted timeout list */
+ struct timeout_user *prev; /* prev in sorted timeout list */
+ struct timeval when; /* timeout expiry (absolute time) */
+ timeout_callback callback; /* callback function */
+ void *private; /* callback private data */
};
-static struct user *users[FD_SETSIZE]; /* users array */
+static struct select_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 */
+static struct timeout_user *timeout_head; /* sorted timeouts list head */
+static struct timeout_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 )
+/* register a user */
+void register_select_user( struct select_user *user )
{
int flags;
- struct user *user = malloc( sizeof(*user) );
- if (!user) return -1;
- assert( !users[fd] );
+ assert( !users[user->fd] );
- user->ops = ops;
- user->when.tv_sec = 0;
- user->when.tv_usec = 0;
- user->fd = fd;
- user->private = private;
+ flags = fcntl( user->fd, F_GETFL, 0 );
+ fcntl( user->fd, F_SETFL, flags | O_NONBLOCK );
- 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;
+ users[user->fd] = user;
+ if (user->fd > max_fd) max_fd = user->fd;
nb_users++;
- return fd;
}
/* remove a user */
-void remove_select_user( int fd )
+void unregister_select_user( struct select_user *user )
{
- struct user *user = users[fd];
- assert( user );
+ assert( users[user->fd] == 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--;
+ FD_CLR( user->fd, &read_set );
+ FD_CLR( user->fd, &write_set );
+ users[user->fd] = NULL;
+ if (max_fd == user->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 )
+/* set the events that select waits for on this fd */
+void set_select_events( struct select_user *user, int events )
{
- struct user *user = users[fd];
- struct user *pos;
- assert( user );
+ assert( users[user->fd] == user );
+ if (events & READ_EVENT) FD_SET( user->fd, &read_set );
+ else FD_CLR( user->fd, &read_set );
+ if (events & WRITE_EVENT) FD_SET( user->fd, &write_set );
+ else FD_CLR( user->fd, &write_set );
+}
- 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;
+/* check if events are pending */
+int check_select_events( struct select_user *user, int events )
+{
+ fd_set read_fds, write_fds;
+ struct timeval tv = { 0, 0 };
+
+ FD_ZERO( &read_fds );
+ FD_ZERO( &write_fds );
+ if (events & READ_EVENT) FD_SET( user->fd, &read_fds );
+ if (events & WRITE_EVENT) FD_SET( user->fd, &write_fds );
+ return select( user->fd + 1, &read_fds, &write_fds, NULL, &tv ) > 0;
+}
+
+/* add a timeout user */
+struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback func, void *private )
+{
+ struct timeout_user *user;
+ struct timeout_user *pos;
+
+ if (!(user = mem_alloc( sizeof(*user) ))) return NULL;
+ user->when = *when;
+ user->callback = func;
+ user->private = private;
/* Now insert it in the linked list */
@@ -117,24 +118,41 @@
user->prev = timeout_tail;
timeout_tail = user;
}
+ return user;
}
-/* set the events that select waits for on this fd */
-void set_select_events( int fd, int events )
+/* remove a timeout user */
+void remove_timeout_user( struct timeout_user *user )
{
- 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 );
+ 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;
+ free( user );
}
-/* get a user private data, checking the type */
-void *get_select_private_data( const struct select_ops *ops, int fd )
+/* make an absolute timeout value from a relative timeout in milliseconds */
+void make_timeout( struct timeval *when, int timeout )
{
- struct user *user = users[fd];
- assert( user );
- assert( user->ops == ops );
- return user->private;
+ gettimeofday( when, 0 );
+ if (!timeout) return;
+ if ((when->tv_usec += (timeout % 1000) * 1000) >= 1000000)
+ {
+ when->tv_usec -= 1000000;
+ when->tv_sec++;
+ }
+ when->tv_sec += timeout / 1000;
+}
+
+/* handle an expired timeout */
+static void handle_timeout( struct timeout_user *user )
+{
+ 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->callback( user->private );
+ free( user );
}
/* server main loop */
@@ -148,12 +166,6 @@
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;
@@ -162,7 +174,7 @@
((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 );
+ handle_timeout( timeout_head );
continue;
}
tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec;
@@ -171,10 +183,22 @@
tv.tv_usec += 1000000;
tv.tv_sec--;
}
+#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( " timeout %d.%06d\n", tv.tv_sec, tv.tv_usec );
+#endif
ret = select( max_fd + 1, &read, &write, NULL, &tv );
}
else /* no timeout */
{
+#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( " no timeout\n" );
+#endif
ret = select( max_fd + 1, &read, &write, NULL, NULL );
}
@@ -194,7 +218,7 @@
called in an earlier pass of this loop might have removed
the current user ... */
if (event && users[i])
- users[i]->ops->event( i, event, users[i]->private );
+ users[i]->func( event, users[i]->private );
}
}
}