server: Allow async i/o operations to send completion messages.
diff --git a/server/async.c b/server/async.c
index 3a99f80..98d6075 100644
--- a/server/async.c
+++ b/server/async.c
@@ -144,6 +144,10 @@
         return;
     }
 
+    /* send error completion event */
+    if (status != STATUS_ALERTED && async->data.cvalue && async->queue && async->queue->fd)
+        fd_add_completion( async->queue->fd, async->data.cvalue, status, 0 );
+
     memset( &data, 0, sizeof(data) );
     data.type            = APC_ASYNC_IO;
     data.async_io.func   = async->data.callback;
@@ -251,6 +255,8 @@
         if (async->timeout) remove_timeout_user( async->timeout );
         async->timeout = NULL;
         async->status = status;
+        if (async->data.cvalue && async->queue && async->queue->fd)
+            fd_add_completion( async->queue->fd, async->data.cvalue, status, 0 ); /* TODO pass Information field */
         if (async->data.apc)
         {
             apc_call_t data;
diff --git a/server/completion.c b/server/completion.c
index bff2ada..96aaacc 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -132,7 +132,7 @@
     return (struct completion *) get_handle_obj( process, handle, access, &completion_ops );
 }
 
-static void add_completion( struct completion *completion, unsigned long ckey, unsigned long cvalue, unsigned int status, unsigned long information )
+void add_completion( struct completion *completion, unsigned long ckey, unsigned long cvalue, unsigned int status, unsigned long information )
 {
     struct comp_msg *msg = mem_alloc( sizeof( *msg ) );
 
diff --git a/server/fd.c b/server/fd.c
index dc292cb..cb3aea8 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1919,6 +1919,13 @@
     return fd;
 }
 
+/* add a completion result to a completion queue attached to the fd */
+void fd_add_completion( struct fd *fd, unsigned long cvalue, unsigned int status, unsigned long information )
+{
+    if (fd->completion)
+        add_completion( fd->completion, fd->comp_key, cvalue, status, information );
+}
+
 /* flush a file buffers */
 DECL_HANDLER(flush_file)
 {
diff --git a/server/file.h b/server/file.h
index d6295d9..dbf0930 100644
--- a/server/file.h
+++ b/server/file.h
@@ -122,7 +122,8 @@
 
 /* completion */
 
-struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access );
+extern struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access );
+extern void add_completion( struct completion *completion, unsigned long ckey, unsigned long cvalue, unsigned int status, unsigned long information );
 
 /* serial port functions */
 
@@ -139,6 +140,7 @@
 extern int async_waiting( struct async_queue *queue );
 extern void async_terminate( struct async *async, unsigned int status );
 extern void async_wake_up( struct async_queue *queue, unsigned int status );
+extern void fd_add_completion( struct fd *fd, unsigned long cvalue, unsigned int status, unsigned long information );
 
 /* access rights that require Unix read permission */
 #define FILE_UNIX_READ_ACCESS (FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA)
diff --git a/server/protocol.def b/server/protocol.def
index 90e943b..93e4887 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -179,6 +179,7 @@
     void           *arg;           /* opaque user data to pass to callback */
     void           *apc;           /* user apc to call */
     obj_handle_t    event;         /* event to signal when done */
+    unsigned long   cvalue;        /* completion value to use for completion events */
 } async_data_t;
 
 /* structures for extra message data */