[PATCH v2 2/2] ntdll: sock_recv keeps message ordered

Dongwan Kim kdw6485 at gmail.com
Mon Dec 6 19:30:47 CST 2021


Calling try_recv could make messages misordered.

Suppose that a server program calls a overlapped WSARecv
and its operation is pending.
Another program sends a message and wineserver is busy.
The server program calls another overlapped WSARecv and
it intercepts the message for the pending WSARecv.

The problem already had been discussed here
https://www.winehq.org/pipermail/wine-devel/2021-May/186612.html

To avoid this situation, this kind of approach can be applied.

The server program needs to know if there are pending asyncs
before calling try_recv.
Wineserver notifies it whenever invoking APC_ASYNC_IO.
Then, the server program updates it and check it before calling try_recv.

Signed-off-by: Dongwan Kim <kdw6485 at gmail.com>
---
 dlls/ntdll/unix/server.c       |  8 +++----
 dlls/ntdll/unix/socket.c       | 42 +++++++++++++++++++++++++++++++++-
 include/wine/server_protocol.h |  1 +
 server/async.c                 |  8 +++++++
 4 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 1dcd0792072..1144bbcfa11 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -374,17 +374,17 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO
     case APC_ASYNC_IO:
     {
         struct async_fileio *user = wine_server_get_ptr( call->async_io.user );
-        ULONG_PTR info = call->async_io.result;
+        ULONG_PTR info[2] = { call->async_io.result, call->async_io.async_wait };
         NTSTATUS status;
 
         result->type = call->type;
         status = call->async_io.status;
-        if (user->callback( user, &info, &status ))
+        if (user->callback( user, info, &status ))
         {
             result->async_io.status = status;
-            result->async_io.total = info;
+            result->async_io.total = *info;
             /* the server will pass us NULL if a call failed synchronously */
-            set_async_iosb( call->async_io.sb, result->async_io.status, info );
+            set_async_iosb( call->async_io.sb, result->async_io.status, *info );
         }
         else result->async_io.status = STATUS_PENDING; /* restart it */
         break;
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 2e79b9baa0f..ca56a7707a6 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -575,13 +575,45 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
     return status;
 }
 
+struct async_queue{
+  struct list entry;
+  HANDLE sock;
+  char read_q;
+  char write_q;
+};
+static struct list async_queue_list = LIST_INIT(async_queue_list);
+
+
+static struct async_queue* alloc_async_queue(HANDLE handle){
+  struct async_queue *queue =
+    (struct async_queue*)malloc(sizeof(struct async_queue));
+  memset(queue, 0, sizeof(struct async_queue));
+
+  queue->sock = handle;
+  list_add_head(&async_queue_list, &queue->entry);
+  return queue;
+}
+
+static struct async_queue* find_async_queue(HANDLE handle){
+  struct async_queue *queue;
+
+  LIST_FOR_EACH_ENTRY(queue, &async_queue_list, struct async_queue, entry){
+    if(queue->sock == handle)
+      return queue;
+  }
+  return NULL;
+}
 static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
 {
     struct async_recv_ioctl *async = user;
+    struct async_queue *queue;
     int fd, needs_close;
 
     TRACE( "%#x\n", *status );
 
+    queue = find_async_queue(async->io.handle);
+    if(queue)
+      queue->read_q = info[1];  //store if waiting asyncs exist
     if (*status == STATUS_ALERTED)
     {
         if ((*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL )))
@@ -603,6 +635,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
                            struct WS_sockaddr *addr, int *addr_len, DWORD *ret_flags, int unix_flags, int force_async )
 {
     struct async_recv_ioctl *async;
+    struct async_queue *queue;
     ULONG_PTR information;
     HANDLE wait_handle;
     DWORD async_size;
@@ -641,7 +674,13 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     async->addr_len = addr_len;
     async->ret_flags = ret_flags;
 
-    status = try_recv( fd, async, &information );
+    queue = find_async_queue(handle);
+    if(!queue)
+      queue = alloc_async_queue(handle);
+    if(queue->read_q ) // if there are waiting asyncs, avoid cutting in line.
+      status = STATUS_DEVICE_NOT_READY;
+    else
+      status = try_recv( fd, async, &information );
 
     if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW && status != STATUS_DEVICE_NOT_READY)
     {
@@ -670,6 +709,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     SERVER_END_REQ;
 
     if (status != STATUS_PENDING) release_fileio( &async->io );
+    else queue->read_q = 1;
 
     if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
     return status;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b37a8e8e056..4dc2eb567db 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -492,6 +492,7 @@ typedef union
         client_ptr_t     user;
         client_ptr_t     sb;
         data_size_t      result;
+        unsigned int     async_wait;
     } async_io;
     struct
     {
diff --git a/server/async.c b/server/async.c
index ec3396c930f..0220bf4e2f4 100644
--- a/server/async.c
+++ b/server/async.c
@@ -164,6 +164,7 @@ static void async_destroy( struct object *obj )
 void async_terminate( struct async *async, unsigned int status )
 {
     struct iosb *iosb = async->iosb;
+    struct async* async_waiting;
 
     if (async->terminated) return;
 
@@ -202,6 +203,13 @@ void async_terminate( struct async *async, unsigned int status )
         else
             data.async_io.status = status;
 
+        async_waiting = find_pending_async(async->queue);
+        if(async_waiting)
+        {
+          data.async_io.async_wait = 1;
+          release_object(async_waiting);
+        }
+
         thread_queue_apc( async->thread->process, async->thread, &async->obj, &data );
     }
 
-- 
2.30.2




More information about the wine-devel mailing list