[PATCH v5 07/10] server: Attempt to complete I/O request immediately in send_socket.

Jinoh Kang jinoh.kang.kr at gmail.com
Fri Mar 11 14:22:28 CST 2022


Make send_socket alert the async immediately if poll() call detects that
there are incoming data in the socket, bypassing the wineserver's main
polling loop.

For sock_transmit, we always mark the async as pending and set the IOSB
(unless async allocation has failed).

Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---

Notes:
    v1 -> v2:
    - retain the behaviour of returning success if we had a short write and
      the socket is nonblocking and force_async is unset
    v2 -> v3: fix typo in comment
    v3 -> v4: no changes
    v4 -> v5: no changes

 dlls/ntdll/unix/socket.c | 72 ++++++++++++++++++++++++++++++++++------
 server/protocol.def      |  1 +
 server/sock.c            | 22 +++++++++++-
 3 files changed, 83 insertions(+), 12 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 2f8bd6e62bf..62920a7e557 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -868,11 +868,13 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
                            const struct WS_sockaddr *addr, unsigned int addr_len, int unix_flags, int force_async )
 {
     struct async_send_ioctl *async;
+    ULONG_PTR information;
     HANDLE wait_handle;
     DWORD async_size;
     NTSTATUS status;
     unsigned int i;
     ULONG options;
+    BOOL nonblocking, alerted;
 
     async_size = offsetof( struct async_send_ioctl, iov[count] );
 
@@ -925,16 +927,38 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
         status = wine_server_call( req );
         wait_handle = wine_server_ptr_handle( reply->wait );
         options     = reply->options;
-        if ((!NT_ERROR(status) || wait_handle) && status != STATUS_PENDING)
+        nonblocking = reply->nonblocking;
+    }
+    SERVER_END_REQ;
+
+    alerted = status == STATUS_ALERTED;
+    if (alerted)
+    {
+        status = try_send( fd, async );
+        if (status == STATUS_DEVICE_NOT_READY && (force_async || !nonblocking))
+            status = STATUS_PENDING;
+
+        /* If we had a short write and the socket is nonblocking (and we are
+         * not trying to force the operation to be asynchronous), return
+         * success.  Windows actually refuses to send any data in this case,
+         * and returns EWOULDBLOCK, but we have no way of doing that. */
+        if (status == STATUS_DEVICE_NOT_READY && async->sent_len)
+            status = STATUS_SUCCESS;
+    }
+
+    if (status != STATUS_PENDING)
+    {
+        information = async->sent_len;
+        if (!NT_ERROR(status) || (wait_handle && !alerted))
         {
             io->Status = status;
-            io->Information = async->sent_len;
+            io->Information = information;
         }
+        release_fileio( &async->io );
     }
-    SERVER_END_REQ;
-
-    if (status != STATUS_PENDING) release_fileio( &async->io );
+    else information = 0;
 
+    if (alerted) set_async_direct_result( &wait_handle, status, information, FALSE );
     if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
     return status;
 }
@@ -1055,7 +1079,9 @@ static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
     socklen_t addr_len;
     HANDLE wait_handle;
     NTSTATUS status;
+    ULONG_PTR information;
     ULONG options;
+    BOOL alerted;
 
     addr_len = sizeof(addr);
     if (getpeername( fd, &addr.addr, &addr_len ) != 0)
@@ -1105,16 +1131,40 @@ static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
         status = wine_server_call( req );
         wait_handle = wine_server_ptr_handle( reply->wait );
         options     = reply->options;
-        /* In theory we'd fill the iosb here, as above in sock_send(), but it's
-         * actually currently impossible to get STATUS_SUCCESS. The server will
-         * either return STATUS_PENDING or an error code, and in neither case
-         * should the iosb be filled. */
-        if (!status) FIXME( "Unhandled success status." );
     }
     SERVER_END_REQ;
 
-    if (status != STATUS_PENDING) release_fileio( &async->io );
+    alerted = status == STATUS_ALERTED;
+    if (alerted)
+    {
+        status = try_transmit( fd, file_fd, async );
+        if (status == STATUS_DEVICE_NOT_READY)
+            status = STATUS_PENDING;
+    }
 
+    if (status != STATUS_PENDING)
+    {
+        information = async->head_cursor + async->file_cursor + async->tail_cursor;
+        if (!NT_ERROR(status) || wait_handle)
+        {
+            io->Status = status;
+            io->Information = information;
+        }
+        release_fileio( &async->io );
+    }
+    else information = 0;
+
+    if (alerted)
+    {
+        set_async_direct_result( &wait_handle, status, information, TRUE );
+        if (!(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))
+        {
+            /* Pretend we always do async I/O.  The client can always retrieve
+             * the actual I/O status via the IO_STATUS_BLOCK.
+             */
+            status = STATUS_PENDING;
+        }
+    }
     if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
     return status;
 }
diff --git a/server/protocol.def b/server/protocol.def
index 66c6c97b1e0..81b44aefd7c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1467,6 +1467,7 @@ enum server_fd_type
 @REPLY
     obj_handle_t wait;          /* handle to wait on for blocking send */
     unsigned int options;       /* device open options */
+    int          nonblocking;   /* is socket non-blocking? */
 @END
 
 
diff --git a/server/sock.c b/server/sock.c
index 0441ce2bbfa..9170f8fe352 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -3527,6 +3527,25 @@ DECL_HANDLER(send_socket)
     if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && sock->wr_shutdown)
         status = STATUS_PIPE_DISCONNECTED;
 
+    if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && !async_queued( &sock->write_q ))
+    {
+        /* If write_q is not empty, we cannot really tell if the already queued
+         * asyncs will not consume all available space; if there's no space
+         * available, the current request won't be immediately satiable.
+         */
+        struct pollfd pollfd;
+        pollfd.fd = get_unix_fd( sock->fd );
+        pollfd.events = POLLOUT;
+        pollfd.revents = 0;
+        if (poll(&pollfd, 1, 0) >= 0 && pollfd.revents)
+        {
+            /* Give the client opportunity to complete synchronously.
+             * If it turns out that the I/O request is not actually immediately satiable,
+             * the client may then choose to re-queue the async (with STATUS_PENDING). */
+            status = STATUS_ALERTED;
+        }
+    }
+
     if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async )))
     {
         struct send_req *send_req;
@@ -3549,7 +3568,7 @@ DECL_HANDLER(send_socket)
         if (timeout)
             async_set_timeout( async, timeout, STATUS_IO_TIMEOUT );
 
-        if (status == STATUS_PENDING)
+        if (status == STATUS_PENDING || status == STATUS_ALERTED)
         {
             queue_async( &sock->write_q, async );
             sock_reselect( sock );
@@ -3557,6 +3576,7 @@ DECL_HANDLER(send_socket)
 
         reply->wait = async_handoff( async, NULL, 0 );
         reply->options = get_fd_options( fd );
+        reply->nonblocking = sock->nonblocking;
         release_object( async );
     }
     release_object( sock );
-- 
2.34.1




More information about the wine-devel mailing list