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(a)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
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 0e386e3537c..e899695bb60 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -3525,6 +3525,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)
@@ -3540,11 +3559,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