Use poll() instead of select() for the server main loop.
Fixed races with SIGCHLD handling and ptrace.
Minor fixes to timeout handling.

diff --git a/server/console.c b/server/console.c
index 40d7cd3..d34fee6 100644
--- a/server/console.c
+++ b/server/console.c
@@ -38,7 +38,8 @@
 struct console_input
 {
     struct object         obj;           /* object header */
-    struct select_user    select;        /* select user */
+    int                   fd;            /* file descriptor */
+    int                   select;        /* select user id */
     int                   mode;          /* input mode */
     struct screen_buffer *output;        /* associated screen buffer */
     int                   recnum;        /* number of input records */
@@ -48,7 +49,8 @@
 struct screen_buffer
 {
     struct object         obj;           /* object header */
-    struct select_user    select;        /* select user */
+    int                   fd;            /* file descriptor */
+    int                   select;        /* select user id */
     int                   mode;          /* output mode */
     struct console_input *input;         /* associated console input */
     int                   cursor_size;   /* size of cursor (percentage filled) */
@@ -117,16 +119,16 @@
     }
     if ((console_input = alloc_object( &console_input_ops )))
     {
-        console_input->select.fd      = fd;
-        console_input->select.func    = default_select_event;
-        console_input->select.private = console_input;
-        console_input->mode           = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
-                                        ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
-        console_input->output         = NULL;
-        console_input->recnum         = 0;
-        console_input->records        = NULL;
-        register_select_user( &console_input->select );
-        return &console_input->obj;
+        console_input->fd      = fd;
+        console_input->mode    = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
+                                 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
+        console_input->output  = NULL;
+        console_input->recnum  = 0;
+        console_input->records = NULL;
+        console_input->select  = add_select_user( fd, default_select_event, console_input );
+        if (console_input->select != -1) return &console_input->obj;
+        release_object( console_input );
+        return NULL;
     }
     close( fd );
     return NULL;
@@ -144,16 +146,19 @@
     }
     if ((screen_buffer = alloc_object( &screen_buffer_ops )))
     {
-        screen_buffer->select.fd      = fd;
-        screen_buffer->select.func    = default_select_event;
-        screen_buffer->select.private = screen_buffer;
+        screen_buffer->fd             = fd;
         screen_buffer->mode           = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
         screen_buffer->input          = console_input;
         screen_buffer->cursor_size    = 100;
         screen_buffer->cursor_visible = 1;
         screen_buffer->pid            = 0;
         screen_buffer->title          = strdup( "Wine console" );
-        register_select_user( &screen_buffer->select );
+        screen_buffer->select = add_select_user( fd, default_select_event, screen_buffer );
+        if (screen_buffer->select == -1)
+        {
+            release_object( screen_buffer );
+            return NULL;
+        }
         console_input->output = screen_buffer;
         return &screen_buffer->obj;
     }
@@ -218,15 +223,11 @@
     assert( !input->obj.head );
     assert( !output->obj.head );
 
-    unregister_select_user( &input->select );
-    unregister_select_user( &output->select );
-    close( input->select.fd );
-    close( output->select.fd );
-    input->select.fd  = fd_in;
-    output->select.fd = fd_out;
-    output->pid       = pid;
-    register_select_user( &input->select );
-    register_select_user( &output->select );
+    change_select_fd( input->select, fd_in );
+    change_select_fd( output->select, fd_out );
+    input->fd   = fd_in;
+    output->fd  = fd_out;
+    output->pid = pid;
     release_object( input );
     release_object( output );
     return 1;
@@ -359,7 +360,7 @@
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    fprintf( stderr, "Console input fd=%d\n", console->select.fd );
+    fprintf( stderr, "Console input fd=%d\n", console->fd );
 }
 
 static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -367,7 +368,7 @@
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
     if (!obj->head)  /* first on the queue */
-        set_select_events( &console->select, READ_EVENT );
+        set_select_events( console->select, POLLIN );
     add_queue( obj, entry );
     return 1;
 }
@@ -379,7 +380,7 @@
 
     remove_queue( obj, entry );
     if (!obj->head)  /* last on the queue is gone */
-        set_select_events( &console->select, 0 );
+        set_select_events( console->select, 0 );
     release_object( obj );
 }
 
@@ -388,16 +389,16 @@
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
 
-    if (check_select_events( &console->select, READ_EVENT ))
+    if (check_select_events( console->fd, POLLIN ))
     {
         /* stop waiting on select() if we are signaled */
-        set_select_events( &console->select, 0 );
+        set_select_events( console->select, 0 );
         return 1;
     }
     else
     {
         /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( &console->select, READ_EVENT );
+        if (obj->head) set_select_events( console->select, POLLIN );
         return 0;
     }
 }
@@ -406,7 +407,7 @@
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    return dup( console->select.fd );
+    return dup( console->fd );
 }
 
 static int console_get_info( struct object *obj, struct get_file_info_request *req )
@@ -428,8 +429,7 @@
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    unregister_select_user( &console->select );
-    close( console->select.fd );
+    remove_select_user( console->select );
     if (console->output) console->output->input = NULL;
 }
 
@@ -437,7 +437,7 @@
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    fprintf( stderr, "Console screen buffer fd=%d\n", console->select.fd );
+    fprintf( stderr, "Console screen buffer fd=%d\n", console->fd );
 }
 
 static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -445,7 +445,7 @@
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
     if (!obj->head)  /* first on the queue */
-        set_select_events( &console->select, WRITE_EVENT );
+        set_select_events( console->select, POLLOUT );
     add_queue( obj, entry );
     return 1;
 }
@@ -457,7 +457,7 @@
 
     remove_queue( obj, entry );
     if (!obj->head)  /* last on the queue is gone */
-        set_select_events( &console->select, 0 );
+        set_select_events( console->select, 0 );
     release_object( obj );
 }
 
@@ -466,16 +466,16 @@
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
 
-    if (check_select_events( &console->select, WRITE_EVENT ))
+    if (check_select_events( console->fd, POLLOUT ))
     {
         /* stop waiting on select() if we are signaled */
-        set_select_events( &console->select, 0 );
+        set_select_events( console->select, 0 );
         return 1;
     }
     else
     {
         /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( &console->select, WRITE_EVENT );
+        if (obj->head) set_select_events( console->select, POLLOUT );
         return 0;
     }
 }
@@ -484,15 +484,14 @@
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    return dup( console->select.fd );
+    return dup( console->fd );
 }
 
 static void screen_buffer_destroy( struct object *obj )
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    unregister_select_user( &console->select );
-    close( console->select.fd );
+    remove_select_user( console->select );
     if (console->input) console->input->output = NULL;
     if (console->title) free( console->title );
 }
diff --git a/server/debugger.c b/server/debugger.c
index 683270c..fd9d8f6 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -218,7 +218,8 @@
     }
     if (timeout != -1)  /* start the timeout */
     {
-        make_timeout( &when, timeout );
+        gettimeofday( &when, 0 );
+        add_timeout( &when, timeout );
         if (!(debug_ctx->timeout = add_timeout_user( &when, wait_event_timeout, debug_ctx )))
             return 0;
     }
diff --git a/server/file.c b/server/file.c
index dd25a11..c80ecce 100644
--- a/server/file.c
+++ b/server/file.c
@@ -32,7 +32,8 @@
 struct file
 {
     struct object       obj;        /* object header */
-    struct select_user  select;     /* select user */
+    int                 fd;         /* file descriptor */
+    int                 select;     /* select user id */
     struct file        *next;       /* next file in hashing list */
     char               *name;       /* file name */
     unsigned int        access;     /* file access (GENERIC_READ/WRITE) */
@@ -108,15 +109,17 @@
     struct file *file;
     if ((file = alloc_object( &file_ops )))
     {
-        file->name           = NULL;
-        file->next           = NULL;
-        file->select.fd      = fd;
-        file->select.func    = default_select_event;
-        file->select.private = file;
-        file->access         = access;
-        file->flags          = attrs;
-        file->sharing        = sharing;
-        register_select_user( &file->select );
+        file->name    = NULL;
+        file->next    = NULL;
+        file->fd      = fd;
+        file->access  = access;
+        file->flags   = attrs;
+        file->sharing = sharing;
+        if ((file->select = add_select_user( fd, default_select_event, file )) == -1)
+        {
+            release_object( file );
+            file = NULL;
+        }
     }
     return file;
 }
@@ -221,8 +224,7 @@
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    fprintf( stderr, "File fd=%d flags=%08x name='%s'\n",
-             file->select.fd, file->flags, file->name );
+    fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->fd, file->flags, file->name );
 }
 
 static int file_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -232,9 +234,9 @@
     if (!obj->head)  /* first on the queue */
     {
         int events = 0;
-        if (file->access & GENERIC_READ) events |= READ_EVENT;
-        if (file->access & GENERIC_WRITE) events |= WRITE_EVENT;
-        set_select_events( &file->select, events );
+        if (file->access & GENERIC_READ) events |= POLLIN;
+        if (file->access & GENERIC_WRITE) events |= POLLOUT;
+        set_select_events( file->select, events );
     }
     add_queue( obj, entry );
     return 1;
@@ -247,7 +249,7 @@
 
     remove_queue( obj, entry );
     if (!obj->head)  /* last on the queue is gone */
-        set_select_events( &file->select, 0 );
+        set_select_events( file->select, 0 );
     release_object( obj );
 }
 
@@ -257,18 +259,18 @@
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
 
-    if (file->access & GENERIC_READ) events |= READ_EVENT;
-    if (file->access & GENERIC_WRITE) events |= WRITE_EVENT;
-    if (check_select_events( &file->select, events ))
+    if (file->access & GENERIC_READ) events |= POLLIN;
+    if (file->access & GENERIC_WRITE) events |= POLLOUT;
+    if (check_select_events( file->fd, events ))
     {
         /* stop waiting on select() if we are signaled */
-        set_select_events( &file->select, 0 );
+        set_select_events( file->select, 0 );
         return 1;
     }
     else
     {
         /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( &file->select, events );
+        if (obj->head) set_select_events( file->select, events );
         return 0;
     }
 }
@@ -277,14 +279,14 @@
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    return dup( file->select.fd );
+    return dup( file->fd );
 }
 
 static int file_get_write_fd( struct object *obj )
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    return dup( file->select.fd );
+    return dup( file->fd );
 }
 
 static int file_flush( struct object *obj )
@@ -293,7 +295,7 @@
     struct file *file = (struct file *)grab_object(obj);
     assert( obj->ops == &file_ops );
 
-    ret = (fsync( file->select.fd ) != -1);
+    ret = (fsync( file->fd ) != -1);
     if (!ret) file_set_error();
     release_object( file );
     return ret;
@@ -305,13 +307,13 @@
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
 
-    if (fstat( file->select.fd, &st ) == -1)
+    if (fstat( file->fd, &st ) == -1)
     {
         file_set_error();
         return 0;
     }
     if (S_ISCHR(st.st_mode) || S_ISFIFO(st.st_mode) ||
-        S_ISSOCK(st.st_mode) || isatty(file->select.fd)) req->type = FILE_TYPE_CHAR;
+        S_ISSOCK(st.st_mode) || isatty(file->fd)) req->type = FILE_TYPE_CHAR;
     else req->type = FILE_TYPE_DISK;
     if (S_ISDIR(st.st_mode)) req->attr = FILE_ATTRIBUTE_DIRECTORY;
     else req->attr = FILE_ATTRIBUTE_ARCHIVE;
@@ -342,8 +344,7 @@
         if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name );
         free( file->name );
     }
-    unregister_select_user( &file->select );
-    close( file->select.fd );
+    remove_select_user( file->select );
 }
 
 /* set the last error depending on errno */
@@ -378,7 +379,7 @@
 
 int file_get_mmap_fd( struct file *file )
 {
-    return dup( file->select.fd );
+    return dup( file->fd );
 }
 
 static int set_file_pointer( int handle, int *low, int *high, int whence )
@@ -395,7 +396,7 @@
 
     if (!(file = get_file_obj( current->process, handle, 0 )))
         return 0;
-    if ((result = lseek( file->select.fd, *low, whence )) == -1)
+    if ((result = lseek( file->fd, *low, whence )) == -1)
     {
         /* Check for seek before start of file */
         if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0))
@@ -417,8 +418,8 @@
 
     if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE )))
         return 0;
-    if (((result = lseek( file->select.fd, 0, SEEK_CUR )) == -1) ||
-        (ftruncate( file->select.fd, result ) == -1))
+    if (((result = lseek( file->fd, 0, SEEK_CUR )) == -1) ||
+        (ftruncate( file->fd, result ) == -1))
     {
         file_set_error();
         release_object( file );
@@ -439,13 +440,13 @@
         set_error( ERROR_INVALID_PARAMETER );
         return 0;
     }
-    if (fstat( file->select.fd, &st ) == -1)
+    if (fstat( file->fd, &st ) == -1)
     {
         file_set_error();
         return 0;
     }
     if (st.st_size >= size_low) return 1;  /* already large enough */
-    if (ftruncate( file->select.fd, size_low ) != -1) return 1;
+    if (ftruncate( file->fd, size_low ) != -1) return 1;
     file_set_error();
     return 0;
 }
diff --git a/server/object.h b/server/object.h
index 46c9c6c..d5335f1 100644
--- a/server/object.h
+++ b/server/object.h
@@ -11,6 +11,7 @@
 #error This file can only be used in the Wine server
 #endif
 
+#include <sys/poll.h>
 #include <sys/time.h>
 #include "server.h"
 
@@ -89,21 +90,11 @@
 
 /* select functions */
 
-#define READ_EVENT    1
-#define WRITE_EVENT   2
-#define EXCEPT_EVENT  4
-
-struct select_user
-{
-    int    fd;                              /* user fd */
-    void (*func)(int event, void *private); /* callback function */
-    void  *private;                         /* callback private data */
-};
-
-extern void register_select_user( struct select_user *user );
-extern void unregister_select_user( struct select_user *user );
-extern void set_select_events( struct select_user *user, int events );
-extern int check_select_events( struct select_user *user, int events );
+extern int add_select_user( int fd, void (*func)(int, void *), void *private );
+extern void remove_select_user( int user );
+extern void change_select_fd( int user, int fd );
+extern void set_select_events( int user, int events );
+extern int check_select_events( int fd, int events );
 extern void select_loop(void);
 
 /* timeout functions */
@@ -115,7 +106,13 @@
 extern struct timeout_user *add_timeout_user( struct timeval *when,
                                               timeout_callback func, void *private );
 extern void remove_timeout_user( struct timeout_user *user );
-extern void make_timeout( struct timeval *when, int timeout );
+extern void add_timeout( struct timeval *when, int timeout );
+/* return 1 if t1 is before t2 */
+static inline int time_before( struct timeval *t1, struct timeval *t2 )
+{
+    return ((t1->tv_sec < t2->tv_sec) ||
+            ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec)));
+}
 
 /* socket functions */
 
diff --git a/server/pipe.c b/server/pipe.c
index 5459f3e..f449ca6 100644
--- a/server/pipe.c
+++ b/server/pipe.c
@@ -33,7 +33,8 @@
 {
     struct object       obj;         /* object header */
     struct pipe        *other;       /* the pipe other end */
-    struct select_user  select;      /* select user */
+    int                 fd;          /* file descriptor */
+    int                 select;      /* select user id */
     enum side           side;        /* which side of the pipe is this */
 };
 
@@ -68,12 +69,14 @@
 
     if ((pipe = alloc_object( &pipe_ops )))
     {
-        pipe->select.fd      = fd;
-        pipe->select.func    = default_select_event;
-        pipe->select.private = pipe;
-        pipe->other          = NULL;
-        pipe->side           = side;
-        register_select_user( &pipe->select );
+        pipe->fd     = fd;
+        pipe->other  = NULL;
+        pipe->side   = side;
+        if ((pipe->select = add_select_user( fd, default_select_event, pipe )) == -1)
+        {
+            release_object( pipe );
+            pipe = NULL;
+        }
     }
     return pipe;
 }
@@ -111,7 +114,7 @@
     struct pipe *pipe = (struct pipe *)obj;
     assert( obj->ops == &pipe_ops );
     fprintf( stderr, "Pipe %s-side fd=%d\n",
-             (pipe->side == READ_SIDE) ? "read" : "write", pipe->select.fd );
+             (pipe->side == READ_SIDE) ? "read" : "write", pipe->fd );
 }
 
 static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -119,8 +122,7 @@
     struct pipe *pipe = (struct pipe *)obj;
     assert( obj->ops == &pipe_ops );
     if (!obj->head)  /* first on the queue */
-        set_select_events( &pipe->select,
-                           (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT );
+        set_select_events( pipe->select, (pipe->side == READ_SIDE) ? POLLIN : POLLOUT );
     add_queue( obj, entry );
     return 1;
 }
@@ -132,7 +134,7 @@
 
     remove_queue( obj, entry );
     if (!obj->head)  /* last on the queue is gone */
-        set_select_events( &pipe->select, 0 );
+        set_select_events( pipe->select, 0 );
     release_object( obj );
 }
 
@@ -142,17 +144,17 @@
     struct pipe *pipe = (struct pipe *)obj;
     assert( obj->ops == &pipe_ops );
 
-    event = (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT;
-    if (check_select_events( &pipe->select, event ))
+    event = (pipe->side == READ_SIDE) ? POLLIN : POLLOUT;
+    if (check_select_events( pipe->fd, event ))
     {
         /* stop waiting on select() if we are signaled */
-        set_select_events( &pipe->select, 0 );
+        set_select_events( pipe->select, 0 );
         return 1;
     }
     else
     {
         /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( &pipe->select, event );
+        if (obj->head) set_select_events( pipe->select, event );
         return 0;
     }
 }
@@ -172,7 +174,7 @@
         set_error( ERROR_ACCESS_DENIED );
         return -1;
     }
-    return dup( pipe->select.fd );
+    return dup( pipe->fd );
 }
 
 static int pipe_get_write_fd( struct object *obj )
@@ -190,7 +192,7 @@
         set_error( ERROR_ACCESS_DENIED );
         return -1;
     }
-    return dup( pipe->select.fd );
+    return dup( pipe->fd );
 }
 
 static int pipe_get_info( struct object *obj, struct get_file_info_request *req )
@@ -214,8 +216,7 @@
     assert( obj->ops == &pipe_ops );
 
     if (pipe->other) pipe->other->other = NULL;
-    unregister_select_user( &pipe->select );
-    close( pipe->select.fd );
+    remove_select_user( pipe->select );
 }
 
 /* create an anonymous pipe */
diff --git a/server/ptrace.c b/server/ptrace.c
index 89a42e4..7567046 100644
--- a/server/ptrace.c
+++ b/server/ptrace.c
@@ -39,58 +39,70 @@
 
 static const int use_ptrace = 1;  /* set to 0 to disable ptrace */
 
-
-/* wait for a ptraced child to get a certain signal */
-/* if the signal is 0, we simply check if anything is pending and return at once */
-void wait4_thread( struct thread *thread, int signal )
+/* handle a status returned by wait4 */
+static int handle_child_status( struct thread *thread, int pid, int status )
 {
-    int status;
-    int pid;
-
- restart:
-    pid = thread ? thread->unix_pid : -1;
-    if ((pid = wait4( pid, &status, WUNTRACED | (signal ? 0 : WNOHANG), NULL )) == -1)
-    {
-        perror( "wait4" );
-        return;
-    }
     if (WIFSTOPPED(status))
     {
         int sig = WSTOPSIG(status);
-        if (debug_level) fprintf( stderr, "ptrace: pid %d got sig %d\n", pid, sig );
+        if (debug_level && thread)
+            fprintf( stderr, "%08x: *signal* signal=%d\n", (unsigned int)thread, sig );
         switch(sig)
         {
         case SIGSTOP:  /* continue at once if not suspended */
-            if (!thread)
-                if (!(thread = get_thread_from_pid( pid ))) break;
-            if (!(thread->process->suspend + thread->suspend))
+            if (!thread || !(thread->process->suspend + thread->suspend))
                 ptrace( PTRACE_CONT, pid, 1, sig );
             break;
         default:  /* ignore other signals for now */
             ptrace( PTRACE_CONT, pid, 1, sig );
             break;
         }
-        if (signal && sig != signal) goto restart;
+        return sig;
     }
-    else if (WIFSIGNALED(status))
+    if (thread && (WIFSIGNALED(status) || WIFEXITED(status)))
     {
-        int exit_code = WTERMSIG(status);
+        thread->attached = 0;
+        thread->unix_pid = 0;
         if (debug_level)
-            fprintf( stderr, "ptrace: pid %d killed by sig %d\n", pid, exit_code );
-        if (!thread)
-            if (!(thread = get_thread_from_pid( pid ))) return;
-        if (thread->client) remove_client( thread->client, exit_code );
+        {
+            if (WIFSIGNALED(status))
+                fprintf( stderr, "%08x: *exited* signal=%d\n",
+                         (unsigned int)thread, WTERMSIG(status) );
+            else
+                fprintf( stderr, "%08x: *exited* status=%d\n",
+                         (unsigned int)thread, WEXITSTATUS(status) );
+        }
     }
-    else if (WIFEXITED(status))
+    return 0;
+}
+
+/* handle a SIGCHLD signal */
+void sigchld_handler()
+{
+    int pid, status;
+
+    for (;;)
     {
-        int exit_code = WEXITSTATUS(status);
-        if (debug_level)
-            fprintf( stderr, "ptrace: pid %d exited with status %d\n", pid, exit_code );
-        if (!thread)
-            if (!(thread = get_thread_from_pid( pid ))) return;
-        if (thread->client) remove_client( thread->client, exit_code );
+        if (!(pid = wait4( -1, &status, WUNTRACED | WNOHANG, NULL ))) break;
+        if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status );
+        else break;
     }
-    else fprintf( stderr, "wait4: pid %d unknown status %x\n", pid, status );
+}
+
+/* wait for a ptraced child to get a certain signal */
+void wait4_thread( struct thread *thread, int signal )
+{
+    int res, status;
+
+    do
+    {
+        if ((res = wait4( thread->unix_pid, &status, WUNTRACED, NULL )) == -1)
+        {
+            perror( "wait4" );
+            return;
+        }
+        res = handle_child_status( thread, res, status );
+    } while (res && res != signal);
 }
 
 /* attach to a Unix thread */
@@ -98,7 +110,7 @@
 {
     /* this may fail if the client is already being debugged */
     if (!use_ptrace || (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1)) return 0;
-    if (debug_level) fprintf( stderr, "ptrace: attached to pid %d\n", thread->unix_pid );
+    if (debug_level) fprintf( stderr, "%08x: *attached*\n", (unsigned int)thread );
     thread->attached = 1;
     wait4_thread( thread, SIGSTOP );
     return 1;
@@ -113,7 +125,7 @@
     if (thread->attached)
     {
         wait4_thread( thread, SIGTERM );
-        if (debug_level) fprintf( stderr, "ptrace: detaching from %d\n", thread->unix_pid );
+        if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread );
         ptrace( PTRACE_DETACH, thread->unix_pid, 1, SIGTERM );
         thread->attached = 0;
     }
diff --git a/server/select.c b/server/select.c
index 540a09b..a1286cf 100644
--- a/server/select.c
+++ b/server/select.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/poll.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -17,6 +18,13 @@
 #include "object.h"
 #include "thread.h"
 
+
+struct poll_user
+{
+    void (*func)(int event, void *private); /* callback function */
+    void  *private;                         /* callback private data */
+};
+
 struct timeout_user
 {
     struct timeout_user  *next;       /* next in sorted timeout list */
@@ -26,62 +34,92 @@
     void                 *private;    /* callback private data */
 };
 
-static struct select_user *users[FD_SETSIZE];  /* users array */
-static fd_set read_set, write_set, except_set; /* current select sets */
-static int nb_users;                        /* current number of users */
-static int max_fd;                          /* max fd in use */
+static struct poll_user *poll_users;        /* users array */
+static struct pollfd *pollfd;               /* poll fd array */
+static int nb_users;                        /* count of array entries actually in use */
+static int active_users;                    /* current number of active users */
+static int allocated_users;                 /* count of allocated entries in the array */
+static struct poll_user *freelist;          /* list of free entries in the array */
+
 static struct timeout_user *timeout_head;   /* sorted timeouts list head */
 static struct timeout_user *timeout_tail;   /* sorted timeouts list tail */
 
 
-/* register a user */
-void register_select_user( struct select_user *user )
+/* add a user and return an opaque handle to it, or -1 on failure */
+int add_select_user( int fd, void (*func)(int, void *), void *private )
 {
-    assert( !users[user->fd] );
-
-    users[user->fd] = user;
-    if (user->fd > max_fd) max_fd = user->fd;
-    nb_users++;
+    int ret;
+    if (freelist)
+    {
+        ret = freelist - poll_users;
+        freelist = poll_users[ret].private;
+        assert( !poll_users[ret].func );
+    }
+    else
+    {
+        if (nb_users == allocated_users)
+        {
+            struct poll_user *newusers;
+            struct pollfd *newpoll;
+            int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
+            if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
+            if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
+            {
+                free( newusers );
+                return -1;
+            }
+            poll_users = newusers;
+            pollfd = newpoll;
+            allocated_users = new_count;
+        }
+        ret = nb_users++;
+    }
+    pollfd[ret].fd = fd;
+    pollfd[ret].events = 0;
+    pollfd[ret].revents = 0;
+    poll_users[ret].func = func;
+    poll_users[ret].private = private;
+    active_users++;
+    return ret;
 }
 
-/* remove a user */
-void unregister_select_user( struct select_user *user )
+/* remove a user and close its fd */
+void remove_select_user( int user )
 {
-    assert( users[user->fd] == user );
+    if (user == -1) return;  /* avoids checking in all callers */
+    assert( poll_users[user].func );
+    close( pollfd[user].fd );
+    pollfd[user].fd = -1;
+    pollfd[user].events = 0;
+    pollfd[user].revents = 0;
+    poll_users[user].func = NULL;
+    poll_users[user].private = freelist;
+    freelist = &poll_users[user];
+    active_users--;
+}
 
-    FD_CLR( user->fd, &read_set );
-    FD_CLR( user->fd, &write_set );
-    FD_CLR( user->fd, &except_set );
-    users[user->fd] = NULL;
-    if (max_fd == user->fd) while (max_fd && !users[max_fd]) max_fd--;
-    nb_users--;
+/* change the fd of a select user (the old fd is closed) */
+void change_select_fd( int user, int fd )
+{
+    assert( poll_users[user].func );
+    close( pollfd[user].fd );
+    pollfd[user].fd = fd;
 }
 
 /* set the events that select waits for on this fd */
-void set_select_events( struct select_user *user, int events )
+void set_select_events( int user, int events )
 {
-    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 (events & EXCEPT_EVENT) FD_SET( user->fd, &except_set );
-    else FD_CLR( user->fd, &except_set );
+    assert( poll_users[user].func );
+    pollfd[user].events = events;
 }
 
 /* check if events are pending */
-int check_select_events( struct select_user *user, int events )
+int check_select_events( int fd, int events )
 {
-    fd_set read_fds, write_fds, except_fds;
-    struct timeval tv = { 0, 0 };
-
-    FD_ZERO( &read_fds );
-    FD_ZERO( &write_fds );
-    FD_ZERO( &except_fds );
-    if (events & READ_EVENT) FD_SET( user->fd, &read_fds );
-    if (events & WRITE_EVENT) FD_SET( user->fd, &write_fds );
-    if (events & EXCEPT_EVENT) FD_SET( user->fd, &except_fds );
-    return select( user->fd + 1, &read_fds, &write_fds, &except_fds, &tv ) > 0;
+    struct pollfd pfd;
+    pfd.fd     = fd;
+    pfd.events = events;
+    return poll( &pfd, 1, 0 ) > 0;
 }
 
 /* add a timeout user */
@@ -98,11 +136,7 @@
     /* 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 (!time_before( &pos->when, when )) break;
 
     if (pos)  /* insert it before 'pos' */
     {
@@ -132,123 +166,103 @@
     free( user );
 }
 
-/* make an absolute timeout value from a relative timeout in milliseconds */
-void make_timeout( struct timeval *when, int timeout )
+/* add a timeout in milliseconds to an absolute time */
+void add_timeout( struct timeval *when, int timeout )
 {
-    gettimeofday( when, 0 );
-    if (!timeout) return;
-    if ((when->tv_usec += (timeout % 1000) * 1000) >= 1000000)
+    if (timeout)
     {
-        when->tv_usec -= 1000000;
-        when->tv_sec++;
+        long sec = timeout / 1000;
+        if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
+        {
+            when->tv_usec -= 1000000;
+            when->tv_sec++;
+        }
+        when->tv_sec += sec;
     }
-    when->tv_sec += timeout / 1000;
 }
 
-/* handle an expired timeout */
-static void handle_timeout( struct timeout_user *user )
+/* handle the next expired timeout */
+static void handle_timeout(void)
 {
+    struct timeout_user *user = timeout_head;
+    timeout_head = user->next;
     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 );
 }
 
-#ifdef DEBUG_OBJECTS
-static int do_dump_objects;
-
 /* SIGHUP handler */
-static void sighup()
+static void sighup_handler()
 {
-    do_dump_objects = 1;
-}
+#ifdef DEBUG_OBJECTS
+    dump_objects();
 #endif
-
-/* dummy SIGCHLD handler */
-static void sigchld()
-{
 }
 
 /* server main loop */
 void select_loop(void)
 {
-    int i, ret;
+    int ret;
+    sigset_t sigset;
+    struct sigaction action;
 
     setsid();
     signal( SIGPIPE, SIG_IGN );
-    signal( SIGCHLD, sigchld );
-#ifdef DEBUG_OBJECTS
-    signal( SIGHUP, sighup );
-#endif
 
-    while (nb_users)
+    /* block the signals we use */
+    sigemptyset( &sigset );
+    sigaddset( &sigset, SIGCHLD );
+    sigaddset( &sigset, SIGHUP );
+    sigprocmask( SIG_BLOCK, &sigset, NULL );
+
+    /* set the handlers */
+    action.sa_mask = sigset;
+    action.sa_flags = 0;
+    action.sa_handler = sigchld_handler;
+    sigaction( SIGCHLD, &action, NULL );
+    action.sa_handler = sighup_handler;
+    sigaction( SIGHUP, &action, NULL );
+
+    while (active_users)
     {
-        fd_set read = read_set, write = write_set, except = except_set;
+        long diff = -1;
         if (timeout_head)
         {
-            struct timeval tv, now;
+            struct timeval 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)))
+            while (timeout_head)
             {
-                handle_timeout( timeout_head );
-                continue;
+                if (!time_before( &now, &timeout_head->when )) handle_timeout();
+                else
+                {
+                    diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
+                            + (timeout_head->when.tv_usec - now.tv_usec) / 1000;
+                    break;
+                }
             }
-            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--;
-            }
-#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, &except, &tv );
         }
-        else  /* no timeout */
+
+        sigprocmask( SIG_UNBLOCK, &sigset, NULL );
+
+        /* Note: we assume that the signal handlers do not manipulate the pollfd array
+         *       or the timeout list, otherwise there is a race here.
+         */
+        ret = poll( pollfd, nb_users, diff );
+
+        sigprocmask( SIG_BLOCK, &sigset, NULL );
+
+        if (ret > 0)
         {
-#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, &except, NULL );
-        }
-
-        if (!ret) continue;
-        if (ret == -1) {
-            if (errno == EINTR)
+            int i;
+            for (i = 0; i < nb_users; i++)
             {
-#ifdef DEBUG_OBJECTS
-                if (do_dump_objects) dump_objects();
-                do_dump_objects = 0;
-#endif
-                wait4_thread( NULL, 0 );
-                continue;
+                if (pollfd[i].revents)
+                {
+                    poll_users[i].func( pollfd[i].revents, poll_users[i].private );
+                    if (!--ret) break;
+                }
             }
-            perror("select");
-            continue;
-        }
-
-        for (i = 0; i <= max_fd; i++)
-        {
-            int event = 0;
-            if (FD_ISSET( i, &except )) event |= EXCEPT_EVENT;
-            if (FD_ISSET( i, &write ))  event |= WRITE_EVENT;
-            if (FD_ISSET( i, &read ))   event |= READ_EVENT;
-
-            /* Note: users[i] might be NULL here, because an event routine
-               called in an earlier pass of this loop might have removed 
-               the current user ... */
-            if (event && users[i]) 
-                users[i]->func( event, users[i]->private );
         }
     }
 }
diff --git a/server/sock.c b/server/sock.c
index 22c5c81..cb9928f 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -42,7 +42,8 @@
 struct sock
 {
     struct object       obj;         /* object header */
-    struct select_user  select;      /* select user */
+    int                 fd;          /* file descriptor */
+    int                 select;      /* select user */
     unsigned int        state;       /* status bits */
     unsigned int        mask;        /* event mask */
     unsigned int        hmask;       /* held (blocked) events */
@@ -77,17 +78,17 @@
 static int sock_event( struct sock *sock )
 {
     unsigned int mask = sock->mask & sock->state & ~sock->hmask;
-    int ev = EXCEPT_EVENT;
+    int ev = 0;
 
     if (sock->state & WS_FD_CONNECT)
         /* connecting, wait for writable */
-        return WRITE_EVENT | EXCEPT_EVENT;
+        return POLLOUT;
     if (sock->state & WS_FD_LISTENING)
         /* listening, wait for readable */
-        return ((sock->hmask & FD_ACCEPT) ? 0 : READ_EVENT) | EXCEPT_EVENT;
+        return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;
 
-    if (mask & FD_READ)  ev |= READ_EVENT;
-    if (mask & FD_WRITE) ev |= WRITE_EVENT;
+    if (mask & FD_READ)  ev |= POLLIN;
+    if (mask & FD_WRITE) ev |= POLLOUT;
     return ev;
 }
 
@@ -95,8 +96,8 @@
 {
     int ev = sock_event( sock );
     if (debug_level)
-        fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->select.fd, ev);
-    set_select_events( &sock->select, ev );
+        fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->fd, ev);
+    set_select_events( sock->select, ev );
 }
 
 inline static int sock_error(int s)
@@ -114,11 +115,11 @@
     unsigned int emask;
     assert( sock->obj.ops == &sock_ops );
     if (debug_level)
-        fprintf(stderr, "socket %d select event: %x\n", sock->select.fd, event);
+        fprintf(stderr, "socket %d select event: %x\n", sock->fd, event);
     if (sock->state & WS_FD_CONNECT)
     {
         /* connecting */
-        if (event & WRITE_EVENT)
+        if (event & POLLOUT)
         {
             /* we got connected */
             sock->state |= WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
@@ -126,43 +127,43 @@
             sock->pmask |= FD_CONNECT;
             sock->errors[FD_CONNECT_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d connection success\n", sock->select.fd);
+                fprintf(stderr, "socket %d connection success\n", sock->fd);
         }
-        else if (event & EXCEPT_EVENT)
+        else if (event & (POLLERR|POLLHUP))
         {
             /* we didn't get connected? */
             sock->state &= ~WS_FD_CONNECT;
             sock->pmask |= FD_CONNECT;
-            sock->errors[FD_CONNECT_BIT] = sock_error( sock->select.fd );
+            sock->errors[FD_CONNECT_BIT] = sock_error( sock->fd );
             if (debug_level)
-                fprintf(stderr, "socket %d connection failure\n", sock->select.fd);
+                fprintf(stderr, "socket %d connection failure\n", sock->fd);
         }
     } else
     if (sock->state & WS_FD_LISTENING)
     {
         /* listening */
-        if (event & READ_EVENT)
+        if (event & POLLIN)
         {
             /* incoming connection */
             sock->pmask |= FD_ACCEPT;
             sock->errors[FD_ACCEPT_BIT] = 0;
             sock->hmask |= FD_ACCEPT;
         }
-        else if (event & EXCEPT_EVENT)
+        else if (event & (POLLERR|POLLHUP))
         {
             /* failed incoming connection? */
             sock->pmask |= FD_ACCEPT;
-            sock->errors[FD_ACCEPT_BIT] = sock_error( sock->select.fd );
+            sock->errors[FD_ACCEPT_BIT] = sock_error( sock->fd );
             sock->hmask |= FD_ACCEPT;
         }
     } else
     {
         /* normal data flow */
-        if (event & READ_EVENT)
+        if (event & POLLIN)
         {
             /* make sure there's data here */
             int bytes = 0;
-            ioctl(sock->select.fd, FIONREAD, (char*)&bytes);
+            ioctl(sock->fd, FIONREAD, (char*)&bytes);
             if (bytes)
             {
                 /* incoming data */
@@ -170,7 +171,7 @@
                 sock->hmask |= FD_READ;
                 sock->errors[FD_READ_BIT] = 0;
                 if (debug_level)
-                    fprintf(stderr, "socket %d has %d bytes\n", sock->select.fd, bytes);
+                    fprintf(stderr, "socket %d has %d bytes\n", sock->fd, bytes);
             }
             else
             {
@@ -179,27 +180,27 @@
                 sock->pmask |= FD_CLOSE;
                 sock->errors[FD_CLOSE_BIT] = 0;
                 if (debug_level)
-                    fprintf(stderr, "socket %d is closing\n", sock->select.fd);
+                    fprintf(stderr, "socket %d is closing\n", sock->fd);
             }
         }
-        if (event & WRITE_EVENT)
+        if (event & POLLOUT)
         {
             sock->pmask |= FD_WRITE;
             sock->hmask |= FD_WRITE;
             sock->errors[FD_WRITE_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d is writable\n", sock->select.fd);
+                fprintf(stderr, "socket %d is writable\n", sock->fd);
         }
-        if (event & EXCEPT_EVENT)
+        if (event & (POLLERR|POLLHUP))
         {
-            sock->errors[FD_CLOSE_BIT] = sock_error( sock->select.fd );
+            sock->errors[FD_CLOSE_BIT] = sock_error( sock->fd );
             if (sock->errors[FD_CLOSE_BIT])
             {
                 /* we got an error, socket closing? */
                 sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE);
                 sock->pmask |= FD_CLOSE;
                 if (debug_level)
-                    fprintf(stderr, "socket %d aborted by error %d\n", sock->select.fd, sock->errors[FD_CLOSE_BIT]);
+                    fprintf(stderr, "socket %d aborted by error %d\n", sock->fd, sock->errors[FD_CLOSE_BIT]);
             }
             else
             {
@@ -207,7 +208,7 @@
                 sock->pmask |= FD_OOB;
                 sock->hmask |= FD_OOB;
                 if (debug_level)
-                    fprintf(stderr, "socket %d got OOB data\n", sock->select.fd);
+                    fprintf(stderr, "socket %d got OOB data\n", sock->fd);
             }
         }
     }
@@ -216,7 +217,7 @@
     /* wake up anyone waiting for whatever just happened */
     emask = sock->pmask & sock->mask;
     if (debug_level && emask)
-        fprintf(stderr, "socket %d pending events: %x\n", sock->select.fd, emask);
+        fprintf(stderr, "socket %d pending events: %x\n", sock->fd, emask);
     if (emask && sock->event) {
         if (debug_level) fprintf(stderr, "signalling event ptr %p\n", sock->event);
         set_event(sock->event);
@@ -232,7 +233,7 @@
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
     printf( "Socket fd=%d, state=%x, mask=%x, pending=%x, held=%x\n",
-            sock->select.fd, sock->state,
+            sock->fd, sock->state,
             sock->mask, sock->pmask, sock->hmask );
 }
 
@@ -259,7 +260,7 @@
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
 
-    return check_select_events( &sock->select, sock_event( sock ) );
+    return check_select_events( sock->fd, sock_event( sock ) );
 }
 
 static int sock_get_fd( struct object *obj )
@@ -267,7 +268,7 @@
     struct sock *sock = (struct sock *)obj;
     int fd;
     assert( obj->ops == &sock_ops );
-    fd = dup( sock->select.fd );
+    fd = dup( sock->fd );
     if (fd==-1)
     	sock_set_error();
     return fd;
@@ -278,9 +279,8 @@
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
 
-    unregister_select_user( &sock->select );
     /* FIXME: special socket shutdown stuff? */
-    close( sock->select.fd );
+    remove_select_user( sock->select );
     if (sock->event)
     {
         /* if the service thread was waiting for the event object,
@@ -311,15 +311,17 @@
         close( sockfd );
         return NULL;
     }
-    sock->select.fd      = sockfd;
-    sock->select.func    = sock_select_event;
-    sock->select.private = sock;
-    sock->state          = (type!=SOCK_STREAM) ? WS_FD_READ|WS_FD_WRITE : 0;
-    sock->mask           = 0;
-    sock->hmask          = 0;
-    sock->pmask          = 0;
-    sock->event          = NULL;
-    register_select_user( &sock->select );
+    sock->fd    = sockfd;
+    sock->state = (type!=SOCK_STREAM) ? WS_FD_READ|WS_FD_WRITE : 0;
+    sock->mask  = 0;
+    sock->hmask = 0;
+    sock->pmask = 0;
+    sock->event = NULL;
+    if ((sock->select = add_select_user( sockfd, sock_select_event, sock )) == -1)
+    {
+        release_object( sock );
+        return NULL;
+    }
     sock_reselect( sock );
     clear_error();
     return &sock->obj;
@@ -343,7 +345,7 @@
      * return.
      */
     slen = sizeof(saddr);
-    acceptfd = accept(sock->select.fd,&saddr,&slen);
+    acceptfd = accept(sock->fd,&saddr,&slen);
     if (acceptfd==-1) {
     	sock_set_error();
         release_object( sock );
@@ -356,19 +358,21 @@
         return NULL;
     }
 
-    acceptsock->select.fd      = acceptfd;
-    acceptsock->select.func    = sock_select_event;
-    acceptsock->select.private = acceptsock;
-    acceptsock->state          = WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
-    acceptsock->mask           = sock->mask;
-    acceptsock->hmask          = 0;
-    acceptsock->pmask          = 0;
-    if (sock->event)
-        acceptsock->event          = (struct event *)grab_object( sock->event );
-    else
-        acceptsock->event          = NULL;
+    acceptsock->fd     = acceptfd;
+    acceptsock->state  = WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
+    acceptsock->mask   = sock->mask;
+    acceptsock->hmask  = 0;
+    acceptsock->pmask  = 0;
+    acceptsock->event  = NULL;
+    acceptsock->select = add_select_user( acceptfd, sock_select_event, acceptsock );
+    if (acceptsock->select == -1)
+    {
+        release_object( acceptsock );
+        release_object( sock );
+        return NULL;
+    }
+    if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event );
 
-    register_select_user( &acceptsock->select );
     sock_reselect( acceptsock );
     clear_error();
     sock->pmask &= ~FD_ACCEPT;
diff --git a/server/socket.c b/server/socket.c
index 5741110..fb3345a 100644
--- a/server/socket.c
+++ b/server/socket.c
@@ -32,7 +32,8 @@
 /* client structure */
 struct client
 {
-    struct select_user   select;     /* select user */
+    int                  fd;         /* socket file descriptor */
+    int                  select;     /* select user id */
     unsigned int         res;        /* current result to send */
     int                  pass_fd;    /* fd to pass to and from the client */
     struct thread       *self;       /* client thread (opaque pointer) */
@@ -62,7 +63,7 @@
 
     if (client->pass_fd == -1)
     {
-        ret = write( client->select.fd, &client->res, sizeof(client->res) );
+        ret = write( client->fd, &client->res, sizeof(client->res) );
         if (ret == sizeof(client->res)) goto ok;
     }
     else  /* we have an fd to send */
@@ -79,7 +80,7 @@
         myiovec.iov_base = (void *)&client->res;
         myiovec.iov_len  = sizeof(client->res);
 
-        ret = sendmsg( client->select.fd, &msghdr, 0 );
+        ret = sendmsg( client->fd, &msghdr, 0 );
         close( client->pass_fd );
         client->pass_fd = -1;
         if (ret == sizeof(client->res)) goto ok;
@@ -91,10 +92,10 @@
     }
     else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) );
     remove_client( client, BROKEN_PIPE );
-    return 0;
+    return -1;
 
  ok:
-    set_select_events( &client->select, READ_EVENT );
+    set_select_events( client->select, POLLIN );
     return 1;
 }
 
@@ -119,7 +120,7 @@
     myiovec.iov_base = (void *)&req;
     myiovec.iov_len  = sizeof(req);
 
-    ret = recvmsg( client->select.fd, &msghdr, 0 );
+    ret = recvmsg( client->fd, &msghdr, 0 );
 #ifndef HAVE_MSGHDR_ACCRIGHTS
     client->pass_fd = cmsg.fd;
 #endif
@@ -150,8 +151,12 @@
 static void client_event( int event, void *private )
 {
     struct client *client = (struct client *)private;
-    if (event & WRITE_EVENT) do_write( client );
-    if (event & READ_EVENT) do_read( client );
+    if (event & (POLLERR | POLLHUP)) remove_client( client, BROKEN_PIPE );
+    else
+    {
+        if (event & POLLOUT) do_write( client );
+        if (event & POLLIN) do_read( client );
+    }
 }
 
 /*******************************************************************/
@@ -167,14 +172,16 @@
     flags = fcntl( fd, F_GETFL, 0 );
     fcntl( fd, F_SETFL, flags | O_NONBLOCK );
 
-    client->select.fd            = fd;
-    client->select.func          = client_event;
-    client->select.private       = client;
-    client->self                 = self;
-    client->timeout              = NULL;
-    client->pass_fd              = -1;
-    register_select_user( &client->select );
-    set_select_events( &client->select, READ_EVENT );
+    client->fd      = fd;
+    client->self    = self;
+    client->timeout = NULL;
+    client->pass_fd = -1;
+    if ((client->select = add_select_user( fd, client_event, client )) == -1)
+    {
+        free( client );
+        return NULL;
+    }
+    set_select_events( client->select, POLLIN );
     return client;
 }
 
@@ -186,8 +193,7 @@
     call_kill_handler( client->self, exit_code );
 
     if (client->timeout) remove_timeout_user( client->timeout );
-    unregister_select_user( &client->select );
-    close( client->select.fd );
+    remove_select_user( client->select );
 
     /* Purge messages */
     if (client->pass_fd != -1) close( client->pass_fd );
@@ -206,5 +212,5 @@
 {
     if (debug_level) trace_reply( client->self, res, client->pass_fd );
     client->res = res;
-    if (!do_write( client )) set_select_events( &client->select, WRITE_EVENT );
+    if (!do_write( client )) set_select_events( client->select, POLLOUT );
 }
diff --git a/server/thread.c b/server/thread.c
index b65f7e0..e319f51 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -330,7 +330,11 @@
     wait->count   = count;
     wait->flags   = flags;
     wait->user    = NULL;
-    if (flags & SELECT_TIMEOUT) make_timeout( &wait->timeout, timeout );
+    if (flags & SELECT_TIMEOUT)
+    {
+        gettimeofday( &wait->timeout, 0 );
+        add_timeout( &wait->timeout, timeout );
+    }
 
     for (i = 0, entry = wait->queues; i < count; i++, entry++)
     {
@@ -399,9 +403,7 @@
     {
         struct timeval now;
         gettimeofday( &now, NULL );
-        if ((now.tv_sec > wait->timeout.tv_sec) ||
-            ((now.tv_sec == wait->timeout.tv_sec) &&
-             (now.tv_usec >= wait->timeout.tv_usec)))
+        if (!time_before( &now, &wait->timeout ))
         {
             *signaled = STATUS_TIMEOUT;
             return 1;
diff --git a/server/thread.h b/server/thread.h
index 6a482cc..bfe9bf4 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -80,7 +80,8 @@
 
 /* ptrace functions */
 
-extern void wait4_thread( struct thread *thread, int wait );
+extern void sigchld_handler();
+extern void wait4_thread( struct thread *thread, int signal );
 extern void stop_thread( struct thread *thread );
 extern void continue_thread( struct thread *thread );
 extern void detach_thread( struct thread *thread );
diff --git a/server/timer.c b/server/timer.c
index 0a20ca1..d228d16 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -81,7 +81,7 @@
 
     if (timer->period)  /* schedule the next expiration */
     {
-        make_timeout( &timer->when, timer->period );
+        add_timeout( &timer->when, timer->period );
         timer->timeout = add_timeout_user( &timer->when, timer_callback, timer );
     }
     else timer->timeout = NULL;
@@ -101,8 +101,17 @@
         timer->signaled = 0;
     }
     if (timer->timeout) remove_timeout_user( timer->timeout );
-    timer->when.tv_sec  = sec;
-    timer->when.tv_usec = usec;
+    if (!sec && !usec)
+    {
+        /* special case: use now + period as first expiration */
+        gettimeofday( &timer->when, 0 );
+        add_timeout( &timer->when, period );
+    }
+    else
+    {
+        timer->when.tv_sec  = sec;
+        timer->when.tv_usec = usec;
+    }
     timer->period       = period;
     timer->callback     = callback;
     timer->arg          = arg;