[RFC PATCH 04/11] server: Attempt to complete I/O request immediately in recv_socket.
Jinoh Kang
jinoh.kang.kr at gmail.com
Sat Jan 22 06:59:55 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:
In the previous approach, we simply test for STATUS_ALERTED, do the I/O and
substitute the status code appropriately. This would obviate the need for:
- The hack where we pass the "nonblocking mode" flag via iosb information field.
- The "is_completion_deferred" function, since we can substitute STATUS_ALERTED
for STATUS_PENDING in the requestor function before doing anything else.
dlls/ntdll/unix/socket.c | 10 +++++++---
include/wine/afd.h | 17 +++++++++++++++++
server/sock.c | 26 +++++++++++++++++++++++++-
3 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 71dfcdd1114..7d3795a953e 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -77,6 +77,7 @@
#include "wsipx.h"
#include "af_irda.h"
#include "wine/afd.h"
+#include "wine/async.h"
#include "unix_private.h"
@@ -656,6 +657,7 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
{
struct async_recv_ioctl *async = user;
int fd, needs_close;
+ BOOL nonblocking;
TRACE( "%#x\n", *status );
@@ -664,11 +666,13 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
if ((*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL )))
return TRUE;
+ nonblocking = *info == AFD_WINE_IN_NONBLOCKING_MODE;
+ if (nonblocking) *info = 0;
*status = try_recv( fd, async, info );
TRACE( "got status %#x, %#lx bytes read\n", *status, *info );
if (needs_close) close( fd );
- if (*status == STATUS_DEVICE_NOT_READY)
+ if (*status == STATUS_DEVICE_NOT_READY && !nonblocking)
return FALSE;
}
release_fileio( &async->io );
@@ -756,7 +760,7 @@ 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)
+ if ((!NT_ERROR(status) || wait_handle) && !is_completion_deferred( status ))
{
io->Status = status;
io->Information = information;
@@ -764,7 +768,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
}
SERVER_END_REQ;
- if (status != STATUS_PENDING) release_fileio( &async->io );
+ if (!is_completion_deferred( status )) release_fileio( &async->io );
if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
return status;
diff --git a/include/wine/afd.h b/include/wine/afd.h
index efd5787e90a..1e26739229d 100644
--- a/include/wine/afd.h
+++ b/include/wine/afd.h
@@ -37,6 +37,23 @@ struct afd_wsabuf_32
# define WS(x) x
#endif
+
+/* Used in the iosb.result field to indicate that the current socket I/O
+ * operation is in synchronous non-blocking mode. This value is normally
+ * transmitted via the APC_ASYNC_IO system APC call (with status STATUS_ALERTED)
+ * when the server gives the client a chance to complete the I/O synchronously
+ * before resuming the request as fully asynchronous I/O or failing it.
+ * If the I/O fails with EWOULDBLOCK and the iosb.result field is set to any
+ * other value, the client shall request the server to resume the asynchronous
+ * operation.
+ *
+ * The value (ULONG_PTR)-1 (the maximum value of ULONG_PTR) is chosen so that
+ * it will be least likely to be confused with "the number of bytes transferred
+ * so far." Any I/O operation that has made it to the maximum number of bytes
+ * shall complete immediately anyway.
+ */
+#define AFD_WINE_IN_NONBLOCKING_MODE ((ULONG_PTR)-1)
+
#define IOCTL_AFD_BIND CTL_CODE(FILE_DEVICE_BEEP, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
diff --git a/server/sock.c b/server/sock.c
index 650e67a2e0a..03c867317b2 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -91,6 +91,7 @@
#include "wsipx.h"
#include "af_irda.h"
#include "wine/afd.h"
+#include "wine/async.h"
#include "process.h"
#include "file.h"
@@ -3397,6 +3398,7 @@ DECL_HANDLER(recv_socket)
{
struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
unsigned int status = req->status;
+ int pending = 0;
timeout_t timeout = 0;
struct async *async;
struct fd *fd;
@@ -3422,6 +3424,25 @@ 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). */
+ pending = status == 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,12 +3459,15 @@ DECL_HANDLER(recv_socket)
if (timeout)
async_set_timeout( async, timeout, STATUS_IO_TIMEOUT );
- if (status == STATUS_PENDING)
+ if (is_completion_deferred( status ))
queue_async( &sock->read_q, async );
/* always reselect; we changed reported_events above */
sock_reselect( sock );
+ if (status == STATUS_ALERTED)
+ async_start_sync_io_request( async, pending ? 0 : AFD_WINE_IN_NONBLOCKING_MODE );
+
reply->wait = async_handoff( async, NULL, 0 );
reply->options = get_fd_options( fd );
release_object( async );
--
2.31.1
More information about the wine-devel
mailing list