[PATCH v4 4/8] server: Defer postprocessing until after setting initial status in send_socket handler.

Jinoh Kang jinoh.kang.kr at gmail.com
Thu Mar 3 07:30:31 CST 2022


This allows the client to postpone the initial I/O until the server has
queued the I/O request.  The server should perform the postprocessing
only after the initial I/O has been done.

In the case of send_socket, the manipulation of event flags shall
ideally be done *after* (not *before*) the client has attempted the
initial I/O, since the outbound queue status of the socket may change in
the meanwhile.  Also, the implicitly bound address is available only
after the send* system call has been performed.

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

Notes:
    v1 -> v2:
    - pass around total size of data to be transmitted
    - detect short write in send_socket_initial_callback
    v2 -> v3: no changes
    v3 -> v4: no changes

 dlls/ntdll/unix/socket.c | 15 ++++++++++++++
 server/protocol.def      |  1 +
 server/sock.c            | 42 +++++++++++++++++++++++++++-------------
 3 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 23059e3cff8..d04ebb7f874 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -873,6 +873,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     NTSTATUS status;
     unsigned int i;
     ULONG options;
+    data_size_t data_size;
 
     async_size = offsetof( struct async_send_ioctl, iov[count] );
 
@@ -906,6 +907,18 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     async->iov_cursor = 0;
     async->sent_len = 0;
 
+    data_size = 0;
+    for (i = 0; i < count; ++i)
+    {
+        SIZE_T len = async->iov[i].iov_len;
+        if (len > (SIZE_T)(data_size_t)-1 || (data_size_t)(data_size + len) < data_size)
+        {
+            release_fileio( &async->io );
+            return STATUS_NO_MEMORY;
+        }
+        data_size += len;
+    }
+
     status = try_send( fd, async );
 
     if (status != STATUS_SUCCESS && status != STATUS_DEVICE_NOT_READY)
@@ -919,6 +932,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
 
     SERVER_START_REQ( send_socket )
     {
+        req->data_size = data_size;
         req->status = status;
         req->total  = async->sent_len;
         req->async  = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) );
@@ -1099,6 +1113,7 @@ static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
 
     SERVER_START_REQ( send_socket )
     {
+        req->data_size = async->head_len + async->file_len + async->tail_len;
         req->status = STATUS_PENDING;
         req->total  = 0;
         req->async  = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) );
diff --git a/server/protocol.def b/server/protocol.def
index 9d90544fa41..2e57c91ccad 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1461,6 +1461,7 @@ enum server_fd_type
 
 /* Perform a send on a socket */
 @REQ(send_socket)
+    data_size_t  data_size;     /* total number of bytes to send (>= total) */
     async_data_t async;         /* async I/O parameters */
     unsigned int status;        /* status of initial call */
     unsigned int total;         /* number of bytes already sent */
diff --git a/server/sock.c b/server/sock.c
index 91f98556552..0e386e3537c 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -3454,16 +3454,17 @@ DECL_HANDLER(recv_socket)
     release_object( sock );
 }
 
-DECL_HANDLER(send_socket)
+static void send_socket_initial_callback( void *private, struct async *async, struct fd *fd, unsigned int status )
 {
-    struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
-    unsigned int status = req->status;
-    timeout_t timeout = 0;
-    struct async *async;
-    struct fd *fd;
+    struct sock *sock = get_fd_user( fd );
+    struct iosb *iosb;
+    int is_short_write = 0;
 
-    if (!sock) return;
-    fd = sock->fd;
+    if ((iosb = async_get_iosb( async )))
+    {
+        is_short_write = iosb->result < (unsigned long)private;
+        release_object( iosb );
+    }
 
     if (sock->type == WS_SOCK_DGRAM)
     {
@@ -3476,13 +3477,29 @@ DECL_HANDLER(send_socket)
         sock->bound = 1;
     }
 
-    if (status != STATUS_SUCCESS)
+    if (status != STATUS_SUCCESS || is_short_write)
     {
-        /* send() calls only clear and reselect events if unsuccessful. */
+        /* send() calls only clear and reselect events if unsuccessful.
+         * Also treat short writes as being unsuccessful.
+         */
         sock->pending_events &= ~AFD_POLL_WRITE;
         sock->reported_events &= ~AFD_POLL_WRITE;
     }
 
+    sock_reselect( sock );
+}
+
+DECL_HANDLER(send_socket)
+{
+    struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
+    unsigned int status = req->status;
+    timeout_t timeout = 0;
+    struct async *async;
+    struct fd *fd;
+
+    if (!sock) return;
+    fd = sock->fd;
+
     /* If we had a short write and the socket is nonblocking (and the client is
      * not trying to force the operation to be asynchronous), return success.
      * Windows actually refuses to send any data in this case, and returns
@@ -3518,15 +3535,14 @@ DECL_HANDLER(send_socket)
         }
         set_error( status );
 
+        async_set_initial_status_callback( async, send_socket_initial_callback, (void *)(unsigned long)req->data_size );
+
         if (timeout)
             async_set_timeout( async, timeout, STATUS_IO_TIMEOUT );
 
         if (status == STATUS_PENDING)
             queue_async( &sock->write_q, async );
 
-        /* always reselect; we changed reported_events above */
-        sock_reselect( sock );
-
         reply->wait = async_handoff( async, NULL, 0 );
         reply->options = get_fd_options( fd );
         release_object( async );
-- 
2.34.1




More information about the wine-devel mailing list