Moved poll handling to the generic part of the server objects.
Fixed busy waiting on POLLERR events.
Merged struct client into struct thread.

diff --git a/server/Makefile.in b/server/Makefile.in
index 60aa6e0..b4ec97a 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -25,7 +25,6 @@
 	semaphore.c \
 	snapshot.c \
 	sock.c \
-	socket.c \
 	thread.c \
 	timer.c \
 	trace.c \
diff --git a/server/change.c b/server/change.c
index 50f193e..8347651 100644
--- a/server/change.c
+++ b/server/change.c
@@ -27,24 +27,26 @@
 
 static const struct object_ops change_ops =
 {
-    sizeof(struct change),
-    change_dump,
-    add_queue,
-    remove_queue,
-    change_signaled,
-    no_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    no_destroy
+    sizeof(struct change),    /* size */
+    change_dump,              /* dump */
+    add_queue,                /* add_queue */
+    remove_queue,             /* remove_queue */
+    change_signaled,          /* signaled */
+    no_satisfied,             /* satisfied */
+    NULL,                     /* get_poll_events */
+    NULL,                     /* poll_event */
+    no_read_fd,               /* get_read_fd */
+    no_write_fd,              /* get_write_fd */
+    no_flush,                 /* flush */
+    no_get_file_info,         /* get_file_info */
+    no_destroy                /* destroy */
 };
 
 
 static struct change *create_change_notification( int subtree, int filter )
 {
     struct change *change;
-    if ((change = alloc_object( &change_ops )))
+    if ((change = alloc_object( &change_ops, -1 )))
     {
         change->subtree = subtree;
         change->filter  = filter;
diff --git a/server/console.c b/server/console.c
index d34fee6..f82528e 100644
--- a/server/console.c
+++ b/server/console.c
@@ -38,8 +38,6 @@
 struct console_input
 {
     struct object         obj;           /* object header */
-    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 */
@@ -49,8 +47,6 @@
 struct screen_buffer
 {
     struct object         obj;           /* object header */
-    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) */
@@ -61,16 +57,12 @@
 
 
 static void console_input_dump( struct object *obj, int verbose );
-static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry );
-static void console_input_remove_queue( struct object *obj, struct wait_queue_entry *entry );
-static int console_input_signaled( struct object *obj, struct thread *thread );
+static int console_input_get_poll_events( struct object *obj );
 static int console_input_get_read_fd( struct object *obj );
 static void console_input_destroy( struct object *obj );
 
 static void screen_buffer_dump( struct object *obj, int verbose );
-static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry *entry );
-static void screen_buffer_remove_queue( struct object *obj, struct wait_queue_entry *entry );
-static int screen_buffer_signaled( struct object *obj, struct thread *thread );
+static int screen_buffer_get_poll_events( struct object *obj );
 static int screen_buffer_get_write_fd( struct object *obj );
 static void screen_buffer_destroy( struct object *obj );
 
@@ -79,32 +71,36 @@
 
 static const struct object_ops console_input_ops =
 {
-    sizeof(struct console_input),
-    console_input_dump,
-    console_input_add_queue,
-    console_input_remove_queue,
-    console_input_signaled,
-    no_satisfied,
-    console_input_get_read_fd,
-    no_write_fd,
-    no_flush,
-    console_get_info,
-    console_input_destroy
+    sizeof(struct console_input),     /* size */
+    console_input_dump,               /* dump */
+    default_poll_add_queue,           /* add_queue */
+    default_poll_remove_queue,        /* remove_queue */
+    default_poll_signaled,            /* signaled */
+    no_satisfied,                     /* satisfied */
+    console_input_get_poll_events,    /* get_poll_events */
+    default_poll_event,               /* poll_event */
+    console_input_get_read_fd,        /* get_read_fd */
+    no_write_fd,                      /* get_write_fd */
+    no_flush,                         /* flush */
+    console_get_info,                 /* get_file_info */
+    console_input_destroy             /* destroy */
 };
 
 static const struct object_ops screen_buffer_ops =
 {
-    sizeof(struct screen_buffer),
-    screen_buffer_dump,
-    screen_buffer_add_queue,
-    screen_buffer_remove_queue,
-    screen_buffer_signaled,
-    no_satisfied,
-    no_read_fd,
-    screen_buffer_get_write_fd,
-    no_flush,
-    console_get_info,
-    screen_buffer_destroy
+    sizeof(struct screen_buffer),     /* size */
+    screen_buffer_dump,               /* dump */
+    default_poll_add_queue,           /* add_queue */
+    default_poll_remove_queue,        /* remove_queue */
+    default_poll_signaled,            /* signaled */
+    no_satisfied,                     /* satisfied */
+    screen_buffer_get_poll_events,    /* get_poll_events */
+    default_poll_event,               /* poll_event */
+    no_read_fd,                       /* get_read_fd */
+    screen_buffer_get_write_fd,       /* get_write_fd */
+    no_flush,                         /* flush */
+    console_get_info,                 /* get_file_info */
+    screen_buffer_destroy             /* destroy */
 };
 
 
@@ -117,21 +113,13 @@
         file_set_error();
         return NULL;
     }
-    if ((console_input = alloc_object( &console_input_ops )))
-    {
-        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;
+    if (!(console_input = alloc_object( &console_input_ops, fd ))) return NULL;
+    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;
+    return &console_input->obj;
 }
 
 static struct object *create_console_output( int fd, struct object *input )
@@ -144,26 +132,15 @@
         file_set_error();
         return NULL;
     }
-    if ((screen_buffer = alloc_object( &screen_buffer_ops )))
-    {
-        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" );
-        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;
-    }
-    close( fd );
-    return NULL;
+    if (!(screen_buffer = alloc_object( &screen_buffer_ops, fd ))) return NULL;
+    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" );
+    console_input->output = screen_buffer;
+    return &screen_buffer->obj;
 }
 
 /* allocate a console for this process */
@@ -223,10 +200,8 @@
     assert( !input->obj.head );
     assert( !output->obj.head );
 
-    change_select_fd( input->select, fd_in );
-    change_select_fd( output->select, fd_out );
-    input->fd   = fd_in;
-    output->fd  = fd_out;
+    change_select_fd( &input->obj, fd_in );
+    change_select_fd( &output->obj, fd_out );
     output->pid = pid;
     release_object( input );
     release_object( output );
@@ -360,54 +335,19 @@
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    fprintf( stderr, "Console input fd=%d\n", console->fd );
+    fprintf( stderr, "Console input fd=%d\n", console->obj.fd );
 }
 
-static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry )
+static int console_input_get_poll_events( struct object *obj )
 {
-    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, POLLIN );
-    add_queue( obj, entry );
-    return 1;
-}
-
-static void console_input_remove_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    struct console_input *console = (struct console_input *)grab_object(obj);
-    assert( obj->ops == &console_input_ops );
-
-    remove_queue( obj, entry );
-    if (!obj->head)  /* last on the queue is gone */
-        set_select_events( console->select, 0 );
-    release_object( obj );
-}
-
-static int console_input_signaled( struct object *obj, struct thread *thread )
-{
-    struct console_input *console = (struct console_input *)obj;
-    assert( obj->ops == &console_input_ops );
-
-    if (check_select_events( console->fd, POLLIN ))
-    {
-        /* stop waiting on select() if we are signaled */
-        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, POLLIN );
-        return 0;
-    }
+    return POLLIN;
 }
 
 static int console_input_get_read_fd( struct object *obj )
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    return dup( console->fd );
+    return dup( console->obj.fd );
 }
 
 static int console_get_info( struct object *obj, struct get_file_info_request *req )
@@ -429,7 +369,6 @@
 {
     struct console_input *console = (struct console_input *)obj;
     assert( obj->ops == &console_input_ops );
-    remove_select_user( console->select );
     if (console->output) console->output->input = NULL;
 }
 
@@ -437,61 +376,25 @@
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    fprintf( stderr, "Console screen buffer fd=%d\n", console->fd );
+    fprintf( stderr, "Console screen buffer fd=%d\n", console->obj.fd );
 }
 
-static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry *entry )
+static int screen_buffer_get_poll_events( struct object *obj )
 {
-    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, POLLOUT );
-    add_queue( obj, entry );
-    return 1;
-}
-
-static void screen_buffer_remove_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    struct screen_buffer *console = (struct screen_buffer *)grab_object(obj);
-    assert( obj->ops == &screen_buffer_ops );
-
-    remove_queue( obj, entry );
-    if (!obj->head)  /* last on the queue is gone */
-        set_select_events( console->select, 0 );
-    release_object( obj );
-}
-
-static int screen_buffer_signaled( struct object *obj, struct thread *thread )
-{
-    struct screen_buffer *console = (struct screen_buffer *)obj;
-    assert( obj->ops == &screen_buffer_ops );
-
-    if (check_select_events( console->fd, POLLOUT ))
-    {
-        /* stop waiting on select() if we are signaled */
-        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, POLLOUT );
-        return 0;
-    }
+    return POLLOUT;
 }
 
 static int screen_buffer_get_write_fd( struct object *obj )
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    return dup( console->fd );
+    return dup( console->obj.fd );
 }
 
 static void screen_buffer_destroy( struct object *obj )
 {
     struct screen_buffer *console = (struct screen_buffer *)obj;
     assert( obj->ops == &screen_buffer_ops );
-    remove_select_user( console->select );
     if (console->input) console->input->output = NULL;
     if (console->title) free( console->title );
 }
diff --git a/server/device.c b/server/device.c
index b9f488e..b2c038e 100644
--- a/server/device.c
+++ b/server/device.c
@@ -34,23 +34,25 @@
 
 static const struct object_ops device_ops =
 {
-    sizeof(struct device),
-    device_dump,
-    no_add_queue,
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    device_get_info,
-    no_destroy
+    sizeof(struct device),    /* size */
+    device_dump,              /* dump */
+    no_add_queue,             /* add_queue */
+    NULL,                     /* remove_queue */
+    NULL,                     /* signaled */
+    NULL,                     /* satisfied */
+    NULL,                     /* get_poll_events */
+    NULL,                     /* poll_event */
+    no_read_fd,               /* get_read_fd */
+    no_write_fd,              /* get_write_fd */
+    no_flush,                 /* flush */
+    device_get_info,          /* get_file_info */
+    no_destroy                /* destroy */
 };
 
 static struct device *create_device( int id )
 {
     struct device *dev;
-    if ((dev = alloc_object( &device_ops )))
+    if ((dev = alloc_object( &device_ops, -1 )))
     {
         dev->id = id;
     }
diff --git a/server/event.c b/server/event.c
index db4a7a0..b5e3e3a 100644
--- a/server/event.c
+++ b/server/event.c
@@ -28,17 +28,19 @@
 
 static const struct object_ops event_ops =
 {
-    sizeof(struct event),
-    event_dump,
-    add_queue,
-    remove_queue,
-    event_signaled,
-    event_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    no_destroy
+    sizeof(struct event),      /* size */
+    event_dump,                /* dump */
+    add_queue,                 /* add_queue */
+    remove_queue,              /* remove_queue */
+    event_signaled,            /* signaled */
+    event_satisfied,           /* satisfied */
+    NULL,                      /* get_poll_events */
+    NULL,                      /* poll_event */
+    no_read_fd,                /* get_read_fd */
+    no_write_fd,               /* get_write_fd */
+    no_flush,                  /* flush */
+    no_get_file_info,          /* get_file_info */
+    no_destroy                 /* destroy */
 };
 
 
diff --git a/server/file.c b/server/file.c
index c80ecce..3cc46bd 100644
--- a/server/file.c
+++ b/server/file.c
@@ -32,8 +32,6 @@
 struct file
 {
     struct object       obj;        /* object header */
-    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) */
@@ -46,9 +44,7 @@
 static struct file *file_hash[NAME_HASH_SIZE];
 
 static void file_dump( struct object *obj, int verbose );
-static int file_add_queue( struct object *obj, struct wait_queue_entry *entry );
-static void file_remove_queue( struct object *obj, struct wait_queue_entry *entry );
-static int file_signaled( struct object *obj, struct thread *thread );
+static int file_get_poll_events( struct object *obj );
 static int file_get_read_fd( struct object *obj );
 static int file_get_write_fd( struct object *obj );
 static int file_flush( struct object *obj );
@@ -57,17 +53,19 @@
 
 static const struct object_ops file_ops =
 {
-    sizeof(struct file),
-    file_dump,
-    file_add_queue,
-    file_remove_queue,
-    file_signaled,
-    no_satisfied,
-    file_get_read_fd,
-    file_get_write_fd,
-    file_flush,
-    file_get_info,
-    file_destroy
+    sizeof(struct file),          /* size */
+    file_dump,                    /* dump */
+    default_poll_add_queue,       /* add_queue */
+    default_poll_remove_queue,    /* remove_queue */
+    default_poll_signaled,        /* signaled */
+    no_satisfied,                 /* satisfied */
+    file_get_poll_events,         /* get_poll_events */
+    default_poll_event,           /* poll_event */
+    file_get_read_fd,             /* get_read_fd */
+    file_get_write_fd,            /* get_write_fd */
+    file_flush,                   /* flush */
+    file_get_info,                /* get_file_info */
+    file_destroy                  /* destroy */
 };
 
 
@@ -103,23 +101,19 @@
     return 0;
 }
 
+/* create a file from a file descriptor */
+/* if the function fails the fd is closed */
 static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing,
                                         unsigned int attrs )
 {
     struct file *file;
-    if ((file = alloc_object( &file_ops )))
+    if ((file = alloc_object( &file_ops, fd )))
     {
         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;
 }
@@ -171,7 +165,11 @@
         goto error;
     }            
 
-    if (!(file = create_file_for_fd( fd, access, sharing, attrs ))) goto error;
+    if (!(file = create_file_for_fd( fd, access, sharing, attrs )))
+    {
+        free( name );
+        return NULL;
+    }
     file->name = name;
     file->next = file_hash[hash];
     file_hash[hash] = file;
@@ -212,81 +210,41 @@
 /* Create a temp file for anonymous mappings */
 struct file *create_temp_file( int access )
 {
-    struct file *file;
     int fd;
 
     if ((fd = create_anonymous_file()) == -1) return NULL;
-    if (!(file = create_file_for_fd( fd, access, 0, 0 ))) close( fd );
-    return file;
+    return create_file_for_fd( fd, access, 0, 0 );
 }
 
 static void file_dump( struct object *obj, int verbose )
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->fd, file->flags, file->name );
+    fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->obj.fd, file->flags, file->name );
 }
 
-static int file_add_queue( struct object *obj, struct wait_queue_entry *entry )
+static int file_get_poll_events( struct object *obj )
 {
     struct file *file = (struct file *)obj;
-    assert( obj->ops == &file_ops );
-    if (!obj->head)  /* first on the queue */
-    {
-        int events = 0;
-        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;
-}
-
-static void file_remove_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    struct file *file = (struct file *)grab_object(obj);
-    assert( obj->ops == &file_ops );
-
-    remove_queue( obj, entry );
-    if (!obj->head)  /* last on the queue is gone */
-        set_select_events( file->select, 0 );
-    release_object( obj );
-}
-
-static int file_signaled( struct object *obj, struct thread *thread )
-{
     int events = 0;
-    struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-
     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 );
-        return 1;
-    }
-    else
-    {
-        /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( file->select, events );
-        return 0;
-    }
+    return events;
 }
 
 static int file_get_read_fd( struct object *obj )
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    return dup( file->fd );
+    return dup( file->obj.fd );
 }
 
 static int file_get_write_fd( struct object *obj )
 {
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
-    return dup( file->fd );
+    return dup( file->obj.fd );
 }
 
 static int file_flush( struct object *obj )
@@ -295,7 +253,7 @@
     struct file *file = (struct file *)grab_object(obj);
     assert( obj->ops == &file_ops );
 
-    ret = (fsync( file->fd ) != -1);
+    ret = (fsync( file->obj.fd ) != -1);
     if (!ret) file_set_error();
     release_object( file );
     return ret;
@@ -307,13 +265,13 @@
     struct file *file = (struct file *)obj;
     assert( obj->ops == &file_ops );
 
-    if (fstat( file->fd, &st ) == -1)
+    if (fstat( file->obj.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->fd)) req->type = FILE_TYPE_CHAR;
+        S_ISSOCK(st.st_mode) || isatty(file->obj.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;
@@ -344,7 +302,6 @@
         if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name );
         free( file->name );
     }
-    remove_select_user( file->select );
 }
 
 /* set the last error depending on errno */
@@ -379,7 +336,7 @@
 
 int file_get_mmap_fd( struct file *file )
 {
-    return dup( file->fd );
+    return dup( file->obj.fd );
 }
 
 static int set_file_pointer( int handle, int *low, int *high, int whence )
@@ -396,7 +353,7 @@
 
     if (!(file = get_file_obj( current->process, handle, 0 )))
         return 0;
-    if ((result = lseek( file->fd, *low, whence )) == -1)
+    if ((result = lseek( file->obj.fd, *low, whence )) == -1)
     {
         /* Check for seek before start of file */
         if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0))
@@ -418,8 +375,8 @@
 
     if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE )))
         return 0;
-    if (((result = lseek( file->fd, 0, SEEK_CUR )) == -1) ||
-        (ftruncate( file->fd, result ) == -1))
+    if (((result = lseek( file->obj.fd, 0, SEEK_CUR )) == -1) ||
+        (ftruncate( file->obj.fd, result ) == -1))
     {
         file_set_error();
         release_object( file );
@@ -440,13 +397,13 @@
         set_error( ERROR_INVALID_PARAMETER );
         return 0;
     }
-    if (fstat( file->fd, &st ) == -1)
+    if (fstat( file->obj.fd, &st ) == -1)
     {
         file_set_error();
         return 0;
     }
     if (st.st_size >= size_low) return 1;  /* already large enough */
-    if (ftruncate( file->fd, size_low ) != -1) return 1;
+    if (ftruncate( file->obj.fd, size_low ) != -1) return 1;
     file_set_error();
     return 0;
 }
@@ -518,7 +475,6 @@
             req->handle = alloc_handle( current->process, file, req->access, 0 );
             release_object( file );
         }
-        else close( fd );
     }
     else file_set_error();
 }
diff --git a/server/handle.c b/server/handle.c
index ea6d6c8..be81511 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -69,17 +69,19 @@
 
 static const struct object_ops handle_table_ops =
 {
-    sizeof(struct handle_table),
-    handle_table_dump,
-    no_add_queue,
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    handle_table_destroy
+    sizeof(struct handle_table),     /* size */
+    handle_table_dump,               /* dump */
+    no_add_queue,                    /* add_queue */
+    NULL,                            /* remove_queue */
+    NULL,                            /* signaled */
+    NULL,                            /* satisfied */
+    NULL,                            /* get_poll_events */
+    NULL,                            /* poll_event */
+    no_read_fd,                      /* get_read_fd */
+    no_write_fd,                     /* get_write_fd */
+    no_flush,                        /* flush */
+    no_get_file_info,                /* get_file_info */
+    handle_table_destroy             /* destroy */
 };
 
 /* dump a handle table */
@@ -127,7 +129,7 @@
     struct handle_table *table;
 
     if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES;
-    if (!(table = alloc_object( &handle_table_ops )))
+    if (!(table = alloc_object( &handle_table_ops, -1 )))
         return NULL;
     table->process = process;
     table->count   = count;
diff --git a/server/mapping.c b/server/mapping.c
index bf45e04..66fefd2 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -32,17 +32,19 @@
 
 static const struct object_ops mapping_ops =
 {
-    sizeof(struct mapping),
-    mapping_dump,
-    no_add_queue,
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    mapping_destroy
+    sizeof(struct mapping),      /* size */
+    mapping_dump,                /* dump */
+    no_add_queue,                /* add_queue */
+    NULL,                        /* remove_queue */
+    NULL,                        /* signaled */
+    NULL,                        /* satisfied */
+    NULL,                        /* get_poll_events */
+    NULL,                        /* poll_event */
+    no_read_fd,                  /* get_read_fd */
+    no_write_fd,                 /* get_write_fd */
+    no_flush,                    /* flush */
+    no_get_file_info,            /* get_file_info */
+    mapping_destroy              /* destroy */
 };
 
 #ifdef __i386__
diff --git a/server/mutex.c b/server/mutex.c
index 1ce86f9..c818d8a 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -32,17 +32,19 @@
 
 static const struct object_ops mutex_ops =
 {
-    sizeof(struct mutex),
-    mutex_dump,
-    add_queue,
-    remove_queue,
-    mutex_signaled,
-    mutex_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    mutex_destroy
+    sizeof(struct mutex),      /* size */
+    mutex_dump,                /* dump */
+    add_queue,                 /* add_queue */
+    remove_queue,              /* remove_queue */
+    mutex_signaled,            /* signaled */
+    mutex_satisfied,           /* satisfied */
+    NULL,                      /* get_poll_events */
+    NULL,                      /* poll_event */
+    no_read_fd,                /* get_read_fd */
+    no_write_fd,               /* get_write_fd */
+    no_flush,                  /* flush */
+    no_get_file_info,          /* get_file_info */
+    mutex_destroy              /* destroy */
 };
 
 
diff --git a/server/object.c b/server/object.c
index 9d44965..28cfe67 100644
--- a/server/object.c
+++ b/server/object.c
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "winerror.h"
 #include "thread.h"
@@ -121,23 +122,34 @@
 }
 
 /* allocate and initialize an object */
-void *alloc_object( const struct object_ops *ops )
+/* if the function fails the fd is closed */
+void *alloc_object( const struct object_ops *ops, int fd )
 {
     struct object *obj = mem_alloc( ops->size );
     if (obj)
     {
         obj->refcount = 1;
+        obj->fd       = fd;
+        obj->select   = -1;
         obj->ops      = ops;
         obj->head     = NULL;
         obj->tail     = NULL;
         obj->name     = NULL;
+        if ((fd != -1) && (add_select_user( obj ) == -1))
+        {
+            close( fd );
+            free( obj );
+            return NULL;
+        }
 #ifdef DEBUG_OBJECTS
         obj->prev = NULL;
         if ((obj->next = first) != NULL) obj->next->prev = obj;
         first = obj;
 #endif
+        return obj;
     }
-    return obj;
+    if (fd != -1) close( fd );
+    return NULL;
 }
 
 void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len )
@@ -145,7 +157,7 @@
     struct object *obj;
     struct object_name *name_ptr;
 
-    if (!name || !len) return alloc_object( ops );
+    if (!name || !len) return alloc_object( ops, -1 );
     if (!(name_ptr = alloc_name( name, len ))) return NULL;
 
     if ((obj = find_object( name_ptr->name, name_ptr->len )))
@@ -159,7 +171,7 @@
         set_error( ERROR_INVALID_HANDLE );
         return NULL;
     }
-    if ((obj = alloc_object( ops )))
+    if ((obj = alloc_object( ops, -1 )))
     {
         set_object_name( obj, name_ptr );
         clear_error();
@@ -199,14 +211,16 @@
         /* if the refcount is 0, nobody can be in the wait queue */
         assert( !obj->head );
         assert( !obj->tail );
+        obj->ops->destroy( obj );
         if (obj->name) free_name( obj );
+        if (obj->select != -1) remove_select_user( obj );
+        if (obj->fd != -1) close( obj->fd );
 #ifdef DEBUG_OBJECTS
         if (obj->next) obj->next->prev = obj->prev;
         if (obj->prev) obj->prev->next = obj->next;
         else first = obj->next;
-#endif
-        obj->ops->destroy( obj );
         memset( obj, 0xaa, obj->ops->size );
+#endif
         free( obj );
     }
 }
@@ -224,7 +238,7 @@
     return NULL;
 }
 
-/* functions for unimplemented object operations */
+/* functions for unimplemented/default object operations */
 
 int no_add_queue( struct object *obj, struct wait_queue_entry *entry )
 {
@@ -265,9 +279,45 @@
 {
 }
 
-void default_select_event( int event, void *private )
+/* default add_queue() routine for objects that poll() on an fd */
+int default_poll_add_queue( struct object *obj, struct wait_queue_entry *entry )
 {
-    struct object *obj = (struct object *)private;
-    assert( obj );
+    if (!obj->head)  /* first on the queue */
+        set_select_events( obj, obj->ops->get_poll_events( obj ) );
+    add_queue( obj, entry );
+    return 1;
+}
+
+/* default remove_queue() routine for objects that poll() on an fd */
+void default_poll_remove_queue( struct object *obj, struct wait_queue_entry *entry )
+{
+    grab_object(obj);
+    remove_queue( obj, entry );
+    if (!obj->head)  /* last on the queue is gone */
+        set_select_events( obj, 0 );
+    release_object( obj );
+}
+
+/* default signaled() routine for objects that poll() on an fd */
+int default_poll_signaled( struct object *obj, struct thread *thread )
+{
+    int events = obj->ops->get_poll_events( obj );
+
+    if (check_select_events( obj->fd, events ))
+    {
+        /* stop waiting on select() if we are signaled */
+        set_select_events( obj, 0 );
+        return 1;
+    }
+    /* restart waiting on select() if we are no longer signaled */
+    if (obj->head) set_select_events( obj, events );
+    return 0;
+}
+
+/* default handler for poll() events */
+void default_poll_event( struct object *obj, int event )
+{
+    /* an error occurred, stop polling this fd to avoid busy-looping */
+    if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1 );
     wake_up( obj, 0 );
 }
diff --git a/server/object.h b/server/object.h
index d5335f1..5825dce 100644
--- a/server/object.h
+++ b/server/object.h
@@ -41,6 +41,10 @@
     int  (*signaled)(struct object *,struct thread *);
     /* wait satisfied; return 1 if abandoned */
     int  (*satisfied)(struct object *,struct thread *);
+    /* get the events we want to poll() for on this object */
+    int  (*get_poll_events)(struct object *);
+    /* a poll() event occured */
+    void (*poll_event)(struct object *,int event);
     /* return a Unix fd that can be used to read from the object */
     int  (*get_read_fd)(struct object *);
     /* return a Unix fd that can be used to write to the object */
@@ -55,7 +59,9 @@
 
 struct object
 {
-    unsigned int              refcount;
+    unsigned int              refcount;    /* reference count */
+    int                       fd;          /* file descriptor */
+    int                       select;      /* select() user id */
     const struct object_ops  *ops;
     struct wait_queue_entry  *head;
     struct wait_queue_entry  *tail;
@@ -68,7 +74,7 @@
 
 extern void *mem_alloc( size_t size );  /* malloc wrapper */
 extern void *memdup( const void *data, size_t len );
-extern void *alloc_object( const struct object_ops *ops );
+extern void *alloc_object( const struct object_ops *ops, int fd );
 extern void dump_object_name( struct object *obj );
 extern void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len );
 /* grab/release_object can take any pointer, but you better make sure */
@@ -83,17 +89,20 @@
 extern int no_flush( struct object *obj );
 extern int no_get_file_info( struct object *obj, struct get_file_info_request *info );
 extern void no_destroy( struct object *obj );
-extern void default_select_event( int event, void *private );
+extern int default_poll_add_queue( struct object *obj, struct wait_queue_entry *entry );
+extern void default_poll_remove_queue( struct object *obj, struct wait_queue_entry *entry );
+extern int default_poll_signaled( struct object *obj, struct thread *thread );
+extern void default_poll_event( struct object *obj, int event );
 #ifdef DEBUG_OBJECTS
 extern void dump_objects(void);
 #endif
 
 /* select functions */
 
-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 add_select_user( struct object *obj );
+extern void remove_select_user( struct object *obj );
+extern void change_select_fd( struct object *obj, int fd );
+extern void set_select_events( struct object *obj, int events );
 extern int check_select_events( int fd, int events );
 extern void select_loop(void);
 
@@ -114,15 +123,6 @@
             ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec)));
 }
 
-/* socket functions */
-
-struct client;
-
-extern struct client *add_client( int client_fd, struct thread *self );
-extern void remove_client( struct client *client, int exit_code );
-extern void client_pass_fd( struct client *client, int pass_fd );
-extern void client_reply( struct client *client, unsigned int res );
-
 /* event functions */
 
 struct event;
diff --git a/server/pipe.c b/server/pipe.c
index f449ca6..49ec7aa 100644
--- a/server/pipe.c
+++ b/server/pipe.c
@@ -33,15 +33,11 @@
 {
     struct object       obj;         /* object header */
     struct pipe        *other;       /* the pipe other end */
-    int                 fd;          /* file descriptor */
-    int                 select;      /* select user id */
     enum side           side;        /* which side of the pipe is this */
 };
 
 static void pipe_dump( struct object *obj, int verbose );
-static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry );
-static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry );
-static int pipe_signaled( struct object *obj, struct thread *thread );
+static int pipe_get_poll_events( struct object *obj );
 static int pipe_get_read_fd( struct object *obj );
 static int pipe_get_write_fd( struct object *obj );
 static int pipe_get_info( struct object *obj, struct get_file_info_request *req );
@@ -49,17 +45,19 @@
 
 static const struct object_ops pipe_ops =
 {
-    sizeof(struct pipe),
-    pipe_dump,
-    pipe_add_queue,
-    pipe_remove_queue,
-    pipe_signaled,
-    no_satisfied,
-    pipe_get_read_fd,
-    pipe_get_write_fd,
-    no_flush,
-    pipe_get_info,
-    pipe_destroy
+    sizeof(struct pipe),          /* size */
+    pipe_dump,                    /* dump */
+    default_poll_add_queue,       /* add_queue */
+    default_poll_remove_queue,    /* remove_queue */
+    default_poll_signaled,        /* signaled */
+    no_satisfied,                 /* satisfied */
+    pipe_get_poll_events,         /* get_poll_events */
+    default_poll_event,           /* poll_event */
+    pipe_get_read_fd,             /* get_read_fd */
+    pipe_get_write_fd,            /* get_write_fd */
+    no_flush,                     /* flush */
+    pipe_get_info,                /* get_file_info */
+    pipe_destroy                  /* destroy */
 };
 
 
@@ -67,16 +65,10 @@
 {
     struct pipe *pipe;
 
-    if ((pipe = alloc_object( &pipe_ops )))
+    if ((pipe = alloc_object( &pipe_ops, fd )))
     {
-        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;
 }
@@ -104,8 +96,7 @@
         }
         release_object( read_pipe );
     }
-    close( fd[0] );
-    close( fd[1] );
+    else close( fd[1] );
     return 0;
 }
 
@@ -114,49 +105,14 @@
     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->fd );
+             (pipe->side == READ_SIDE) ? "read" : "write", pipe->obj.fd );
 }
 
-static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry )
+static int pipe_get_poll_events( struct object *obj )
 {
     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) ? POLLIN : POLLOUT );
-    add_queue( obj, entry );
-    return 1;
-}
-
-static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    struct pipe *pipe = (struct pipe *)grab_object(obj);
-    assert( obj->ops == &pipe_ops );
-
-    remove_queue( obj, entry );
-    if (!obj->head)  /* last on the queue is gone */
-        set_select_events( pipe->select, 0 );
-    release_object( obj );
-}
-
-static int pipe_signaled( struct object *obj, struct thread *thread )
-{
-    int event;
-    struct pipe *pipe = (struct pipe *)obj;
-    assert( obj->ops == &pipe_ops );
-
-    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 );
-        return 1;
-    }
-    else
-    {
-        /* restart waiting on select() if we are no longer signaled */
-        if (obj->head) set_select_events( pipe->select, event );
-        return 0;
-    }
+    return (pipe->side == READ_SIDE) ? POLLIN : POLLOUT;
 }
 
 static int pipe_get_read_fd( struct object *obj )
@@ -174,7 +130,7 @@
         set_error( ERROR_ACCESS_DENIED );
         return -1;
     }
-    return dup( pipe->fd );
+    return dup( pipe->obj.fd );
 }
 
 static int pipe_get_write_fd( struct object *obj )
@@ -192,7 +148,7 @@
         set_error( ERROR_ACCESS_DENIED );
         return -1;
     }
-    return dup( pipe->fd );
+    return dup( pipe->obj.fd );
 }
 
 static int pipe_get_info( struct object *obj, struct get_file_info_request *req )
@@ -216,7 +172,6 @@
     assert( obj->ops == &pipe_ops );
 
     if (pipe->other) pipe->other->other = NULL;
-    remove_select_user( pipe->select );
 }
 
 /* create an anonymous pipe */
diff --git a/server/process.c b/server/process.c
index 3bc200d..20892ba 100644
--- a/server/process.c
+++ b/server/process.c
@@ -36,17 +36,19 @@
 
 static const struct object_ops process_ops =
 {
-    sizeof(struct process),
-    process_dump,
-    add_queue,
-    remove_queue,
-    process_signaled,
-    no_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    process_destroy
+    sizeof(struct process),      /* size */
+    process_dump,                /* dump */
+    add_queue,                   /* add_queue */
+    remove_queue,                /* remove_queue */
+    process_signaled,            /* signaled */
+    no_satisfied,                /* satisfied */
+    NULL,                        /* get_poll_events */
+    NULL,                        /* poll_event */
+    no_read_fd,                  /* get_read_fd */
+    no_write_fd,                 /* get_write_fd */
+    no_flush,                    /* flush */
+    no_get_file_info,            /* get_file_info */
+    process_destroy              /* destroy */
 };
 
 
@@ -56,7 +58,7 @@
 {
     struct process *process;
 
-    if (!(process = alloc_object( &process_ops ))) return NULL;
+    if (!(process = alloc_object( &process_ops, -1 ))) return NULL;
     process->next            = NULL;
     process->prev            = NULL;
     process->thread_list     = NULL;
diff --git a/server/registry.c b/server/registry.c
index b28a2ce..e7dd55f 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -106,17 +106,19 @@
 
 static const struct object_ops key_ops =
 {
-    sizeof(struct key),
-    key_dump,
-    no_add_queue,
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    key_destroy
+    sizeof(struct key),      /* size */
+    key_dump,                /* dump */
+    no_add_queue,            /* add_queue */
+    NULL,                    /* remove_queue */
+    NULL,                    /* signaled */
+    NULL,                    /* satisfied */
+    NULL,                    /* get_poll_events */
+    NULL,                    /* poll_event */
+    no_read_fd,              /* get_read_fd */
+    no_write_fd,             /* get_write_fd */
+    no_flush,                /* flush */
+    no_get_file_info,        /* get_file_info */
+    key_destroy              /* destroy */
 };
 
 
@@ -322,7 +324,7 @@
 static struct key *alloc_key( const WCHAR *name, time_t modif )
 {
     struct key *key;
-    if ((key = (struct key *)alloc_object( &key_ops )))
+    if ((key = (struct key *)alloc_object( &key_ops, -1 )))
     {
         key->name        = NULL;
         key->class       = NULL;
diff --git a/server/request.c b/server/request.c
index 56b394d..62b15e9 100644
--- a/server/request.c
+++ b/server/request.c
@@ -4,13 +4,20 @@
  * Copyright (C) 1998 Alexandre Julliard
  */
 
+#include "config.h"
+
 #include <assert.h>
-#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
-#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
 #include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
 #include <sys/uio.h>
 #include <unistd.h>
 
@@ -22,9 +29,30 @@
 #include "server.h"
 #define WANT_REQUEST_HANDLERS
 #include "request.h"
+
+/* Some versions of glibc don't define this */
+#ifndef SCM_RIGHTS
+#define SCM_RIGHTS 1
+#endif
+
  
 struct thread *current = NULL;  /* thread handling the current request */
 
+
+/* socket communication static structures */
+static struct iovec myiovec;
+static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
+#ifndef HAVE_MSGHDR_ACCRIGHTS
+struct cmsg_fd
+{
+    int len;   /* sizeof structure */
+    int level; /* SOL_SOCKET */
+    int type;  /* SCM_RIGHTS */
+    int fd;    /* fd to pass */
+};
+static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 };
+#endif  /* HAVE_MSGHDR_ACCRIGHTS */
+
 /* complain about a protocol error and terminate the client connection */
 void fatal_protocol_error( struct thread *thread, const char *err, ... )
 {
@@ -34,11 +62,11 @@
     fprintf( stderr, "Protocol error:%p: ", thread );
     vfprintf( stderr, err, args );
     va_end( args );
-    remove_client( thread->client, PROTOCOL_ERROR );
+    kill_thread( thread, PROTOCOL_ERROR );
 }
 
 /* call a request handler */
-void call_req_handler( struct thread *thread, enum request req, int fd )
+static void call_req_handler( struct thread *thread, enum request req, int fd )
 {
     current = thread;
     clear_error();
@@ -65,31 +93,109 @@
     current = NULL;
 }
 
-/* a thread has been killed */
-void call_kill_handler( struct thread *thread, int exit_code )
-{
-    /* must be reentrant WRT call_req_handler */
-    struct thread *old_current = current;
-    current = thread;
-    if (current)
-    {
-        if (debug_level) trace_kill( exit_code );
-        thread_killed( current, exit_code );
-    }
-    current = (old_current != thread) ? old_current : NULL;
-}
-
 /* set the fd to pass to the thread */
 void set_reply_fd( struct thread *thread, int pass_fd )
 {
-    client_pass_fd( thread->client, pass_fd );
+    assert( thread->pass_fd == -1 );
+    thread->pass_fd = pass_fd;
 }
 
 /* send a reply to a thread */
 void send_reply( struct thread *thread )
 {
     if (thread->state == SLEEPING) thread->state = RUNNING;
-    client_reply( thread->client, thread->error );
+    if (debug_level) trace_reply( thread );
+    if (!write_request( thread )) set_select_events( &thread->obj, POLLOUT );
+}
+
+/* read a message from a client that has something to say */
+void read_request( struct thread *thread )
+{
+    int ret;
+    enum request req;
+
+#ifdef HAVE_MSGHDR_ACCRIGHTS
+    msghdr.msg_accrightslen = sizeof(int);
+    msghdr.msg_accrights = (void *)&thread->pass_fd;
+#else  /* HAVE_MSGHDR_ACCRIGHTS */
+    msghdr.msg_control    = &cmsg;
+    msghdr.msg_controllen = sizeof(cmsg);
+    cmsg.fd = -1;
+#endif  /* HAVE_MSGHDR_ACCRIGHTS */
+
+    assert( thread->pass_fd == -1 );
+
+    myiovec.iov_base = (void *)&req;
+    myiovec.iov_len  = sizeof(req);
+
+    ret = recvmsg( thread->obj.fd, &msghdr, 0 );
+#ifndef HAVE_MSGHDR_ACCRIGHTS
+    thread->pass_fd = cmsg.fd;
+#endif
+
+    if (ret == sizeof(req))
+    {
+        int pass_fd = thread->pass_fd;
+        thread->pass_fd = -1;
+        call_req_handler( thread, req, pass_fd );
+        if (pass_fd != -1) close( pass_fd );
+        return;
+    }
+    if (ret == -1)
+    {
+        perror("recvmsg");
+        kill_thread( thread, BROKEN_PIPE );
+        return;
+    }
+    if (!ret)  /* closed pipe */
+    {
+        kill_thread( thread, BROKEN_PIPE );
+        return;
+    }
+    fatal_protocol_error( thread, "partial message received %d/%d\n", ret, sizeof(req) );
+}
+
+/* send a message to a client that is ready to receive something */
+int write_request( struct thread *thread )
+{
+    int ret;
+
+    if (thread->pass_fd == -1)
+    {
+        ret = write( thread->obj.fd, &thread->error, sizeof(thread->error) );
+        if (ret == sizeof(thread->error)) goto ok;
+    }
+    else  /* we have an fd to send */
+    {
+#ifdef HAVE_MSGHDR_ACCRIGHTS
+        msghdr.msg_accrightslen = sizeof(int);
+        msghdr.msg_accrights = (void *)&thread->pass_fd;
+#else  /* HAVE_MSGHDR_ACCRIGHTS */
+        msghdr.msg_control    = &cmsg;
+        msghdr.msg_controllen = sizeof(cmsg);
+        cmsg.fd = thread->pass_fd;
+#endif  /* HAVE_MSGHDR_ACCRIGHTS */
+
+        myiovec.iov_base = (void *)&thread->error;
+        myiovec.iov_len  = sizeof(thread->error);
+
+        ret = sendmsg( thread->obj.fd, &msghdr, 0 );
+        close( thread->pass_fd );
+        thread->pass_fd = -1;
+        if (ret == sizeof(thread->error)) goto ok;
+    }
+    if (ret == -1)
+    {
+        if (errno == EWOULDBLOCK) return 0;  /* not a fatal error */
+        if (errno != EPIPE) perror("sendmsg");
+    }
+    else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(thread->error) );
+    kill_thread( thread, BROKEN_PIPE );
+    return -1;
+
+ ok:
+    set_select_events( &thread->obj, POLLIN );
+    return 1;
 }
 
 /* set the debug level */
diff --git a/server/request.h b/server/request.h
index d34c798..766b924 100644
--- a/server/request.h
+++ b/server/request.h
@@ -26,17 +26,17 @@
 
 /* request functions */
 
+extern void read_request( struct thread *thread );
+extern int write_request( struct thread *thread );
 extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
-extern void call_req_handler( struct thread *thread, enum request req, int fd );
 extern void call_timeout_handler( void *thread );
-extern void call_kill_handler( struct thread *thread, int exit_code );
 extern void set_reply_fd( struct thread *thread, int pass_fd );
 extern void send_reply( struct thread *thread );
 
 extern void trace_request( enum request req, int fd );
 extern void trace_timeout(void);
-extern void trace_kill( int exit_code );
-extern void trace_reply( struct thread *thread, unsigned int res, int pass_fd );
+extern void trace_kill( struct thread *thread );
+extern void trace_reply( struct thread *thread );
 
 /* get the request buffer */
 static inline void *get_req_ptr( struct thread *thread )
diff --git a/server/select.c b/server/select.c
index a1286cf..98b4817 100644
--- a/server/select.c
+++ b/server/select.c
@@ -19,12 +19,6 @@
 #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 */
@@ -34,32 +28,31 @@
     void                 *private;    /* callback private data */
 };
 
-static struct poll_user *poll_users;        /* users array */
+static struct object **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 object **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 */
 
 
 /* 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 )
+int add_select_user( struct object *obj )
 {
     int ret;
     if (freelist)
     {
         ret = freelist - poll_users;
-        freelist = poll_users[ret].private;
-        assert( !poll_users[ret].func );
+        freelist = (struct object **)poll_users[ret];
     }
     else
     {
         if (nb_users == allocated_users)
         {
-            struct poll_user *newusers;
+            struct object **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;
@@ -74,43 +67,53 @@
         }
         ret = nb_users++;
     }
-    pollfd[ret].fd = fd;
+    pollfd[ret].fd = obj->fd;
     pollfd[ret].events = 0;
     pollfd[ret].revents = 0;
-    poll_users[ret].func = func;
-    poll_users[ret].private = private;
+    poll_users[ret] = obj;
+    obj->select = ret;
     active_users++;
     return ret;
 }
 
-/* remove a user and close its fd */
-void remove_select_user( int user )
+/* remove an object from the select list and close its fd */
+void remove_select_user( struct object *obj )
 {
-    if (user == -1) return;  /* avoids checking in all callers */
-    assert( poll_users[user].func );
-    close( pollfd[user].fd );
+    int user = obj->select;
+    assert( poll_users[user] == obj );
     pollfd[user].fd = -1;
     pollfd[user].events = 0;
     pollfd[user].revents = 0;
-    poll_users[user].func = NULL;
-    poll_users[user].private = freelist;
+    poll_users[user] = (struct object *)freelist;
     freelist = &poll_users[user];
+    close( obj->fd );
+    obj->fd = -1;
+    obj->select = -1;
     active_users--;
 }
 
-/* change the fd of a select user (the old fd is closed) */
-void change_select_fd( int user, int fd )
+/* change the fd of an object (the old fd is closed) */
+void change_select_fd( struct object *obj, int fd )
 {
-    assert( poll_users[user].func );
-    close( pollfd[user].fd );
+    int user = obj->select;
+    assert( poll_users[user] == obj );
     pollfd[user].fd = fd;
+    close( obj->fd );
+    obj->fd = fd;
 }
 
 /* set the events that select waits for on this fd */
-void set_select_events( int user, int events )
+void set_select_events( struct object *obj, int events )
 {
-    assert( poll_users[user].func );
-    pollfd[user].events = events;
+    int user = obj->select;
+    assert( poll_users[user] == obj );
+    if (events == -1)  /* stop waiting on this fd completely */
+    {
+        pollfd[user].fd = -1;
+        pollfd[user].events = 0;
+        pollfd[user].revents = 0;
+    }
+    else if (pollfd[user].fd != -1) pollfd[user].events = events;
 }
 
 /* check if events are pending */
@@ -259,7 +262,7 @@
             {
                 if (pollfd[i].revents)
                 {
-                    poll_users[i].func( pollfd[i].revents, poll_users[i].private );
+                    poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );
                     if (!--ret) break;
                 }
             }
diff --git a/server/semaphore.c b/server/semaphore.c
index 36656f4..221d533 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -28,17 +28,19 @@
 
 static const struct object_ops semaphore_ops =
 {
-    sizeof(struct semaphore),
-    semaphore_dump,
-    add_queue,
-    remove_queue,
-    semaphore_signaled,
-    semaphore_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    no_destroy
+    sizeof(struct semaphore),      /* size */
+    semaphore_dump,                /* dump */
+    add_queue,                     /* add_queue */
+    remove_queue,                  /* remove_queue */
+    semaphore_signaled,            /* signaled */
+    semaphore_satisfied,           /* satisfied */
+    NULL,                          /* get_poll_events */
+    NULL,                          /* poll_event */
+    no_read_fd,                    /* get_read_fd */
+    no_write_fd,                   /* get_write_fd */
+    no_flush,                      /* flush */
+    no_get_file_info,              /* get_file_info */
+    no_destroy                     /* destroy */
 };
 
 
diff --git a/server/snapshot.c b/server/snapshot.c
index 5ac30cc..173fb9d 100644
--- a/server/snapshot.c
+++ b/server/snapshot.c
@@ -33,17 +33,19 @@
 
 static const struct object_ops snapshot_ops =
 {
-    sizeof(struct snapshot),
-    snapshot_dump,
-    no_add_queue,
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    NULL,  /* should never get called */
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    snapshot_destroy
+    sizeof(struct snapshot),      /* size */
+    snapshot_dump,                /* dump */
+    no_add_queue,                 /* add_queue */
+    NULL,                         /* remove_queue */
+    NULL,                         /* signaled */
+    NULL,                         /* satisfied */
+    NULL,                         /* get_poll_events */
+    NULL,                         /* poll_event */
+    no_read_fd,                   /* get_read_fd */
+    no_write_fd,                  /* get_write_fd */
+    no_flush,                     /* flush */
+    no_get_file_info,             /* get_file_info */
+    snapshot_destroy              /* destroy */
 };
 
 
@@ -52,7 +54,7 @@
 {
     struct snapshot *snapshot;
 
-    if ((snapshot = alloc_object( &snapshot_ops )))
+    if ((snapshot = alloc_object( &snapshot_ops, -1 )))
     {
         if (flags & TH32CS_SNAPPROCESS)
             snapshot->process = process_snap( &snapshot->process_count );
diff --git a/server/sock.c b/server/sock.c
index cb9928f..531231b 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -42,8 +42,6 @@
 struct sock
 {
     struct object       obj;         /* object header */
-    int                 fd;          /* file descriptor */
-    int                 select;      /* select user */
     unsigned int        state;       /* status bits */
     unsigned int        mask;        /* event mask */
     unsigned int        hmask;       /* held (blocked) events */
@@ -53,51 +51,36 @@
 };
 
 static void sock_dump( struct object *obj, int verbose );
-static int sock_add_queue( struct object *obj, struct wait_queue_entry *entry );
-static void sock_remove_queue( struct object *obj, struct wait_queue_entry *entry );
 static int sock_signaled( struct object *obj, struct thread *thread );
+static int sock_get_poll_events( struct object *obj );
+static void sock_poll_event( struct object *obj, int event );
 static int sock_get_fd( struct object *obj );
 static void sock_destroy( struct object *obj );
 static void sock_set_error(void);
 
 static const struct object_ops sock_ops =
 {
-    sizeof(struct sock),
-    sock_dump,
-    sock_add_queue,
-    sock_remove_queue,
-    sock_signaled,
-    no_satisfied,
-    sock_get_fd,
-    sock_get_fd,
-    no_flush,
-    no_get_file_info,
-    sock_destroy
+    sizeof(struct sock),          /* size */
+    sock_dump,                    /* dump */
+    add_queue,                    /* add_queue */
+    remove_queue,                 /* remove_queue */
+    sock_signaled,                /* signaled */
+    no_satisfied,                 /* satisfied */
+    sock_get_poll_events,         /* get_poll_events */
+    sock_poll_event,              /* poll_event */
+    sock_get_fd,                  /* get_read_fd */
+    sock_get_fd,                  /* get_write_fd */
+    no_flush,                     /* flush */
+    no_get_file_info,             /* get_file_info */
+    sock_destroy                  /* destroy */
 };
 
-static int sock_event( struct sock *sock )
-{
-    unsigned int mask = sock->mask & sock->state & ~sock->hmask;
-    int ev = 0;
-
-    if (sock->state & WS_FD_CONNECT)
-        /* connecting, wait for writable */
-        return POLLOUT;
-    if (sock->state & WS_FD_LISTENING)
-        /* listening, wait for readable */
-        return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;
-
-    if (mask & FD_READ)  ev |= POLLIN;
-    if (mask & FD_WRITE) ev |= POLLOUT;
-    return ev;
-}
-
 static void sock_reselect( struct sock *sock )
 {
-    int ev = sock_event( sock );
+    int ev = sock_get_poll_events( &sock->obj );
     if (debug_level)
-        fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->fd, ev);
-    set_select_events( sock->select, ev );
+        fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->obj.fd, ev);
+    set_select_events( &sock->obj, ev );
 }
 
 inline static int sock_error(int s)
@@ -109,13 +92,13 @@
     return optval;
 }
 
-static void sock_select_event( int event, void *private )
+static void sock_poll_event( struct object *obj, int event )
 {
-    struct sock *sock = (struct sock *)private;
+    struct sock *sock = (struct sock *)obj;
     unsigned int emask;
     assert( sock->obj.ops == &sock_ops );
     if (debug_level)
-        fprintf(stderr, "socket %d select event: %x\n", sock->fd, event);
+        fprintf(stderr, "socket %d select event: %x\n", sock->obj.fd, event);
     if (sock->state & WS_FD_CONNECT)
     {
         /* connecting */
@@ -127,16 +110,16 @@
             sock->pmask |= FD_CONNECT;
             sock->errors[FD_CONNECT_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d connection success\n", sock->fd);
+                fprintf(stderr, "socket %d connection success\n", sock->obj.fd);
         }
         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->fd );
+            sock->errors[FD_CONNECT_BIT] = sock_error( sock->obj.fd );
             if (debug_level)
-                fprintf(stderr, "socket %d connection failure\n", sock->fd);
+                fprintf(stderr, "socket %d connection failure\n", sock->obj.fd);
         }
     } else
     if (sock->state & WS_FD_LISTENING)
@@ -153,7 +136,7 @@
         {
             /* failed incoming connection? */
             sock->pmask |= FD_ACCEPT;
-            sock->errors[FD_ACCEPT_BIT] = sock_error( sock->fd );
+            sock->errors[FD_ACCEPT_BIT] = sock_error( sock->obj.fd );
             sock->hmask |= FD_ACCEPT;
         }
     } else
@@ -161,27 +144,12 @@
         /* normal data flow */
         if (event & POLLIN)
         {
-            /* make sure there's data here */
-            int bytes = 0;
-            ioctl(sock->fd, FIONREAD, (char*)&bytes);
-            if (bytes)
-            {
-                /* incoming data */
-                sock->pmask |= FD_READ;
-                sock->hmask |= FD_READ;
-                sock->errors[FD_READ_BIT] = 0;
-                if (debug_level)
-                    fprintf(stderr, "socket %d has %d bytes\n", sock->fd, bytes);
-            }
-            else
-            {
-                /* 0 bytes readable == socket closed cleanly */
-                sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE);
-                sock->pmask |= FD_CLOSE;
-                sock->errors[FD_CLOSE_BIT] = 0;
-                if (debug_level)
-                    fprintf(stderr, "socket %d is closing\n", sock->fd);
-            }
+            /* incoming data */
+            sock->pmask |= FD_READ;
+            sock->hmask |= FD_READ;
+            sock->errors[FD_READ_BIT] = 0;
+            if (debug_level)
+                fprintf(stderr, "socket %d is readable\n", sock->obj.fd );
         }
         if (event & POLLOUT)
         {
@@ -189,27 +157,24 @@
             sock->hmask |= FD_WRITE;
             sock->errors[FD_WRITE_BIT] = 0;
             if (debug_level)
-                fprintf(stderr, "socket %d is writable\n", sock->fd);
+                fprintf(stderr, "socket %d is writable\n", sock->obj.fd);
+        }
+        if (event & POLLPRI)
+        {
+            sock->pmask |= FD_OOB;
+            sock->hmask |= FD_OOB;
+            if (debug_level)
+                fprintf(stderr, "socket %d got OOB data\n", sock->obj.fd);
         }
         if (event & (POLLERR|POLLHUP))
         {
-            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->fd, sock->errors[FD_CLOSE_BIT]);
-            }
-            else
-            {
-                /* no error, OOB data? */
-                sock->pmask |= FD_OOB;
-                sock->hmask |= FD_OOB;
-                if (debug_level)
-                    fprintf(stderr, "socket %d got OOB data\n", sock->fd);
-            }
+            sock->errors[FD_CLOSE_BIT] = sock_error( sock->obj.fd );
+            /* 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->obj.fd, sock->errors[FD_CLOSE_BIT]);
         }
     }
 
@@ -217,7 +182,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->fd, emask);
+        fprintf(stderr, "socket %d pending events: %x\n", sock->obj.fd, emask);
     if (emask && sock->event) {
         if (debug_level) fprintf(stderr, "signalling event ptr %p\n", sock->event);
         set_event(sock->event);
@@ -233,34 +198,36 @@
     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->fd, sock->state,
+            sock->obj.fd, sock->state,
             sock->mask, sock->pmask, sock->hmask );
 }
 
-static int sock_add_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-/*    struct sock *sock = (struct sock *)obj; */
-    assert( obj->ops == &sock_ops );
-
-    add_queue( obj, entry );
-    return 1;
-}
-
-static void sock_remove_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-/*    struct sock *sock = (struct sock *)grab_object(obj); */
-    assert( obj->ops == &sock_ops );
-
-    remove_queue( obj, entry );
-/*    release_object( obj ); */
-}
-
 static int sock_signaled( struct object *obj, struct thread *thread )
 {
     struct sock *sock = (struct sock *)obj;
     assert( obj->ops == &sock_ops );
 
-    return check_select_events( sock->fd, sock_event( sock ) );
+    return check_select_events( sock->obj.fd, sock_get_poll_events( &sock->obj ) );
+}
+
+static int sock_get_poll_events( struct object *obj )
+{
+    struct sock *sock = (struct sock *)obj;
+    unsigned int mask = sock->mask & sock->state & ~sock->hmask;
+    int ev = 0;
+
+    assert( obj->ops == &sock_ops );
+
+    if (sock->state & WS_FD_CONNECT)
+        /* connecting, wait for writable */
+        return POLLOUT;
+    if (sock->state & WS_FD_LISTENING)
+        /* listening, wait for readable */
+        return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;
+
+    if (mask & FD_READ)  ev |= POLLIN | POLLPRI;
+    if (mask & FD_WRITE) ev |= POLLOUT;
+    return ev;
 }
 
 static int sock_get_fd( struct object *obj )
@@ -268,7 +235,7 @@
     struct sock *sock = (struct sock *)obj;
     int fd;
     assert( obj->ops == &sock_ops );
-    fd = dup( sock->fd );
+    fd = dup( sock->obj.fd );
     if (fd==-1)
     	sock_set_error();
     return fd;
@@ -280,7 +247,6 @@
     assert( obj->ops == &sock_ops );
 
     /* FIXME: special socket shutdown stuff? */
-    remove_select_user( sock->select );
     if (sock->event)
     {
         /* if the service thread was waiting for the event object,
@@ -307,21 +273,12 @@
         return NULL;
     }
     fcntl(sockfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */
-    if (!(sock = alloc_object( &sock_ops ))) {
-        close( sockfd );
-        return NULL;
-    }
-    sock->fd    = sockfd;
+    if (!(sock = alloc_object( &sock_ops, sockfd ))) return NULL;
     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;
@@ -345,32 +302,23 @@
      * return.
      */
     slen = sizeof(saddr);
-    acceptfd = accept(sock->fd,&saddr,&slen);
+    acceptfd = accept(sock->obj.fd,&saddr,&slen);
     if (acceptfd==-1) {
     	sock_set_error();
         release_object( sock );
 	return NULL;
     }
-    if (!(acceptsock = alloc_object( &sock_ops )))
+    if (!(acceptsock = alloc_object( &sock_ops, acceptfd )))
     {
-        close( acceptfd );
         release_object( sock );
         return 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 );
 
     sock_reselect( acceptsock );
diff --git a/server/socket.c b/server/socket.c
deleted file mode 100644
index fb3345a..0000000
--- a/server/socket.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Server-side socket communication functions
- *
- * Copyright (C) 1998 Alexandre Julliard
- */
-
-#include "config.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include "object.h"
-#include "request.h"
-
-/* Some versions of glibc don't define this */
-#ifndef SCM_RIGHTS
-#define SCM_RIGHTS 1
-#endif
-
-/* client structure */
-struct client
-{
-    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) */
-    struct timeout_user *timeout;    /* current timeout (opaque pointer) */
-};
-
-
-/* socket communication static structures */
-static struct iovec myiovec;
-static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
-#ifndef HAVE_MSGHDR_ACCRIGHTS
-struct cmsg_fd
-{
-    int len;   /* sizeof structure */
-    int level; /* SOL_SOCKET */
-    int type;  /* SCM_RIGHTS */
-    int fd;    /* fd to pass */
-};
-static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 };
-#endif  /* HAVE_MSGHDR_ACCRIGHTS */
-
-
-/* send a message to a client that is ready to receive something */
-static int do_write( struct client *client )
-{
-    int ret;
-
-    if (client->pass_fd == -1)
-    {
-        ret = write( client->fd, &client->res, sizeof(client->res) );
-        if (ret == sizeof(client->res)) goto ok;
-    }
-    else  /* we have an fd to send */
-    {
-#ifdef HAVE_MSGHDR_ACCRIGHTS
-        msghdr.msg_accrightslen = sizeof(int);
-        msghdr.msg_accrights = (void *)&client->pass_fd;
-#else  /* HAVE_MSGHDR_ACCRIGHTS */
-        msghdr.msg_control    = &cmsg;
-        msghdr.msg_controllen = sizeof(cmsg);
-        cmsg.fd = client->pass_fd;
-#endif  /* HAVE_MSGHDR_ACCRIGHTS */
-
-        myiovec.iov_base = (void *)&client->res;
-        myiovec.iov_len  = sizeof(client->res);
-
-        ret = sendmsg( client->fd, &msghdr, 0 );
-        close( client->pass_fd );
-        client->pass_fd = -1;
-        if (ret == sizeof(client->res)) goto ok;
-    }
-    if (ret == -1)
-    {
-        if (errno == EWOULDBLOCK) return 0;  /* not a fatal error */
-        if (errno != EPIPE) perror("sendmsg");
-    }
-    else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) );
-    remove_client( client, BROKEN_PIPE );
-    return -1;
-
- ok:
-    set_select_events( client->select, POLLIN );
-    return 1;
-}
-
-
-/* read a message from a client that has something to say */
-static void do_read( struct client *client )
-{
-    int ret;
-    enum request req;
-
-#ifdef HAVE_MSGHDR_ACCRIGHTS
-    msghdr.msg_accrightslen = sizeof(int);
-    msghdr.msg_accrights = (void *)&client->pass_fd;
-#else  /* HAVE_MSGHDR_ACCRIGHTS */
-    msghdr.msg_control    = &cmsg;
-    msghdr.msg_controllen = sizeof(cmsg);
-    cmsg.fd = -1;
-#endif  /* HAVE_MSGHDR_ACCRIGHTS */
-
-    assert( client->pass_fd == -1 );
-
-    myiovec.iov_base = (void *)&req;
-    myiovec.iov_len  = sizeof(req);
-
-    ret = recvmsg( client->fd, &msghdr, 0 );
-#ifndef HAVE_MSGHDR_ACCRIGHTS
-    client->pass_fd = cmsg.fd;
-#endif
-
-    if (ret == sizeof(req))
-    {
-        int pass_fd = client->pass_fd;
-        client->pass_fd = -1;
-        call_req_handler( client->self, req, pass_fd );
-        if (pass_fd != -1) close( pass_fd );
-        return;
-    }
-    if (ret == -1)
-    {
-        perror("recvmsg");
-        remove_client( client, BROKEN_PIPE );
-        return;
-    }
-    if (!ret)  /* closed pipe */
-    {
-        remove_client( client, BROKEN_PIPE );
-        return;
-    }
-    fatal_protocol_error( client->self, "partial message received %d/%d\n", ret, sizeof(req) );
-}
-
-/* handle a client event */
-static void client_event( int event, void *private )
-{
-    struct client *client = (struct client *)private;
-    if (event & (POLLERR | POLLHUP)) remove_client( client, BROKEN_PIPE );
-    else
-    {
-        if (event & POLLOUT) do_write( client );
-        if (event & POLLIN) do_read( client );
-    }
-}
-
-/*******************************************************************/
-/* server-side exported functions                                  */
-
-/* add a client */
-struct client *add_client( int fd, struct thread *self )
-{
-    int flags;
-    struct client *client = mem_alloc( sizeof(*client) );
-    if (!client) return NULL;
-
-    flags = fcntl( fd, F_GETFL, 0 );
-    fcntl( fd, F_SETFL, flags | O_NONBLOCK );
-
-    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;
-}
-
-/* remove a client */
-void remove_client( struct client *client, int exit_code )
-{
-    assert( client );
-
-    call_kill_handler( client->self, exit_code );
-
-    if (client->timeout) remove_timeout_user( client->timeout );
-    remove_select_user( client->select );
-
-    /* Purge messages */
-    if (client->pass_fd != -1) close( client->pass_fd );
-    free( client );
-}
-
-/* set the fd to pass to the client */
-void client_pass_fd( struct client *client, int pass_fd )
-{
-    assert( client->pass_fd == -1 );
-    client->pass_fd = pass_fd;
-}
-
-/* send a reply to a client */
-void client_reply( struct client *client, unsigned int res )
-{
-    if (debug_level) trace_reply( client->self, res, client->pass_fd );
-    client->res = res;
-    if (!do_write( client )) set_select_events( client->select, POLLOUT );
-}
diff --git a/server/thread.c b/server/thread.c
index e319f51..baaff4c 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -63,21 +63,24 @@
 
 static void dump_thread( struct object *obj, int verbose );
 static int thread_signaled( struct object *obj, struct thread *thread );
+extern void thread_poll_event( struct object *obj, int event );
 static void destroy_thread( struct object *obj );
 
 static const struct object_ops thread_ops =
 {
-    sizeof(struct thread),
-    dump_thread,
-    add_queue,
-    remove_queue,
-    thread_signaled,
-    no_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    destroy_thread
+    sizeof(struct thread),      /* size */
+    dump_thread,                /* dump */
+    add_queue,                  /* add_queue */
+    remove_queue,               /* remove_queue */
+    thread_signaled,            /* signaled */
+    no_satisfied,               /* satisfied */
+    NULL,                       /* get_poll_events */
+    thread_poll_event,          /* poll_event */
+    no_read_fd,                 /* get_read_fd */
+    no_write_fd,                /* get_write_fd */
+    no_flush,                   /* flush */
+    no_get_file_info,           /* get_file_info */
+    destroy_thread              /* destroy */
 };
 
 static struct thread *first_thread;
@@ -105,9 +108,11 @@
     struct thread *thread;
     int buf_fd;
 
-    if (!(thread = alloc_object( &thread_ops ))) return NULL;
+    int flags = fcntl( fd, F_GETFL, 0 );
+    fcntl( fd, F_SETFL, flags | O_NONBLOCK );
 
-    thread->client      = NULL;
+    if (!(thread = alloc_object( &thread_ops, fd ))) return NULL;
+
     thread->unix_pid    = 0;  /* not known yet */
     thread->teb         = NULL;
     thread->mutex       = NULL;
@@ -118,6 +123,7 @@
     thread->apc         = NULL;
     thread->apc_count   = 0;
     thread->error       = 0;
+    thread->pass_fd     = -1;
     thread->state       = RUNNING;
     thread->attached    = 0;
     thread->exit_code   = 0x103;  /* STILL_ACTIVE */
@@ -142,11 +148,8 @@
     add_process_thread( process, thread );
 
     if ((buf_fd = alloc_client_buffer( thread )) == -1) goto error;
-    if (!(thread->client = add_client( fd, thread )))
-    {
-        close( buf_fd );
-        goto error;
-    }
+
+    set_select_events( &thread->obj, POLLIN );  /* start listening to events */
     set_reply_fd( thread, buf_fd );  /* send the fd to the client */
     send_reply( thread );
     return thread;
@@ -164,6 +167,20 @@
     select_loop();
 }
 
+/* handle a client event */
+void thread_poll_event( struct object *obj, int event )
+{
+    struct thread *thread = (struct thread *)obj;
+    assert( obj->ops == &thread_ops );
+
+    if (event & (POLLERR | POLLHUP)) kill_thread( thread, BROKEN_PIPE );
+    else
+    {
+        if (event & POLLOUT) write_request( thread );
+        if (event & POLLIN) read_request( thread );
+    }
+}
+
 /* destroy a thread when its refcount is 0 */
 static void destroy_thread( struct object *obj )
 {
@@ -177,6 +194,7 @@
     else first_thread = thread->next;
     if (thread->apc) free( thread->apc );
     if (thread->buffer != (void *)-1) munmap( thread->buffer, MAX_REQUEST_LENGTH );
+    if (thread->pass_fd != -1) close( thread->pass_fd );
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -499,21 +517,17 @@
 void kill_thread( struct thread *thread, int exit_code )
 {
     if (thread->state == TERMINATED) return;  /* already killed */
-    remove_client( thread->client, exit_code ); /* this will call thread_killed */
-}
-
-/* a thread has been killed */
-void thread_killed( struct thread *thread, int exit_code )
-{
     thread->state = TERMINATED;
     thread->exit_code = exit_code;
-    thread->client = NULL;
+    if (current == thread) current = NULL;
+    if (debug_level) trace_kill( thread );
     if (thread->wait) end_wait( thread );
     debug_exit_thread( thread, exit_code );
     abandon_mutexes( thread );
     remove_process_thread( thread->process, thread );
     wake_up( &thread->obj, 0 );
     detach_thread( thread );
+    remove_select_user( &thread->obj );
     release_object( thread );
 }
 
diff --git a/server/thread.h b/server/thread.h
index bfe9bf4..f847fc4 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -46,10 +46,10 @@
     struct thread_apc  *apc;       /* list of async procedure calls */
     int                 apc_count; /* number of outstanding APCs */
     int                 error;     /* current error code */
+    int                 pass_fd;   /* fd to pass to the client */
     enum run_state      state;     /* running state */
     int                 attached;  /* is thread attached with ptrace? */
     int                 exit_code; /* thread exit code */
-    struct client      *client;    /* client for socket communications */
     int                 unix_pid;  /* Unix pid of client */
     void               *teb;       /* TEB address (in client address space) */
     int                 priority;  /* priority level */
@@ -74,7 +74,6 @@
 extern int 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);
 extern void wake_up( struct object *obj, int max );
 
diff --git a/server/timer.c b/server/timer.c
index d228d16..9ded7bd 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -39,17 +39,19 @@
 
 static const struct object_ops timer_ops =
 {
-    sizeof(struct timer),
-    timer_dump,
-    add_queue,
-    remove_queue,
-    timer_signaled,
-    timer_satisfied,
-    no_read_fd,
-    no_write_fd,
-    no_flush,
-    no_get_file_info,
-    timer_destroy
+    sizeof(struct timer),      /* size */
+    timer_dump,                /* dump */
+    add_queue,                 /* add_queue */
+    remove_queue,              /* remove_queue */
+    timer_signaled,            /* signaled */
+    timer_satisfied,           /* satisfied */
+    NULL,                      /* get_poll_events */
+    NULL,                      /* poll_event */
+    no_read_fd,                /* get_read_fd */
+    no_write_fd,               /* get_write_fd */
+    no_flush,                  /* flush */
+    no_get_file_info,          /* get_file_info */
+    timer_destroy              /* destroy */
 };
 
 
diff --git a/server/trace.c b/server/trace.c
index 0e436f2..c9643e9 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1341,22 +1341,22 @@
     fprintf( stderr, "%08x: *timeout*\n", (unsigned int)current );
 }
 
-void trace_kill( int exit_code )
+void trace_kill( struct thread *thread )
 {
     fprintf( stderr,"%08x: *killed* exit_code=%d\n",
-             (unsigned int)current, exit_code );
+             (unsigned int)thread, thread->exit_code );
 }
 
-void trace_reply( struct thread *thread, unsigned int res, int pass_fd )
+void trace_reply( struct thread *thread )
 {
     fprintf( stderr, "%08x: %s() = %d",
-             (unsigned int)thread, req_names[thread->last_req], res );
+             (unsigned int)thread, req_names[thread->last_req], thread->error );
     if (reply_dumpers[thread->last_req])
     {
         fprintf( stderr, " {" );
         reply_dumpers[thread->last_req]( thread->buffer );
 	fprintf( stderr, " }" );
     }
-    if (pass_fd != -1) fprintf( stderr, " fd=%d\n", pass_fd );
+    if (thread->pass_fd != -1) fprintf( stderr, " fd=%d\n", thread->pass_fd );
     else fprintf( stderr, "\n" );
 }