- Add a default asynchronous I/O implementation.
- Make file objects use it.

diff --git a/server/fd.c b/server/fd.c
index 6d6b008..797f77f 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -146,6 +146,8 @@
     int                  unix_fd;     /* unix file descriptor */
     int                  fs_locks;    /* can we use filesystem locks for this fd? */
     int                  poll_index;  /* index of fd in poll array */
+    struct list          read_q;      /* async readers of this fd */
+    struct list          write_q;     /* async writers of this fd */
 };
 
 static void fd_dump( struct object *obj, int verbose );
@@ -1073,6 +1075,9 @@
 {
     struct fd *fd = (struct fd *)obj;
 
+    async_terminate_queue( &fd->read_q, STATUS_CANCELLED );
+    async_terminate_queue( &fd->write_q, STATUS_CANCELLED );
+
     remove_fd_locks( fd );
     list_remove( &fd->inode_entry );
     if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
@@ -1126,6 +1131,8 @@
     fd->poll_index = -1;
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
+    list_init( &fd->read_q );
+    list_init( &fd->write_q );
 
     if ((fd->poll_index = add_poll_user( fd )) == -1)
     {
@@ -1370,14 +1377,77 @@
     return ret;
 }
 
+int default_fd_get_poll_events( struct fd *fd )
+{
+    int events = 0;
+
+    if( !list_empty( &fd->read_q ))
+        events |= POLLIN;
+    if( !list_empty( &fd->write_q ))
+        events |= POLLOUT;
+
+    return events;
+}
+
 /* default handler for poll() events */
 void default_poll_event( struct fd *fd, int event )
 {
-    /* an error occurred, stop polling this fd to avoid busy-looping */
+    if (!list_empty( &fd->read_q ) && (POLLIN & event) )
+    {
+        async_terminate_head( &fd->read_q, STATUS_ALERTED );
+        return;
+    }
+    if (!list_empty( &fd->write_q ) && (POLLOUT & event) )
+    {
+        async_terminate_head( &fd->write_q, STATUS_ALERTED );
+        return;
+    }
+
+    /* if an error occurred, stop polling this fd to avoid busy-looping */
     if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
     wake_up( fd->user, 0 );
 }
 
+void default_fd_queue_async( struct fd *fd, void *apc, void *user, void *io_sb, int type, int count )
+{
+    struct list *queue;
+    int events;
+
+    if (!(fd->fd_ops->get_file_info( fd ) & FD_FLAG_OVERLAPPED))
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    switch (type)
+    {
+    case ASYNC_TYPE_READ:
+        queue = &fd->read_q;
+        break;
+    case ASYNC_TYPE_WRITE:
+        queue = &fd->write_q;
+        break;
+    default:
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+
+    if (!create_async( current, NULL, queue, apc, user, io_sb ))
+        return;
+
+    /* Check if the new pending request can be served immediately */
+    events = check_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
+    if (events) fd->fd_ops->poll_event( fd, events );
+
+    set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
+}
+
+void default_fd_cancel_async( struct fd *fd )
+{
+    async_terminate_queue( &fd->read_q, STATUS_CANCELLED );
+    async_terminate_queue( &fd->write_q, STATUS_CANCELLED );
+}
+
 /* default flush() routine */
 int no_flush( struct fd *fd, struct event **event )
 {