[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