From: Conor McCarthy cmccarthy@codeweavers.com
Supports waitable timer closure while pending, and we must not wait on invalid handles generally. Details are in code comments. --- dlls/ntdll/tests/threadpool.c | 4 ---- dlls/ntdll/threadpool.c | 25 ++++++++++++++++++++++++- dlls/rtworkq/tests/rtworkq.c | 2 -- 3 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index b903165a4fb..55d59d50d86 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -2440,13 +2440,11 @@ static void test_tp_wait_early_closure(void) CloseHandle(timer); pTpSetWait(wait2, semaphores[0], NULL); result = WaitForSingleObject(semaphore, 200); - todo_wine ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphore, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); @@ -2460,13 +2458,11 @@ static void test_tp_wait_early_closure(void) ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphore, 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); result = WaitForSingleObject(semaphore, 300); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 0x10001, "expected info.userdata = 0x10001, got %lu\n", info.userdata);
/* cleanup */ diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 6448477a5cb..5fb11c35698 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -194,6 +194,7 @@ struct threadpool_object struct list wait_entry; ULONGLONG timeout; HANDLE handle; + HANDLE duped_handle; DWORD flags; RTL_WAITORTIMERCALLBACKFUNC rtl_callback; } wait; @@ -1296,7 +1297,10 @@ static void CALLBACK waitqueue_thread_proc( void *param ) assert( num_handles < MAXIMUM_WAITQUEUE_OBJECTS ); InterlockedIncrement( &wait->refcount ); objects[num_handles] = wait; - handles[num_handles] = wait->u.wait.handle; + /* NtWaitForMultipleObjects() fails if any invalid handles are passed, and one invalid handle + * should not affect other waiting items. The calling app is allowed to close waitable timer + * handles immediately after submission, so we need a duplicate for those in particular. */ + handles[num_handles] = wait->u.wait.duped_handle ? wait->u.wait.duped_handle : wait->u.wait.handle; update_serials[num_handles] = wait->update_serial; num_handles++; } @@ -1437,6 +1441,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait ) wait->u.wait.wait_pending = FALSE; wait->u.wait.timeout = 0; wait->u.wait.handle = NULL; + wait->u.wait.duped_handle = NULL;
RtlEnterCriticalSection( &waitqueue.cs );
@@ -2117,6 +2122,15 @@ static void tp_object_prepare_shutdown( struct threadpool_object *object ) tp_ioqueue_unlock( object ); }
+static void tp_wait_close_duped_handle( struct threadpool_object *wait ) +{ + if (wait->u.wait.duped_handle) + { + NtClose( wait->u.wait.duped_handle ); + wait->u.wait.duped_handle = NULL; + } +} + /*********************************************************************** * tp_object_release (internal) * @@ -2150,6 +2164,9 @@ static BOOL tp_object_release( struct threadpool_object *object ) tp_group_release( group ); }
+ if (object->type == TP_OBJECT_TYPE_WAIT) + tp_wait_close_duped_handle( object ); + tp_threadpool_unlock( object->pool );
if (object->race_dll) @@ -3067,6 +3084,12 @@ VOID WINAPI TpSetWait( TP_WAIT *wait, HANDLE handle, LARGE_INTEGER *timeout ) assert( this->u.wait.bucket );
same_handle = this->u.wait.handle == handle; + tp_wait_close_duped_handle( this ); + if (handle && NtDuplicateObject( NtCurrentProcess(), handle, NtCurrentProcess(), + &this->u.wait.duped_handle, 0, 0, DUPLICATE_SAME_ACCESS ) != STATUS_SUCCESS) + { + WARN( "Failed to duplicate handle.\n" ); + } this->u.wait.handle = handle;
if (handle || this->u.wait.wait_pending) diff --git a/dlls/rtworkq/tests/rtworkq.c b/dlls/rtworkq/tests/rtworkq.c index 440a574aaf3..812c1f03c1e 100644 --- a/dlls/rtworkq/tests/rtworkq.c +++ b/dlls/rtworkq/tests/rtworkq.c @@ -556,7 +556,6 @@ static void test_scheduled_items(void) ok(hr == S_OK, "got %#lx\n", hr); IRtwqAsyncResult_Release(result2); res = wait_async_callback_result(&test_callback->IRtwqAsyncCallback_iface, 200, &callback_result); - todo_wine ok(res == 0, "got %#lx\n", res);
SetEvent(event); @@ -584,7 +583,6 @@ static void test_scheduled_items(void) IRtwqAsyncResult_Release(result2); SetEvent(event2); res = wait_async_callback_result(&test_callback2->IRtwqAsyncCallback_iface, 100, &callback_result); - todo_wine ok(res == 0, "got %#lx\n", res);
hr = RtwqCancelWorkItem(key);