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/include/wine/server_protocol.h b/include/wine/server_protocol.h index 5ed95a41c19..be63b290353 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1772,6 +1772,8 @@ struct recv_socket_reply struct reply_header __header; obj_handle_t wait; unsigned int options; + int nonblocking; + char __pad_20[4]; }; @@ -6282,7 +6284,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 744 +#define SERVER_PROTOCOL_VERSION 745 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index ce05e9a38c6..5c0f9a86822 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/request.h b/server/request.h index 128b70fe3cf..98e23bfd4ba 100644 --- a/server/request.h +++ b/server/request.h @@ -1049,7 +1049,8 @@ C_ASSERT( FIELD_OFFSET(struct recv_socket_request, total) == 60 ); C_ASSERT( sizeof(struct recv_socket_request) == 64 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 ); -C_ASSERT( sizeof(struct recv_socket_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, nonblocking) == 16 ); +C_ASSERT( sizeof(struct recv_socket_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, status) == 56 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, total) == 60 ); 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 ); diff --git a/server/trace.c b/server/trace.c index 6b50b9d0d18..1fb39d4f997 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2040,6 +2040,7 @@ static void dump_recv_socket_reply( const struct recv_socket_reply *req ) { fprintf( stderr, " wait=%04x", req->wait ); fprintf( stderr, ", options=%08x", req->options ); + fprintf( stderr, ", nonblocking=%d", req->nonblocking ); } static void dump_send_socket_request( const struct send_socket_request *req )