Signed-off-by: Guillaume Charifi guillaume.charifi@sfr.fr --- dlls/ntdll/unix/socket.c | 6 +++--- include/wine/afd.h | 2 +- server/protocol.def | 1 + server/sock.c | 40 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 8469def786a..b1b90b363ee 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -747,12 +747,11 @@ static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi || in_size < offsetof( struct afd_poll_params, sockets[params->count] )) return STATUS_INVALID_PARAMETER;
- TRACE( "timeout %s, count %u, unknown %#x, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n", - wine_dbgstr_longlong(params->timeout), params->count, params->unknown, + TRACE( "timeout %s, count %u, exclusive %u, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n", + wine_dbgstr_longlong(params->timeout), params->count, params->exclusive, params->padding[0], params->padding[1], params->padding[2], params->sockets[0].socket, params->sockets[0].flags );
- if (params->unknown) FIXME( "unknown boolean is %#x\n", params->unknown ); if (params->padding[0]) FIXME( "padding[0] is %#x\n", params->padding[0] ); if (params->padding[1]) FIXME( "padding[1] is %#x\n", params->padding[1] ); if (params->padding[2]) FIXME( "padding[2] is %#x\n", params->padding[2] ); @@ -793,6 +792,7 @@ static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi SERVER_START_REQ( poll_socket ) { req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); + req->exclusive = !!params->exclusive; req->timeout = params->timeout; wine_server_add_data( req, input, params->count * sizeof(*input) ); wine_server_set_reply( req, async->sockets, params->count * sizeof(async->sockets[0]) ); diff --git a/include/wine/afd.h b/include/wine/afd.h index e67ecae25a9..f4682f464e8 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -104,7 +104,7 @@ struct afd_poll_params { LONGLONG timeout; unsigned int count; - BOOLEAN unknown; + BOOLEAN exclusive; BOOLEAN padding[3]; struct { diff --git a/server/protocol.def b/server/protocol.def index 133d6ad0552..c6da8576f39 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1450,6 +1450,7 @@ struct poll_socket_output
/* Perform an async poll on a socket */ @REQ(poll_socket) + int exclusive; async_data_t async; /* async I/O parameters */ timeout_t timeout; /* timeout */ VARARG(sockets,poll_socket_input); /* list of sockets to poll */ diff --git a/server/sock.c b/server/sock.c index 6452cfb0dd7..f6f0bf06f2e 100644 --- a/server/sock.c +++ b/server/sock.c @@ -128,6 +128,7 @@ struct poll_req struct async *async; struct iosb *iosb; struct timeout_user *timeout; + int exclusive; unsigned int count; struct poll_socket_output *output; struct @@ -205,6 +206,7 @@ struct sock struct list accept_list; /* list of pending accept requests */ struct accept_req *accept_recv_req; /* pending accept-into request which will recv on this socket */ struct connect_req *connect_req; /* pending connection request */ + struct poll_req *main_poll; /* main poll */ union win_sockaddr addr; /* socket name */ int addr_len; /* socket name length */ unsigned int rcvbuf; /* advisory recv buffer size */ @@ -787,7 +789,13 @@ static void free_poll_req( void *private ) if (req->timeout) remove_timeout_user( req->timeout );
for (i = 0; i < req->count; ++i) + { + if (req->sockets[i].sock->main_poll == req) + req->sockets[i].sock->main_poll = NULL; + release_object( req->sockets[i].sock ); + } + release_object( req->async ); release_object( req->iosb ); list_remove( &req->entry ); @@ -1382,6 +1390,7 @@ static struct sock *create_socket(void) sock->ifchange_obj = NULL; sock->accept_recv_req = NULL; sock->connect_req = NULL; + sock->main_poll = NULL; memset( &sock->addr, 0, sizeof(sock->addr) ); sock->addr_len = 0; sock->rd_shutdown = 0; @@ -2839,7 +2848,31 @@ static int poll_single_socket( struct sock *sock, int mask ) return get_poll_flags( sock, pollfd.revents ) & mask; }
-static void poll_socket( struct sock *poll_sock, struct async *async, timeout_t timeout, +static void poll_handle_exclusive(struct poll_req *req) +{ + unsigned int i; + + for (i = 0; i < req->count; ++i) + { + struct poll_req *main_poll = req->sockets[i].sock->main_poll; + + if (main_poll && main_poll->exclusive && req->exclusive) + { + /* Previous exclusive main poll replaced by new exclusive poll. */ + main_poll->iosb->status = STATUS_SUCCESS; + main_poll->iosb->out_data = main_poll->output; + main_poll->iosb->out_size = main_poll->count * sizeof(*main_poll->output); + async_terminate(main_poll->async, STATUS_ALERTED); + + main_poll = NULL; + } + + if (!main_poll) + req->sockets[i].sock->main_poll = req; + } +} + +static void poll_socket( struct sock *poll_sock, struct async *async, int exclusive, timeout_t timeout, unsigned int count, const struct poll_socket_input *input ) { struct poll_socket_output *output; @@ -2880,11 +2913,14 @@ static void poll_socket( struct sock *poll_sock, struct async *async, timeout_t req->sockets[i].flags = input[i].flags; }
+ req->exclusive = exclusive; req->count = count; req->async = (struct async *)grab_object( async ); req->iosb = async_get_iosb( async ); req->output = output;
+ poll_handle_exclusive(req); + list_add_tail( &poll_list, &req->entry ); async_set_completion_callback( async, free_poll_req, req ); queue_async( &poll_sock->poll_q, async ); @@ -3285,7 +3321,7 @@ DECL_HANDLER(poll_socket)
if ((async = create_request_async( sock->fd, get_fd_comp_flags( sock->fd ), &req->async ))) { - poll_socket( sock, async, req->timeout, count, input ); + poll_socket( sock, async, req->exclusive, req->timeout, count, input ); reply->wait = async_handoff( async, NULL, 0 ); reply->options = get_fd_options( sock->fd ); release_object( async );
Signed-off-by: Guillaume Charifi guillaume.charifi@sfr.fr --- dlls/ws2_32/tests/afd.c | 348 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index eb7c8ee50de..5e1eae29049 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -31,6 +31,8 @@ #include "wine/afd.h" #include "wine/test.h"
+#define TIMEOUT_INFINITE _I64_MAX + static void tcp_socketpair(SOCKET *src, SOCKET *dst) { SOCKET server = INVALID_SOCKET; @@ -212,6 +214,7 @@ static void test_poll(void)
in_params->timeout = 0; in_params->count = 1; + in_params->exclusive = FALSE; in_params->sockets[0].socket = listener; in_params->sockets[0].flags = ~0; in_params->sockets[0].status = 0xdeadbeef; @@ -746,6 +749,350 @@ static void test_poll(void) free(large_buffer); }
+struct poll_exclusive_thread_cb_ctx +{ + SOCKET ctl_sock; + HANDLE event; + IO_STATUS_BLOCK *io; + ULONG params_size; + struct afd_poll_params *in_params; + struct afd_poll_params *out_params; +}; + +static DWORD WINAPI poll_exclusive_thread_cb(void *param) +{ + struct poll_exclusive_thread_cb_ctx *ctx = param; + int ret; + + ret = NtDeviceIoControlFile((HANDLE)ctx->ctl_sock, ctx->event, NULL, NULL, ctx->io, + IOCTL_AFD_POLL, ctx->in_params, ctx->params_size, ctx->out_params, ctx->params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(ctx->event, 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + return 0; +} + +#define POLL_SOCK_CNT 2 +#define POLL_CNT 4 + +static void test_poll_exclusive(void) +{ + char in_buffer[offsetof(struct afd_poll_params, sockets[POLL_SOCK_CNT + 1])]; + char out_buffers[POLL_CNT][offsetof(struct afd_poll_params, sockets[POLL_SOCK_CNT + 1])]; + struct afd_poll_params *in_params = (struct afd_poll_params *)in_buffer; + struct afd_poll_params *out_params[POLL_CNT] = { + (struct afd_poll_params *)out_buffers[0], + (struct afd_poll_params *)out_buffers[1], + (struct afd_poll_params *)out_buffers[2], + (struct afd_poll_params *)out_buffers[3] + }; + SOCKET ctl_sock; + SOCKET socks[POLL_CNT]; + IO_STATUS_BLOCK io[POLL_CNT]; + HANDLE events[POLL_CNT]; + ULONG params_size; + struct poll_exclusive_thread_cb_ctx cb_ctx; + HANDLE thrd; + size_t i; + int ret; + + ctl_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + for (i = 0; i < POLL_CNT; i++) + { + socks[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + events[i] = CreateEventW(NULL, TRUE, FALSE, NULL); + } + + params_size = offsetof(struct afd_poll_params, sockets[1]); + + in_params->timeout = TIMEOUT_INFINITE; + in_params->count = 1; + in_params->exclusive = FALSE; + in_params->sockets[0].socket = socks[0]; + in_params->sockets[0].flags = ~0; + in_params->sockets[0].status = 0; + + /***** Exclusive explicitly terminated *****/ + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + /***** Same socket tests *****/ + + /* Basic non-exclusive behavior as reference. */ + + in_params->exclusive = FALSE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* If the main poll is exclusive it is terminated by the following exclusive. */ + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* If the main poll is non-exclusive neither itself nor the following exclusives are terminated. */ + + in_params->exclusive = FALSE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[2], NULL, NULL, &io[2], + IOCTL_AFD_POLL, in_params, params_size, out_params[2], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[2], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* A new poll is considered the main poll if no others are queued at the time. */ + + in_params->exclusive = FALSE; + + ret = NtDeviceIoControlFile((HANDLE)socks[0], events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + CancelIo((HANDLE)socks[0]); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[2], NULL, NULL, &io[2], + IOCTL_AFD_POLL, in_params, params_size, out_params[2], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = WaitForSingleObject(events[2], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* The exclusive check does not happen again after the call to NtDeviceIoControlFile(). */ + + in_params->exclusive = FALSE; + + ret = NtDeviceIoControlFile((HANDLE)socks[0], events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + CancelIo((HANDLE)socks[0]); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[2], NULL, NULL, &io[2], + IOCTL_AFD_POLL, in_params, params_size, out_params[2], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[2], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* After the main poll is terminated, any subsequent poll becomes the main + * and can be terminated if exclusive. */ + + in_params->exclusive = FALSE; + + ret = NtDeviceIoControlFile((HANDLE)socks[0], events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + in_params->exclusive = TRUE; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + CancelIo((HANDLE)socks[0]); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[2], NULL, NULL, &io[2], + IOCTL_AFD_POLL, in_params, params_size, out_params[2], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[3], NULL, NULL, &io[3], + IOCTL_AFD_POLL, in_params, params_size, out_params[3], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[2], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = WaitForSingleObject(events[3], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /***** Exclusive poll on different sockets *****/ + + in_params->exclusive = TRUE; + in_params->sockets[0].socket = socks[0]; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + in_params->sockets[0].socket = socks[1]; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /***** Exclusive poll from other thread *****/ + + in_params->exclusive = TRUE; + in_params->sockets[0].socket = ctl_sock; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + cb_ctx.ctl_sock = ctl_sock; + cb_ctx.event = events[1]; + cb_ctx.io = &io[1]; + cb_ctx.params_size = params_size; + cb_ctx.in_params = in_params; + cb_ctx.out_params = out_params[1]; + + thrd = CreateThread(NULL, 0, poll_exclusive_thread_cb, &cb_ctx, 0, NULL); + WaitForSingleObject(thrd, INFINITE); + CloseHandle(thrd); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /***** Exclusive poll on overlapping socket sets *****/ + + params_size = offsetof(struct afd_poll_params, sockets[2]); + + in_params->exclusive = TRUE; + in_params->count = 2; + in_params->sockets[0].socket = ctl_sock; + in_params->sockets[1].socket = socks[0]; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[0], NULL, NULL, &io[0], + IOCTL_AFD_POLL, in_params, params_size, out_params[0], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + in_params->sockets[0].socket = ctl_sock; + in_params->sockets[1].socket = socks[1]; + + ret = NtDeviceIoControlFile((HANDLE)ctl_sock, events[1], NULL, NULL, &io[1], + IOCTL_AFD_POLL, in_params, params_size, out_params[1], params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = WaitForSingleObject(events[0], 100); + ok(ret == STATUS_SUCCESS, "got %#x\n", ret); + + ret = WaitForSingleObject(events[1], 100); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + CancelIo((HANDLE)ctl_sock); + + /* Cleanup. */ + + closesocket(ctl_sock); + + for (i = 0; i < POLL_CNT; i++) + { + closesocket(socks[i]); + CloseHandle(events[i]); + } +} + +#undef POLL_SOCK_CNT +#undef POLL_CNT + static void test_poll_completion_port(void) { struct afd_poll_params params = {0}; @@ -1751,6 +2098,7 @@ START_TEST(afd)
test_open_device(); test_poll(); + test_poll_exclusive(); test_poll_completion_port(); test_recv(); test_event_select();
On 9/10/21 12:33 PM, Guillaume Charifi wrote:
@@ -787,7 +789,13 @@ static void free_poll_req( void *private ) if (req->timeout) remove_timeout_user( req->timeout );
for (i = 0; i < req->count; ++i)
- {
if (req->sockets[i].sock->main_poll == req)
req->sockets[i].sock->main_poll = NULL;
release_object( req->sockets[i].sock );
- }
release_object( req->async ); release_object( req->iosb ); list_remove( &req->entry );
This needs to be done earlier, which is why you need the cancel_async callback.
Asyncs terminated by the server aren't destroyed immediately; they're kept around until an APC_ASYNC_IO is delivered and processed by the client. Only then is the completion callback called. This is because some asyncs can be restarted by the client.
However, this means that if a new poll_socket request arrives after the async is canceled or completed, but before APC_ASYNC_IO is processed, with this patch it won't be appropriately marked as the main one. So the main poll needs to get reset immediately when the async is terminated, including via cancellation.