From 553ae488d04be572b3125eab486e35abb1bfee6c Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Sun, 5 Jul 2015 02:24:02 +0200 Subject: [PATCH] ntdll/tests: Add basic tests for threadpool wait objects. --- dlls/ntdll/tests/threadpool.c | 266 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index 0671202f1eb..b40e0c10f2e 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -24,11 +24,13 @@ static HMODULE hntdll = 0; static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID); static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); +static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *); static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD); static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *); static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *); +static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *); static VOID (WINAPI *pTpPostWork)(TP_WORK *); static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *); static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID); @@ -37,8 +39,10 @@ static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *); static VOID (WINAPI *pTpReleaseWork)(TP_WORK *); static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD); static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG); +static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *); static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL); +static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL); static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL); #define NTDLL_GET_PROC(func) \ @@ -61,6 +65,7 @@ static BOOL init_threadpool(void) NTDLL_GET_PROC(TpAllocCleanupGroup); NTDLL_GET_PROC(TpAllocPool); NTDLL_GET_PROC(TpAllocTimer); + NTDLL_GET_PROC(TpAllocWait); NTDLL_GET_PROC(TpAllocWork); NTDLL_GET_PROC(TpCallbackMayRunLong); NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); @@ -71,11 +76,14 @@ static BOOL init_threadpool(void) NTDLL_GET_PROC(TpReleaseCleanupGroupMembers); NTDLL_GET_PROC(TpReleasePool); NTDLL_GET_PROC(TpReleaseTimer); + NTDLL_GET_PROC(TpReleaseWait); NTDLL_GET_PROC(TpReleaseWork); NTDLL_GET_PROC(TpSetPoolMaxThreads); NTDLL_GET_PROC(TpSetTimer); + NTDLL_GET_PROC(TpSetWait); NTDLL_GET_PROC(TpSimpleTryPost); NTDLL_GET_PROC(TpWaitForTimer); + NTDLL_GET_PROC(TpWaitForWait); NTDLL_GET_PROC(TpWaitForWork); if (!pTpAllocPool) @@ -906,6 +914,263 @@ static void test_tp_window_length(void) CloseHandle(semaphore); } +struct wait_info +{ + HANDLE semaphore; + LONG userdata; +}; + +static void CALLBACK wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, + TP_WAIT *wait, TP_WAIT_RESULT result) +{ + struct wait_info *info = userdata; + trace("Running wait callback\n"); + + if (result == WAIT_OBJECT_0) + InterlockedIncrement(&info->userdata); + else if (result == WAIT_TIMEOUT) + InterlockedExchangeAdd(&info->userdata, 0x10000); + else + ok(0, "unexpected result %u\n", result); + ReleaseSemaphore(info->semaphore, 1, NULL); +} + +static void test_tp_wait(void) +{ + TP_CALLBACK_ENVIRON environment; + TP_WAIT *wait1, *wait2; + struct wait_info info; + HANDLE semaphores[2]; + LARGE_INTEGER when; + NTSTATUS status; + TP_POOL *pool; + DWORD result; + + semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL); + ok(semaphores[0] != NULL, "failed to create semaphore\n"); + semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); + ok(semaphores[1] != NULL, "failed to create semaphore\n"); + info.semaphore = semaphores[0]; + + /* allocate new threadpool */ + pool = NULL; + status = pTpAllocPool(&pool, NULL); + ok(!status, "TpAllocPool failed with status %x\n", status); + ok(pool != NULL, "expected pool != NULL\n"); + + /* allocate new wait items */ + memset(&environment, 0, sizeof(environment)); + environment.Version = 1; + environment.Pool = pool; + + wait1 = NULL; + status = pTpAllocWait(&wait1, wait_cb, &info, &environment); + ok(!status, "TpAllocWait failed with status %x\n", status); + ok(wait1 != NULL, "expected wait1 != NULL\n"); + + wait2 = NULL; + status = pTpAllocWait(&wait2, wait_cb, &info, &environment); + ok(!status, "TpAllocWait failed with status %x\n", status); + ok(wait2 != NULL, "expected wait2 != NULL\n"); + + /* infinite timeout, signal the semaphore immediately */ + info.userdata = 0; + pTpSetWait(wait1, semaphores[1], NULL); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* relative timeout, no event */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)200 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* repeat test with call to TpWaitForWait(..., TRUE) */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)200 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + pTpWaitForWait(wait1, TRUE); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */, + "WaitForSingleObject returned %u\n", result); + if (result == WAIT_OBJECT_0) + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + else + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* relative timeout, with event */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)200 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* repeat test with call to TpWaitForWait(..., TRUE) */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)200 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + pTpWaitForWait(wait1, TRUE); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */, + "WaitForSingleObject returned %u\n", result); + if (result == WAIT_OBJECT_0) + { + ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + } + else + { + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + } + + /* absolute timeout, no event */ + info.userdata = 0; + NtQuerySystemTime( &when ); + when.QuadPart += (ULONGLONG)200 * 10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* absolute timeout, with event */ + info.userdata = 0; + NtQuerySystemTime( &when ); + when.QuadPart += (ULONGLONG)200 * 10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* test timeout of zero */ + info.userdata = 0; + when.QuadPart = 0; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* cancel a pending wait */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)250 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + pTpSetWait(wait1, NULL, (void *)0xdeadbeef); + Sleep(50); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + + /* test with INVALID_HANDLE_VALUE */ + info.userdata = 0; + when.QuadPart = 0; + pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + + /* cancel a pending wait with INVALID_HANDLE_VALUE */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)250 * -10000; + pTpSetWait(wait1, semaphores[1], &when); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + when.QuadPart = 0; + pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when); + Sleep(50); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + + CloseHandle(semaphores[1]); + semaphores[1] = CreateSemaphoreW(NULL, 0, 2, NULL); + ok(semaphores[1] != NULL, "failed to create semaphore\n"); + + /* add two wait objects with the same semaphore */ + info.userdata = 0; + pTpSetWait(wait1, semaphores[1], NULL); + pTpSetWait(wait2, semaphores[1], NULL); + Sleep(50); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* repeat test above with release count 2 */ + info.userdata = 0; + pTpSetWait(wait1, semaphores[1], NULL); + pTpSetWait(wait2, semaphores[1], NULL); + Sleep(50); + result = ReleaseSemaphore(semaphores[1], 2, NULL); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(info.userdata == 2, "expected info.userdata = 2, got %u\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + + /* cleanup */ + pTpReleaseWait(wait1); + pTpReleaseWait(wait2); + pTpReleasePool(pool); + CloseHandle(semaphores[0]); + CloseHandle(semaphores[1]); +} + START_TEST(threadpool) { if (!init_threadpool()) @@ -919,4 +1184,5 @@ START_TEST(threadpool) test_tp_disassociate(); test_tp_timer(); test_tp_window_length(); + test_tp_wait(); } -- 2.11.4.GIT