From: Jinoh Kang Subject: [PATCH v3 6/8] server: Attempt to complete I/O request immediately in send_socket. Message-Id: Date: Tue, 22 Feb 2022 02:23:32 +0900 In-Reply-To: References: 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 --- 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 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 f24215b66a9..23014ceceaf 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -868,12 +868,14 @@ 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; data_size_t data_size; + BOOL nonblocking, alerted; async_size = offsetof( struct async_send_ioctl, iov[count] ); @@ -939,16 +941,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; } @@ -1069,7 +1093,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) @@ -1120,16 +1146,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 5eb63db3091..334abb66a77 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1468,6 +1468,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 8653492ce34..3cb1b5f3dab 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3531,6 +3531,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 ))) { if (status == STATUS_SUCCESS) @@ -3546,11 +3565,12 @@ 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 ); 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