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@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 );