[RFC PATCH v2 04/11] server: Attempt to complete I/O request immediately in recv_socket.

Jinoh Kang jinoh.kang.kr at gmail.com
Sat Jan 22 08:36:30 CST 2022


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

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

Notes:
    In the previous approach, we simply test for STATUS_ALERTED, do the I/O and
    substitute the status code appropriately.  This would obviate the need for:
    
    - The hack where we pass the "nonblocking mode" flag via iosb information field.
    - The "is_completion_deferred" function, since we can substitute STATUS_ALERTED
      for STATUS_PENDING in the requestor function before doing anything else.

 dlls/ntdll/unix/socket.c | 10 +++++++---
 include/wine/afd.h       | 17 +++++++++++++++++
 server/sock.c            | 26 +++++++++++++++++++++++++-
 3 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 71dfcdd1114..7d3795a953e 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -77,6 +77,7 @@
 #include "wsipx.h"
 #include "af_irda.h"
 #include "wine/afd.h"
+#include "wine/async.h"
 
 #include "unix_private.h"
 
@@ -656,6 +657,7 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
 {
     struct async_recv_ioctl *async = user;
     int fd, needs_close;
+    BOOL nonblocking;
 
     TRACE( "%#x\n", *status );
 
@@ -664,11 +666,13 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
         if ((*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL )))
             return TRUE;
 
+        nonblocking = *info == AFD_WINE_IN_NONBLOCKING_MODE;
+        if (nonblocking) *info = 0;
         *status = try_recv( fd, async, info );
         TRACE( "got status %#x, %#lx bytes read\n", *status, *info );
         if (needs_close) close( fd );
 
-        if (*status == STATUS_DEVICE_NOT_READY)
+        if (*status == STATUS_DEVICE_NOT_READY && !nonblocking)
             return FALSE;
     }
     release_fileio( &async->io );
@@ -756,7 +760,7 @@ static NTSTATUS sock_recv( 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)
+        if ((!NT_ERROR(status) || wait_handle) && !is_completion_deferred( status ))
         {
             io->Status = status;
             io->Information = information;
@@ -764,7 +768,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 );
+    if (!is_completion_deferred( status )) release_fileio( &async->io );
 
     if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
     return status;
diff --git a/include/wine/afd.h b/include/wine/afd.h
index efd5787e90a..1e26739229d 100644
--- a/include/wine/afd.h
+++ b/include/wine/afd.h
@@ -37,6 +37,23 @@ struct afd_wsabuf_32
 # define WS(x)    x
 #endif
 
+
+/* Used in the iosb.result field to indicate that the current socket I/O
+ * operation is in synchronous non-blocking mode.  This value is normally
+ * transmitted via the APC_ASYNC_IO system APC call (with status STATUS_ALERTED)
+ * when the server gives the client a chance to complete the I/O synchronously
+ * before resuming the request as fully asynchronous I/O or failing it.
+ * If the I/O fails with EWOULDBLOCK and the iosb.result field is set to any
+ * other value, the client shall request the server to resume the asynchronous
+ * operation.
+ *
+ * The value (ULONG_PTR)-1 (the maximum value of ULONG_PTR) is chosen so that
+ * it will be least likely to be confused with "the number of bytes transferred
+ * so far."  Any I/O operation that has made it to the maximum number of bytes
+ * shall complete immediately anyway.
+ */
+#define AFD_WINE_IN_NONBLOCKING_MODE ((ULONG_PTR)-1)
+
 #define IOCTL_AFD_BIND                      CTL_CODE(FILE_DEVICE_BEEP, 0x800, METHOD_NEITHER,  FILE_ANY_ACCESS)
 #define IOCTL_AFD_LISTEN                    CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER,  FILE_ANY_ACCESS)
 #define IOCTL_AFD_RECV                      CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER,  FILE_ANY_ACCESS)
diff --git a/server/sock.c b/server/sock.c
index 650e67a2e0a..03c867317b2 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -91,6 +91,7 @@
 #include "wsipx.h"
 #include "af_irda.h"
 #include "wine/afd.h"
+#include "wine/async.h"
 
 #include "process.h"
 #include "file.h"
@@ -3397,6 +3398,7 @@ DECL_HANDLER(recv_socket)
 {
     struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
     unsigned int status = req->status;
+    int pending = 0;
     timeout_t timeout = 0;
     struct async *async;
     struct fd *fd;
@@ -3422,6 +3424,25 @@ DECL_HANDLER(recv_socket)
     if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && sock->rd_shutdown)
         status = STATUS_PIPE_DISCONNECTED;
 
+    /* NOTE: If read_q is not empty, we cannot really tell if the already queued asyncs
+     * NOTE: will not consume all available data; if there's no data available,
+     * NOTE: the current request won't be immediately satiable. */
+    if ((status == STATUS_PENDING || status == STATUS_DEVICE_NOT_READY) && !async_queued( &sock->read_q ))
+    {
+        struct pollfd pollfd;
+        pollfd.fd = get_unix_fd( sock->fd );
+        pollfd.events = req->oob ? POLLPRI : POLLIN;
+        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). */
+            pending = status == STATUS_PENDING;
+            status = STATUS_ALERTED;
+        }
+    }
+
     sock->pending_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ);
     sock->reported_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ);
 
@@ -3438,12 +3459,15 @@ DECL_HANDLER(recv_socket)
         if (timeout)
             async_set_timeout( async, timeout, STATUS_IO_TIMEOUT );
 
-        if (status == STATUS_PENDING)
+        if (is_completion_deferred( status ))
             queue_async( &sock->read_q, async );
 
         /* always reselect; we changed reported_events above */
         sock_reselect( sock );
 
+        if (status == STATUS_ALERTED)
+            async_start_sync_io_request( async, pending ? 0 : AFD_WINE_IN_NONBLOCKING_MODE );
+
         reply->wait = async_handoff( async, NULL, 0 );
         reply->options = get_fd_options( fd );
         release_object( async );
-- 
2.31.1




More information about the wine-devel mailing list