From: Paul Gofman pgofman@codeweavers.com
--- dlls/ws2_32/tests/afd.c | 82 +++++++++++++++++++++++++++-------------- server/async.c | 14 +++++++ server/file.h | 1 + server/sock.c | 2 + 4 files changed, 72 insertions(+), 27 deletions(-)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index 4e76d201e49..f30d5d48d66 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -2450,9 +2450,16 @@ static DWORD WINAPI async_ioctl_thread(void *params) return io->ret; }
-static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, BOOL other_process_handle, HANDLE *handle, HANDLE event, - PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, - void *out_buffer, ULONG out_size) +enum test_close_handle_type +{ + TEST_CLOSE_SAME_PROCESS, + TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT, + TEST_CLOSE_OTHER_AFTER_THREAD_EXIT, +}; + +static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, enum test_close_handle_type other_process_handle, + HANDLE *handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io, ULONG code, + void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size) { HANDLE thread, handle2; struct ioctl_params p; @@ -2481,7 +2488,8 @@ static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, BOOL other ok(!!thread, "got NULL.\n"); ret = WaitForSingleObject(p.complete_event, INFINITE); ok(ret == WAIT_OBJECT_0, "got ret %#lx.\n", ret); - CloseHandle(*handle); + if (other_process_handle != TEST_CLOSE_OTHER_AFTER_THREAD_EXIT) + CloseHandle(*handle); SetEvent(p.handle_closed_event); if (kill_thread) TerminateThread(thread, -1); @@ -2489,6 +2497,8 @@ static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, BOOL other ret = WaitForSingleObject(thread, INFINITE); ok(ret == WAIT_OBJECT_0, "got ret %#lx.\n", ret); CloseHandle(thread); + if (other_process_handle == TEST_CLOSE_OTHER_AFTER_THREAD_EXIT) + CloseHandle(*handle); SleepEx(0, TRUE); *handle = other_process_handle ? NULL : handle2; return p.ret; @@ -2509,7 +2519,7 @@ static void test_async_thread_termination(void) BOOL event; PIO_APC_ROUTINE apc; void *apc_context; - BOOL other_process_handle; + enum test_close_handle_type close_type; } tests[] = { @@ -2530,23 +2540,41 @@ static void test_async_thread_termination(void) {FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef}, {TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef},
- /* other process handle */ - {FALSE, TRUE, NULL, NULL, TRUE}, - {TRUE, TRUE, NULL, NULL, TRUE}, - {FALSE, FALSE, NULL, NULL, TRUE}, - {TRUE, FALSE, NULL, NULL, TRUE}, - {FALSE, TRUE, test_async_thread_termination_apc, NULL, TRUE}, - {TRUE, TRUE, test_async_thread_termination_apc, NULL, TRUE}, - {FALSE, FALSE, test_async_thread_termination_apc, NULL, TRUE}, - {TRUE, FALSE, test_async_thread_termination_apc, NULL, TRUE}, - {FALSE, TRUE, NULL, (void *)0xdeadbeef, TRUE}, - {TRUE, TRUE, NULL, (void *)0xdeadbeef, TRUE}, - {FALSE, FALSE, NULL, (void *)0xdeadbeef, TRUE}, - {TRUE, FALSE, NULL, (void *)0xdeadbeef, TRUE}, - {FALSE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TRUE}, - {TRUE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TRUE}, - {FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TRUE}, - {TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TRUE}, + /* closing handle before thread exit */ + {FALSE, TRUE, NULL, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, TRUE, NULL, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, FALSE, NULL, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, FALSE, NULL, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, TRUE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, TRUE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, FALSE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, FALSE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, TRUE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, TRUE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, FALSE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, FALSE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + + /* closing handle after thread exit */ + {FALSE, TRUE, NULL, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, TRUE, NULL, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {FALSE, FALSE, NULL, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, FALSE, NULL, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {FALSE, TRUE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, TRUE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {FALSE, FALSE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, FALSE, test_async_thread_termination_apc, NULL, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {FALSE, TRUE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, TRUE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, FALSE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {TRUE, FALSE, NULL, (void *)0xdeadbeef, TEST_CLOSE_OTHER_BEFORE_THREAD_EXIT}, + {FALSE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, + {TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef, TEST_CLOSE_OTHER_AFTER_THREAD_EXIT}, };
const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; @@ -2589,7 +2617,7 @@ static void test_async_thread_termination(void)
memset(&io, 0xcc, sizeof(io)); ResetEvent(event); - ret = thread_NtDeviceIoControlFile(tests[i].kill_thread, tests[i].other_process_handle, (HANDLE *)&listener, tests[i].event ? event : NULL, + ret = thread_NtDeviceIoControlFile(tests[i].kill_thread, tests[i].close_type, (HANDLE *)&listener, tests[i].event ? event : NULL, tests[i].apc, tests[i].apc_context, &io, IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); ok(ret == STATUS_PENDING, "got %#x\n", ret); @@ -2610,7 +2638,7 @@ static void test_async_thread_termination(void)
for (i = 0; i < ARRAY_SIZE(tests); ++i) { - winetest_push_context("test %u, other process %d", i, tests[i].other_process_handle); + winetest_push_context("test %u, other process %d", i, tests[i].close_type);
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); ret = bind(listener, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); @@ -2624,7 +2652,7 @@ static void test_async_thread_termination(void)
memset(&io, 0xcc, sizeof(io)); ResetEvent(event); - ret = thread_NtDeviceIoControlFile(tests[i].kill_thread, tests[i].other_process_handle, (HANDLE *)&listener, tests[i].event ? event : NULL, + ret = thread_NtDeviceIoControlFile(tests[i].kill_thread, tests[i].close_type, (HANDLE *)&listener, tests[i].event ? event : NULL, tests[i].apc, tests[i].apc_context, &io, IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); if (tests[i].apc) @@ -2634,9 +2662,9 @@ static void test_async_thread_termination(void) continue; } ok(ret == STATUS_PENDING, "got %#x\n", ret); - if (tests[i].other_process_handle || !tests[i].apc_context || tests[i].event) + if (tests[i].close_type || !tests[i].apc_context || tests[i].event) { - if (tests[i].other_process_handle && !tests[i].event && !tests[i].apc && !!tests[i].apc_context) + if (tests[i].close_type && !tests[i].event && !tests[i].apc && !!tests[i].apc_context) expected = 0xcccccccc; else expected = STATUS_CANCELLED; diff --git a/server/async.c b/server/async.c index 95e86c6f603..649a296472f 100644 --- a/server/async.c +++ b/server/async.c @@ -632,6 +632,20 @@ restart: } }
+void cancel_terminated_threads_asyncs( struct process *process, struct object *obj ) +{ + struct async *async; + +restart: + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) + { + if (async->terminated || async->canceled || async->thread->state != TERMINATED) continue; + async->canceled = 1; + fd_cancel_async( async->fd, async ); + goto restart; + } +} + /* wake up async operations on the queue */ void async_wake_up( struct async_queue *queue, unsigned int status ) { diff --git a/server/file.h b/server/file.h index 210fcec5e78..bc8537ac54c 100644 --- a/server/file.h +++ b/server/file.h @@ -246,6 +246,7 @@ extern struct thread *async_get_thread( struct async *async ); extern struct async *find_pending_async( struct async_queue *queue ); extern void cancel_process_asyncs( struct process *process ); extern void cancel_terminating_thread_asyncs( struct thread *thread ); +extern void cancel_terminated_threads_asyncs( struct process *process, struct object *obj );
static inline void init_async_queue( struct async_queue *queue ) { diff --git a/server/sock.c b/server/sock.c index 34be6ea22ef..af618c9e425 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1655,6 +1655,8 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h if (signaled) complete_async_poll( poll_req, STATUS_SUCCESS ); } } + if (sock->obj.handle_count == 1 || get_obj_handle_count( process, obj ) == 1) + cancel_terminated_threads_asyncs( process, obj );
return 1; }