[PATCH v6 2/4] server: Attempt to complete I/O request immediately in recv_socket.
Jinoh Kang
jinoh.kang.kr at gmail.com
Wed Feb 9 10:17:59 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:
v3 -> v4: don't update IOSB on synchronous failure
v4 -> v5:
- Don't use "may_restart" flag; report nonblocking status directly to client
- s/notify_async\(_direct_result\)\?/set_async_direct_result/g
- don't return status from set_async_direct_result
v5 -> v6: unchanged
dlls/ntdll/unix/socket.c | 22 ++++++++++++++++++----
dlls/ntdll/unix/sync.c | 26 ++++++++++++++++++++++++++
dlls/ntdll/unix/unix_private.h | 1 +
server/protocol.def | 1 +
server/sock.c | 21 ++++++++++++++++++++-
5 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 71dfcdd1114..03a9a93d04a 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -686,6 +686,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
NTSTATUS status;
unsigned int i;
ULONG options;
+ BOOL nonblocking, alerted;
if (unix_flags & MSG_OOB)
{
@@ -756,16 +757,29 @@ 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)
+ nonblocking = reply->nonblocking;
+ }
+ SERVER_END_REQ;
+
+ alerted = status == STATUS_ALERTED;
+ if (alerted)
+ {
+ status = try_recv( fd, async, &information );
+ if (status == STATUS_DEVICE_NOT_READY && (force_async || !nonblocking))
+ status = STATUS_PENDING;
+ }
+
+ if (status != STATUS_PENDING)
+ {
+ if (!NT_ERROR(status) || (wait_handle && !alerted))
{
io->Status = status;
io->Information = information;
}
+ release_fileio( &async->io );
}
- SERVER_END_REQ;
-
- if (status != STATUS_PENDING) release_fileio( &async->io );
+ if (alerted) set_async_direct_result( &wait_handle, status, information );
if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
return status;
}
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 442243d8bcf..ee25dfd0099 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -2510,3 +2510,29 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG
}
#endif
+
+/* Notify direct completion of async and close the wait handle if it is no longer needed.
+ * This function is a no-op (returns status as-is) if the supplied handle is NULL.
+ */
+void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, ULONG_PTR information )
+{
+ NTSTATUS ret;
+
+ if (!*optional_handle) return;
+
+ SERVER_START_REQ( set_async_direct_result )
+ {
+ req->handle = wine_server_obj_handle( *optional_handle );
+ req->status = status;
+ req->information = information;
+ ret = wine_server_call( req );
+ if (ret == STATUS_SUCCESS)
+ *optional_handle = wine_server_ptr_handle( reply->handle );
+ }
+ SERVER_END_REQ;
+
+ if (ret != STATUS_SUCCESS)
+ ERR( "cannot report I/O result back to server: %08x\n", ret );
+
+ return;
+}
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index a79edabc37c..75f03706401 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -274,6 +274,7 @@ extern NTSTATUS get_device_info( int fd, struct _FILE_FS_DEVICE_INFORMATION *inf
extern void init_files(void) DECLSPEC_HIDDEN;
extern void init_cpu_info(void) DECLSPEC_HIDDEN;
extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) DECLSPEC_HIDDEN;
+extern void set_async_direct_result( HANDLE *optional_handle, NTSTATUS status, ULONG_PTR information );
extern void dbg_init(void) DECLSPEC_HIDDEN;
diff --git a/server/protocol.def b/server/protocol.def
index 28dee7c48f7..ebd3612df06 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1456,6 +1456,7 @@ enum server_fd_type
@REPLY
obj_handle_t wait; /* handle to wait on for blocking recv */
unsigned int options; /* device open options */
+ int nonblocking; /* is socket non-blocking? */
@END
diff --git a/server/sock.c b/server/sock.c
index 512b7c0f78e..38aaaac9e39 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -3422,6 +3422,24 @@ 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). */
+ 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,7 +3456,7 @@ DECL_HANDLER(recv_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->read_q, async );
/* always reselect; we changed reported_events above */
@@ -3446,6 +3464,7 @@ DECL_HANDLER(recv_socket)
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
More information about the wine-devel
mailing list