From 72bff2e445f18c1fe4b5c6c3c8ca51ab8be722f5 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Tue, 10 Apr 2007 17:07:27 +0200
Subject: [PATCH] server: Add support for restarting an async I/O when the
 client side couldn't finish it right away.

---
 dlls/ntdll/directory.c         |  3 +-
 dlls/ntdll/file.c              | 51 +++++++++++++---------------
 dlls/ntdll/sync.c              |  8 +++--
 dlls/ws2_32/socket.c           | 62 ++++++++++++----------------------
 include/wine/server_protocol.h |  4 +--
 server/async.c                 | 61 +++++++++++++++++++++++++--------
 server/change.c                | 26 +++++++-------
 server/fd.c                    | 25 ++++++++++++--
 server/file.c                  |  1 +
 server/file.h                  |  4 +++
 server/mailslot.c              |  2 ++
 server/named_pipe.c            |  5 ++-
 server/process.c               |  1 +
 server/protocol.def            |  2 +-
 server/queue.c                 |  1 +
 server/request.c               |  1 +
 server/serial.c                |  1 +
 server/signal.c                |  1 +
 server/sock.c                  |  9 +++++
 server/thread.c                |  1 +
 server/trace.c                 |  1 +
 21 files changed, 164 insertions(+), 106 deletions(-)

diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index ef6802bd043..6af412973d5 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -2169,7 +2169,7 @@ struct read_changes_info
     ULONG BufferSize;
 };
 
-static void WINAPI read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, ULONG status )
+static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status )
 {
     struct read_changes_info *info = user;
     char path[PATH_MAX];
@@ -2222,6 +2222,7 @@ static void WINAPI read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, ULONG st
     iosb->Information = len;
 
     RtlFreeHeap( GetProcessHeap(), 0, info );
+    return ret;
 }
 
 #define FILE_NOTIFY_ALL        (  \
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 6584a92db64..f3ba690fcfe 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -236,8 +236,8 @@ NTSTATUS WINAPI NtCreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATTRIB
 /***********************************************************************
  *                  Asynchronous file I/O                              *
  */
-static void WINAPI FILE_AsyncReadService(void*, PIO_STATUS_BLOCK, ULONG);
-static void WINAPI FILE_AsyncWriteService(void*, PIO_STATUS_BLOCK, ULONG);
+static NTSTATUS FILE_AsyncReadService(void*, PIO_STATUS_BLOCK, NTSTATUS);
+static NTSTATUS FILE_AsyncWriteService(void*, PIO_STATUS_BLOCK, NTSTATUS);
 
 typedef struct async_fileio
 {
@@ -264,13 +264,12 @@ static void fileio_terminate(async_fileio *fileio, IO_STATUS_BLOCK* iosb, NTSTAT
 static ULONG fileio_queue_async(async_fileio* fileio, IO_STATUS_BLOCK* iosb, 
                                 BOOL do_read)
 {
-    PIO_APC_ROUTINE     apc = do_read ? FILE_AsyncReadService : FILE_AsyncWriteService;
-    NTSTATUS            status;
+    NTSTATUS status;
 
     SERVER_START_REQ( register_async )
     {
         req->handle = fileio->handle;
-        req->async.callback = apc;
+        req->async.callback = do_read ? FILE_AsyncReadService : FILE_AsyncWriteService;
         req->async.iosb     = iosb;
         req->async.arg      = fileio;
         req->async.apc      = fileio->apc;
@@ -338,7 +337,7 @@ NTSTATUS FILE_GetNtStatus(void)
 /***********************************************************************
  *             FILE_AsyncReadService      (INTERNAL)
  */
-static void WINAPI FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, ULONG status)
+static NTSTATUS FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status)
 {
     async_fileio *fileio = (async_fileio*)user;
     int fd, needs_close, result;
@@ -351,10 +350,8 @@ static void WINAPI FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, ULON
         /* check to see if the data is ready (non-blocking) */
         if ((status = server_get_unix_fd( fileio->handle, FILE_READ_DATA, &fd,
                                           &needs_close, NULL, NULL )))
-        {
-            fileio_terminate(fileio, iosb, status);
             break;
-        }
+
         result = read(fd, &fileio->buffer[fileio->already], fileio->count - fileio->already);
         if (needs_close) close( fd );
 
@@ -390,16 +387,15 @@ static void WINAPI FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, ULON
                   result, fileio->already, fileio->count,
                   (status == STATUS_SUCCESS) ? "success" : "pending");
         }
-        /* queue another async operation ? */
-        if (status == STATUS_PENDING)
-            fileio_queue_async(fileio, iosb, TRUE);
-        else
-            fileio_terminate(fileio, iosb, status);
         break;
-    default:
-        fileio_terminate(fileio, iosb, status);
+
+    case STATUS_TIMEOUT:
+    case STATUS_IO_TIMEOUT:
+        if (fileio->already) status = STATUS_SUCCESS;
         break;
     }
+    if (status != STATUS_PENDING) fileio_terminate(fileio, iosb, status);
+    return status;
 }
 
 struct io_timeouts
@@ -664,9 +660,9 @@ done:
 /***********************************************************************
  *             FILE_AsyncWriteService      (INTERNAL)
  */
-static void WINAPI FILE_AsyncWriteService(void *ovp, IO_STATUS_BLOCK *iosb, ULONG status)
+static NTSTATUS FILE_AsyncWriteService(void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status)
 {
-    async_fileio *fileio = (async_fileio *) ovp;
+    async_fileio *fileio = user;
     int result, fd, needs_close;
     enum server_fd_type type;
 
@@ -678,10 +674,8 @@ static void WINAPI FILE_AsyncWriteService(void *ovp, IO_STATUS_BLOCK *iosb, ULON
         /* write some data (non-blocking) */
         if ((status = server_get_unix_fd( fileio->handle, FILE_WRITE_DATA, &fd,
                                           &needs_close, &type, NULL )))
-        {
-            fileio_terminate(fileio, iosb, status);
             break;
-        }
+
         if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_PIPE || type == FD_TYPE_SOCKET))
             result = send( fd, fileio->buffer, 0, 0 );
         else
@@ -700,15 +694,15 @@ static void WINAPI FILE_AsyncWriteService(void *ovp, IO_STATUS_BLOCK *iosb, ULON
             status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
             TRACE("wrote %d more bytes %u/%u so far\n", result, fileio->already, fileio->count);
         }
-        if (status == STATUS_PENDING)
-            fileio_queue_async(fileio, iosb, FALSE);
-        else
-            fileio_terminate(fileio, iosb, status);
         break;
-    default:
-        fileio_terminate(fileio, iosb, status);
+
+    case STATUS_TIMEOUT:
+    case STATUS_IO_TIMEOUT:
+        if (fileio->already) status = STATUS_SUCCESS;
         break;
     }
+    if (status != STATUS_PENDING) fileio_terminate(fileio, iosb, status);
+    return status;
 }
 
 /******************************************************************************
@@ -937,10 +931,11 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
 /***********************************************************************
  *           pipe_completion_wait   (Internal)
  */
-static void CALLBACK pipe_completion_wait(void *arg, PIO_STATUS_BLOCK iosb, ULONG status)
+static NTSTATUS pipe_completion_wait(void *arg, PIO_STATUS_BLOCK iosb, NTSTATUS status)
 {
     TRACE("for %p, status=%08x\n", iosb, status);
     iosb->u.Status = status;
+    return status;
 }
 
 /**************************************************************************
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 72d73bb9e66..b2f4a278713 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -680,10 +680,12 @@ static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result )
         break;
     }
     case APC_ASYNC_IO:
-        NtCurrentTeb()->num_async_io--;
-        call->async_io.func( call->async_io.user, call->async_io.sb, call->async_io.status );
         result->type = call->type;
-        result->async_io.status = ((IO_STATUS_BLOCK *)call->async_io.sb)->u.Status;
+        result->async_io.status = call->async_io.func( call->async_io.user,
+                                                       call->async_io.sb,
+                                                       call->async_io.status );
+        if (result->async_io.status != STATUS_PENDING)
+            NtCurrentTeb()->num_async_io--;
         break;
     case APC_VIRTUAL_ALLOC:
         result->type = call->type;
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 87d4afdf039..283a145e583 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -1072,9 +1072,9 @@ static void ws2_async_terminate(ws2_async* as, IO_STATUS_BLOCK* iosb, NTSTATUS s
  *              WS2_make_async          (INTERNAL)
  */
 
-static void WINAPI WS2_async_recv(void*, IO_STATUS_BLOCK*, ULONG);
-static void WINAPI WS2_async_send(void*, IO_STATUS_BLOCK*, ULONG);
-static void WINAPI WS2_async_shutdown( void*, IO_STATUS_BLOCK*, ULONG);
+static NTSTATUS WS2_async_recv(void*, IO_STATUS_BLOCK*, NTSTATUS);
+static NTSTATUS WS2_async_send(void*, IO_STATUS_BLOCK*, NTSTATUS);
+static NTSTATUS WS2_async_shutdown( void*, IO_STATUS_BLOCK*, NTSTATUS);
 
 static inline struct ws2_async*
 WS2_make_async(SOCKET s, enum ws2_mode mode, struct iovec *iovec, DWORD dwBufferCount,
@@ -1137,7 +1137,7 @@ error:
 
 static ULONG ws2_queue_async(struct ws2_async* wsa, IO_STATUS_BLOCK* iosb)
 {
-    PIO_APC_ROUTINE     apc;
+    NTSTATUS (*apc)(void *, IO_STATUS_BLOCK *, NTSTATUS);
     int                 type;
     NTSTATUS            status;
 
@@ -1239,10 +1239,10 @@ out:
  *
  * Handler for overlapped recv() operations.
  */
-static void WINAPI WS2_async_recv( void* ovp, IO_STATUS_BLOCK* iosb, ULONG status)
+static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS status)
 {
-    ws2_async* wsa = (ws2_async*) ovp;
-    int result, fd, err;
+    ws2_async* wsa = user;
+    int result = 0, fd, err;
 
     TRACE( "(%p %p %x)\n", wsa, iosb, status );
 
@@ -1250,10 +1250,8 @@ static void WINAPI WS2_async_recv( void* ovp, IO_STATUS_BLOCK* iosb, ULONG statu
     {
     case STATUS_ALERTED:
         if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_READ_DATA, &fd, NULL ) ))
-        {
-            ws2_async_terminate(wsa, iosb, status, 0);
             break;
-        }
+
         result = WS2_recv( fd, wsa->iovec, wsa->n_iovecs,
                            wsa->addr, wsa->addrlen.ptr, &wsa->flags );
         wine_server_release_fd( wsa->hSocket, fd );
@@ -1279,15 +1277,10 @@ static void WINAPI WS2_async_recv( void* ovp, IO_STATUS_BLOCK* iosb, ULONG statu
                 TRACE( "Error: %x\n", err );
             }
         }
-        if (status == STATUS_PENDING)
-            ws2_queue_async(wsa, iosb);
-        else
-            ws2_async_terminate(wsa, iosb, status, result);
-        break;
-    default:
-        ws2_async_terminate(wsa, iosb, status, 0);
         break;
     }
+    if (status != STATUS_PENDING) ws2_async_terminate(wsa, iosb, status, result);
+    return status;
 }
 
 /***********************************************************************
@@ -1364,10 +1357,10 @@ out:
  *
  * Handler for overlapped send() operations.
  */
-static void WINAPI WS2_async_send(void* as, IO_STATUS_BLOCK* iosb, ULONG status)
+static NTSTATUS WS2_async_send(void* user, IO_STATUS_BLOCK* iosb, NTSTATUS status)
 {
-    ws2_async* wsa = (ws2_async*) as;
-    int result, fd;
+    ws2_async* wsa = user;
+    int result = 0, fd;
 
     TRACE( "(%p %p %x)\n", wsa, iosb, status );
 
@@ -1375,10 +1368,8 @@ static void WINAPI WS2_async_send(void* as, IO_STATUS_BLOCK* iosb, ULONG status)
     {
     case STATUS_ALERTED:
         if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_WRITE_DATA, &fd, NULL ) ))
-        {
-            ws2_async_terminate(wsa, iosb, status, 0);
             break;
-        }
+
         /* check to see if the data is ready (non-blocking) */
         result = WS2_send( fd, wsa->iovec, wsa->n_iovecs, wsa->addr, wsa->addrlen.val, wsa->flags );
         wine_server_release_fd( wsa->hSocket, fd );
@@ -1407,16 +1398,10 @@ static void WINAPI WS2_async_send(void* as, IO_STATUS_BLOCK* iosb, ULONG status)
                 TRACE( "Error: %x\n", err );
             }
         }
-        if (status == STATUS_PENDING)
-            ws2_queue_async(wsa, iosb);
-        else
-            ws2_async_terminate(wsa, iosb, status, result);
-        break;
-    default:
-        ws2_async_terminate(wsa, iosb, status, 0);
         break;
     }
-
+    if (status != STATUS_PENDING) ws2_async_terminate(wsa, iosb, status, result);
+    return status;
 }
 
 /***********************************************************************
@@ -1424,9 +1409,9 @@ static void WINAPI WS2_async_send(void* as, IO_STATUS_BLOCK* iosb, ULONG status)
  *
  * Handler for shutdown() operations on overlapped sockets.
  */
-static void WINAPI WS2_async_shutdown( void* as, PIO_STATUS_BLOCK iosb, ULONG status )
+static NTSTATUS WS2_async_shutdown( void* user, PIO_STATUS_BLOCK iosb, NTSTATUS status )
 {
-    ws2_async* wsa = (ws2_async*) as;
+    ws2_async* wsa = user;
     int fd, err = 1;
 
     TRACE( "async %p %d\n", wsa, wsa->mode );
@@ -1434,10 +1419,8 @@ static void WINAPI WS2_async_shutdown( void* as, PIO_STATUS_BLOCK iosb, ULONG st
     {
     case STATUS_ALERTED:
         if ((status = wine_server_handle_to_fd( wsa->hSocket, 0, &fd, NULL ) ))
-        {
-            ws2_async_terminate(wsa, iosb, status, 0);
             break;
-        }
+
         switch ( wsa->mode )
         {
         case ws2m_sd_read:   err = shutdown( fd, 0 );  break;
@@ -1446,13 +1429,10 @@ static void WINAPI WS2_async_shutdown( void* as, PIO_STATUS_BLOCK iosb, ULONG st
         }
         wine_server_release_fd( wsa->hSocket, fd );
         status = err ? wsaErrno() : STATUS_SUCCESS;
-        ws2_async_terminate(wsa, iosb, status, 0);
-        break;
-    default:
-        ws2_async_terminate(wsa, iosb, status, 0);
         break;
     }
-
+    ws2_async_terminate(wsa, iosb, status, 0);
+    return status;
 }
 
 /***********************************************************************
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 2c486e2326a..ce63f0f1c39 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -263,7 +263,7 @@ typedef union
     struct
     {
         enum apc_type    type;
-        void (__stdcall *func)(void*, void*, unsigned int);
+        unsigned int   (*func)(void*, void*, unsigned int);
         void            *user;
         void            *sb;
         unsigned int     status;
@@ -4677,6 +4677,6 @@ union generic_reply
     struct allocate_locally_unique_id_reply allocate_locally_unique_id_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 290
+#define SERVER_PROTOCOL_VERSION 291
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/async.c b/server/async.c
index e0877b2e062..8b3049cd476 100644
--- a/server/async.c
+++ b/server/async.c
@@ -38,6 +38,7 @@ struct async
     struct thread       *thread;          /* owning thread */
     struct list          queue_entry;     /* entry in async queue list */
     struct async_queue  *queue;           /* queue containing this async */
+    unsigned int         status;          /* current status */
     struct timeout_user *timeout;
     unsigned int         timeout_status;  /* status to report upon timeout */
     struct event        *event;
@@ -92,6 +93,11 @@ static const struct object_ops async_queue_ops =
 };
 
 
+static inline void async_reselect( struct async *async )
+{
+    if (async->queue->fd) fd_reselect_async( async->queue->fd, async->queue );
+}
+
 static void async_dump( struct object *obj, int verbose )
 {
     struct async *async = (struct async *)obj;
@@ -104,10 +110,12 @@ static void async_destroy( struct object *obj )
     struct async *async = (struct async *)obj;
     assert( obj->ops == &async_ops );
 
+    list_remove( &async->queue_entry );
+    async_reselect( async );
+
     if (async->timeout) remove_timeout_user( async->timeout );
     if (async->event) release_object( async->event );
     release_object( async->queue );
-    async->queue = NULL;
     release_object( async->thread );
 }
 
@@ -119,11 +127,19 @@ static void async_queue_dump( struct object *obj, int verbose )
 }
 
 /* notifies client thread of new status of its async request */
-/* destroys the server side of it */
 static void async_terminate( struct async *async, unsigned int status )
 {
     apc_call_t data;
 
+    assert( status != STATUS_PENDING );
+
+    if (async->status != STATUS_PENDING)
+    {
+        /* already terminated, just update status */
+        async->status = status;
+        return;
+    }
+
     memset( &data, 0, sizeof(data) );
     data.type            = APC_ASYNC_IO;
     data.async_io.func   = async->data.callback;
@@ -131,11 +147,9 @@ static void async_terminate( struct async *async, unsigned int status )
     data.async_io.sb     = async->data.iosb;
     data.async_io.status = status;
     thread_queue_apc( async->thread, &async->obj, &data );
-
-    if (async->timeout) remove_timeout_user( async->timeout );
-    async->timeout = NULL;
-    list_remove( &async->queue_entry );
-    release_object( async );
+    async->status = status;
+    async_reselect( async );
+    release_object( async );  /* so that it gets destroyed when the async is done */
 }
 
 /* callback for timeout on an async request */
@@ -184,11 +198,12 @@ struct async *create_async( struct thread *thread, struct async_queue *queue, co
         return NULL;
     }
 
-    async->thread = (struct thread *)grab_object( thread );
-    async->event = event;
-    async->data = *data;
+    async->thread  = (struct thread *)grab_object( thread );
+    async->event   = event;
+    async->status  = STATUS_PENDING;
+    async->data    = *data;
     async->timeout = NULL;
-    async->queue = (struct async_queue *)grab_object( queue );
+    async->queue   = (struct async_queue *)grab_object( queue );
 
     list_add_tail( &queue->queue, &async->queue_entry );
     grab_object( async );
@@ -214,12 +229,24 @@ void async_set_result( struct object *obj, unsigned int status )
 
     if (obj->ops != &async_ops) return;  /* in case the client messed up the APC results */
 
-    if (status == STATUS_PENDING)
+    assert( async->status != STATUS_PENDING );  /* it must have been woken up if we get a result */
+
+    if (status == STATUS_PENDING)  /* restart it */
     {
-        /* FIXME: restart the async operation */
+        status = async->status;
+        async->status = STATUS_PENDING;
+        grab_object( async );
+
+        if (status != STATUS_ALERTED)  /* it was terminated in the meantime */
+            async_terminate( async, status );
+        else
+            async_reselect( async );
     }
     else
     {
+        if (async->timeout) remove_timeout_user( async->timeout );
+        async->timeout = NULL;
+        async->status = status;
         if (async->data.apc)
         {
             apc_call_t data;
@@ -238,7 +265,13 @@ void async_set_result( struct object *obj, unsigned int status )
 /* check if an async operation is waiting to be alerted */
 int async_waiting( struct async_queue *queue )
 {
-    return queue && !list_empty( &queue->queue );
+    struct list *ptr;
+    struct async *async;
+
+    if (!queue) return 0;
+    if (!(ptr = list_head( &queue->queue ))) return 0;
+    async = LIST_ENTRY( ptr, struct async, queue_entry );
+    return async->status == STATUS_PENDING;
 }
 
 /* wake up async operations on the queue */
diff --git a/server/change.c b/server/change.c
index 33034da7b5d..a12c68be103 100644
--- a/server/change.c
+++ b/server/change.c
@@ -183,12 +183,13 @@ static enum server_fd_type dir_get_info( struct fd *fd, int *flags );
 
 static const struct fd_ops dir_fd_ops =
 {
-    dir_get_poll_events,      /* get_poll_events */
-    default_poll_event,       /* poll_event */
-    no_flush,                 /* flush */
-    dir_get_info,             /* get_file_info */
-    default_fd_queue_async,   /* queue_async */
-    default_fd_cancel_async   /* cancel_async */
+    dir_get_poll_events,         /* get_poll_events */
+    default_poll_event,          /* poll_event */
+    no_flush,                    /* flush */
+    dir_get_info,                /* get_file_info */
+    default_fd_queue_async,      /* queue_async */
+    default_fd_reselect_async,   /* reselect_async */
+    default_fd_cancel_async      /* cancel_async */
 };
 
 static struct list change_list = LIST_INIT(change_list);
@@ -516,12 +517,13 @@ static void inotify_poll_event( struct fd *fd, int event );
 
 static const struct fd_ops inotify_fd_ops =
 {
-    inotify_get_poll_events,  /* get_poll_events */
-    inotify_poll_event,       /* poll_event */
-    no_flush,                 /* flush */
-    no_get_file_info,         /* get_file_info */
-    default_fd_queue_async,   /* queue_async */
-    default_fd_cancel_async,  /* cancel_async */
+    inotify_get_poll_events,     /* get_poll_events */
+    inotify_poll_event,          /* poll_event */
+    no_flush,                    /* flush */
+    no_get_file_info,            /* get_file_info */
+    default_fd_queue_async,      /* queue_async */
+    default_fd_reselect_async,   /* reselect_async */
+    default_fd_cancel_async,     /* cancel_async */
 };
 
 static int inotify_get_poll_events( struct fd *fd )
diff --git a/server/fd.c b/server/fd.c
index 3a044b21832..504512e392f 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1638,6 +1638,7 @@ int check_fd_events( struct fd *fd, int events )
     struct pollfd pfd;
 
     if (fd->unix_fd == -1) return POLLERR;
+    if (fd->inode) return events;  /* regular files are always signaled */
 
     pfd.fd     = fd->unix_fd;
     pfd.events = events;
@@ -1666,12 +1667,12 @@ int default_fd_get_poll_events( struct fd *fd )
 /* default handler for poll() events */
 void default_poll_event( struct fd *fd, int event )
 {
-    if (event & POLLIN) async_wake_up( fd->read_q, STATUS_ALERTED );
-    if (event & POLLOUT) async_wake_up( fd->write_q, STATUS_ALERTED );
+    if (event & (POLLIN | POLLERR | POLLHUP)) async_wake_up( fd->read_q, STATUS_ALERTED );
+    if (event & (POLLOUT | POLLERR | POLLHUP)) async_wake_up( fd->write_q, STATUS_ALERTED );
 
     /* if an error occurred, stop polling this fd to avoid busy-looping */
     if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
-    else set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
+    else if (!fd->inode) set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
 }
 
 struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
@@ -1725,6 +1726,11 @@ void fd_async_wake_up( struct fd *fd, int type, unsigned int status )
     }
 }
 
+void fd_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+    fd->fd_ops->reselect_async( fd, queue );
+}
+
 void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
 {
     int flags;
@@ -1743,6 +1749,19 @@ void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type,
     }
 }
 
+/* default reselect_async() fd routine */
+void default_fd_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+    if (queue != fd->wait_q)
+    {
+        int poll_events = fd->fd_ops->get_poll_events( fd );
+        int events = check_fd_events( fd, poll_events );
+        if (events) fd->fd_ops->poll_event( fd, events );
+        else set_fd_events( fd, poll_events );
+    }
+}
+
+/* default cancel_async() fd routine */
 void default_fd_cancel_async( struct fd *fd )
 {
     async_wake_up( fd->read_q, STATUS_CANCELLED );
diff --git a/server/file.c b/server/file.c
index edc92f72a59..e038af37212 100644
--- a/server/file.c
+++ b/server/file.c
@@ -96,6 +96,7 @@ static const struct fd_ops file_fd_ops =
     file_flush,                   /* flush */
     file_get_info,                /* get_file_info */
     default_fd_queue_async,       /* queue_async */
+    default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
 };
 
diff --git a/server/file.h b/server/file.h
index 096b9df7741..54d2d730920 100644
--- a/server/file.h
+++ b/server/file.h
@@ -41,6 +41,8 @@ struct fd_ops
     enum server_fd_type (*get_file_info)(struct fd *fd, int *flags);
     /* queue an async operation */
     void (*queue_async)(struct fd *, const async_data_t *data, int type, int count);
+    /* selected events for async i/o need an update */
+    void (*reselect_async)( struct fd *, struct async_queue *queue );
     /* cancel an async operation */
     void (*cancel_async)(struct fd *);
 };
@@ -70,7 +72,9 @@ extern int default_fd_get_poll_events( struct fd *fd );
 extern void default_poll_event( struct fd *fd, int event );
 extern struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
 extern void fd_async_wake_up( struct fd *fd, int type, unsigned int status );
+extern void fd_reselect_async( struct fd *fd, struct async_queue *queue );
 extern void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
+extern void default_fd_reselect_async( struct fd *fd, struct async_queue *queue );
 extern void default_fd_cancel_async( struct fd *fd );
 extern void no_flush( struct fd *fd, struct event **event );
 extern enum server_fd_type no_get_file_info( struct fd *fd, int *flags );
diff --git a/server/mailslot.c b/server/mailslot.c
index 2898d1a8070..d130f6e6364 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -97,6 +97,7 @@ static const struct fd_ops mailslot_fd_ops =
     no_flush,                   /* flush */
     mailslot_get_info,          /* get_file_info */
     mailslot_queue_async,       /* queue_async */
+    default_fd_reselect_async,  /* reselect_async */
     default_fd_cancel_async     /* cancel_async */
 };
 
@@ -185,6 +186,7 @@ static const struct fd_ops mailslot_device_fd_ops =
     no_flush,                       /* flush */
     mailslot_device_get_file_info,  /* get_file_info */
     default_fd_queue_async,         /* queue_async */
+    default_fd_reselect_async,      /* reselect_async */
     default_fd_cancel_async         /* cancel_async */
 };
 
diff --git a/server/named_pipe.c b/server/named_pipe.c
index a736d83e453..4b238166cd9 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -158,11 +158,12 @@ static const struct object_ops pipe_server_ops =
 
 static const struct fd_ops pipe_server_fd_ops =
 {
-    default_fd_get_poll_events,     /* get_poll_events */
+    default_fd_get_poll_events,   /* get_poll_events */
     default_poll_event,           /* poll_event */
     pipe_server_flush,            /* flush */
     pipe_server_get_info,         /* get_file_info */
     default_fd_queue_async,       /* queue_async */
+    default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async,      /* cancel_async */
 };
 
@@ -197,6 +198,7 @@ static const struct fd_ops pipe_client_fd_ops =
     pipe_client_flush,            /* flush */
     pipe_client_get_info,         /* get_file_info */
     default_fd_queue_async,       /* queue_async */
+    default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
 };
 
@@ -233,6 +235,7 @@ static const struct fd_ops named_pipe_device_fd_ops =
     no_flush,                         /* flush */
     named_pipe_device_get_file_info,  /* get_file_info */
     default_fd_queue_async,           /* queue_async */
+    default_fd_reselect_async,        /* reselect_async */
     default_fd_cancel_async           /* cancel_async */
 };
 
diff --git a/server/process.c b/server/process.c
index f947d6dfd4e..f06f5f46ae6 100644
--- a/server/process.c
+++ b/server/process.c
@@ -87,6 +87,7 @@ static const struct fd_ops process_fd_ops =
     no_flush,                    /* flush */
     no_get_file_info,            /* get_file_info */
     no_queue_async,              /* queue_async */
+    NULL,                        /* reselect_async */
     no_cancel_async              /* cancel async */
 };
 
diff --git a/server/protocol.def b/server/protocol.def
index 48d1096589c..e1e504f41c5 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -279,7 +279,7 @@ typedef union
     struct
     {
         enum apc_type    type;     /* APC_ASYNC_IO */
-        void (__stdcall *func)(void*, void*, unsigned int);
+        unsigned int   (*func)(void*, void*, unsigned int);
         void            *user;     /* user pointer */
         void            *sb;       /* status block */
         unsigned int     status;   /* I/O status */
diff --git a/server/queue.c b/server/queue.c
index 4e66f990e2b..22f4dcd36a6 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -169,6 +169,7 @@ static const struct fd_ops msg_queue_fd_ops =
     no_flush,                    /* flush */
     no_get_file_info,            /* get_file_info */
     no_queue_async,              /* queue_async */
+    NULL,                        /* reselect_async */
     no_cancel_async              /* cancel async */
 };
 
diff --git a/server/request.c b/server/request.c
index 20fe4356fa6..7dac5d11012 100644
--- a/server/request.c
+++ b/server/request.c
@@ -109,6 +109,7 @@ static const struct fd_ops master_socket_fd_ops =
     no_flush,                      /* flush */
     no_get_file_info,              /* get_file_info */
     no_queue_async,                /* queue_async */
+    NULL,                          /* reselect_async */
     no_cancel_async                /* cancel_async */
 };
 
diff --git a/server/serial.c b/server/serial.c
index 8dc1d189a86..b9a3bbda17e 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -109,6 +109,7 @@ static const struct fd_ops serial_fd_ops =
     serial_flush,                 /* flush */
     serial_get_info,              /* get_file_info */
     serial_queue_async,           /* queue_async */
+    default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
 };
 
diff --git a/server/signal.c b/server/signal.c
index 7b388e1305a..238edeface9 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -85,6 +85,7 @@ static const struct fd_ops handler_fd_ops =
     no_flush,                 /* flush */
     no_get_file_info,         /* get_file_info */
     no_queue_async,           /* queue_async */
+    NULL,                     /* reselect_async */
     no_cancel_async           /* cancel_async */
 };
 
diff --git a/server/sock.c b/server/sock.c
index 90103f9a583..51109c35bed 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -97,6 +97,7 @@ static int sock_get_poll_events( struct fd *fd );
 static void sock_poll_event( struct fd *fd, int event );
 static enum server_fd_type sock_get_info( struct fd *fd, int *flags );
 static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
+static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
 static void sock_cancel_async( struct fd *fd );
 
 static int sock_get_error( int err );
@@ -126,6 +127,7 @@ static const struct fd_ops sock_fd_ops =
     no_flush,                     /* flush */
     sock_get_info,                /* get_file_info */
     sock_queue_async,             /* queue_async */
+    sock_reselect_async,          /* reselect_async */
     sock_cancel_async             /* cancel_async */
 };
 
@@ -556,6 +558,13 @@ static void sock_queue_async( struct fd *fd, const async_data_t *data, int type,
     if ( pollev ) sock_try_event( sock, pollev );
 }
 
+static void sock_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+    struct sock *sock = get_fd_user( fd );
+    int events = sock_reselect( sock );
+    if (events) sock_try_event( sock, events );
+}
+
 static void sock_cancel_async( struct fd *fd )
 {
     struct sock *sock = get_fd_user( fd );
diff --git a/server/thread.c b/server/thread.c
index ec1b16170fc..09e0367ce9b 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -132,6 +132,7 @@ static const struct fd_ops thread_fd_ops =
     no_flush,                   /* flush */
     no_get_file_info,           /* get_file_info */
     no_queue_async,             /* queue_async */
+    NULL,                       /* reselect_async */
     no_cancel_async             /* cancel_async */
 };
 
diff --git a/server/trace.c b/server/trace.c
index 0d11e7cf88d..f1e5aca48fc 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -4161,6 +4161,7 @@ static const struct
     { "MEDIA_WRITE_PROTECTED",       STATUS_MEDIA_WRITE_PROTECTED },
     { "MUTANT_NOT_OWNED",            STATUS_MUTANT_NOT_OWNED },
     { "NAME_TOO_LONG",               STATUS_NAME_TOO_LONG },
+    { "NOTIFY_ENUM_DIR",             STATUS_NOTIFY_ENUM_DIR },
     { "NOT_ALL_ASSIGNED",            STATUS_NOT_ALL_ASSIGNED },
     { "NOT_A_DIRECTORY",             STATUS_NOT_A_DIRECTORY },
     { "NOT_IMPLEMENTED",             STATUS_NOT_IMPLEMENTED },
-- 
GitLab