[PATCH 3/3] ntdll: sock_recv keeps message ordering

Dongwan Kim kdw6485 at gmail.com
Sun Dec 5 19:35:51 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.

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

Signed-off-by: Dongwan Kim <kdw6485 at gmail.com>
---
 dlls/ntdll/unix/server.c |  8 ++++----
 dlls/ntdll/unix/socket.c | 42 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 45 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;
-- 
2.30.2




More information about the wine-devel mailing list