diff --git a/dll/ntdll/def/ntdll.spec b/dll/ntdll/def/ntdll.spec index 62f374c05fbd9..88b7f881714e3 100644 --- a/dll/ntdll/def/ntdll.spec +++ b/dll/ntdll/def/ntdll.spec @@ -1311,45 +1311,47 @@ @ stub -version=0x600+ ShipAssertMsgA @ stub -version=0x600+ ShipAssertMsgW @ stub -version=0x600+ TpAllocAlpcCompletion -@ stub -version=0x600+ TpAllocCleanupGroup -@ stub -version=0x600+ TpAllocIoCompletion -@ stub -version=0x600+ TpAllocPool -@ stub -version=0x600+ TpAllocTimer -@ stub -version=0x600+ TpAllocWait -@ stub -version=0x600+ TpAllocWork -@ stub -version=0x600+ TpCallbackLeaveCriticalSectionOnCompletion -@ stub -version=0x600+ TpCallbackMayRunLong -@ stub -version=0x600+ TpCallbackReleaseMutexOnCompletion -@ stub -version=0x600+ TpCallbackReleaseSemaphoreOnCompletion -@ stub -version=0x600+ TpCallbackSetEventOnCompletion -@ stub -version=0x600+ TpCallbackUnloadDllOnCompletion -@ stub -version=0x600+ TpCancelAsyncIoOperation +@ stdcall -version=0x600+ TpAllocCleanupGroup(ptr) +@ stdcall -version=0x600+ TpAllocIoCompletion(ptr ptr ptr ptr ptr) +@ stdcall -version=0x600+ TpAllocPool(ptr ptr) +@ stdcall -version=0x600+ TpAllocTimer(ptr ptr ptr ptr) +@ stdcall -version=0x600+ TpAllocWait(ptr ptr ptr ptr) +@ stdcall -version=0x600+ TpAllocWork(ptr ptr ptr ptr) +@ stdcall -version=0x600+ TpCallbackLeaveCriticalSectionOnCompletion(ptr ptr) +@ stdcall -version=0x600+ TpCallbackMayRunLong(ptr) +@ stdcall -version=0x600+ TpCallbackReleaseMutexOnCompletion(ptr ptr) +@ stdcall -version=0x600+ TpCallbackReleaseSemaphoreOnCompletion(ptr ptr long) +@ stdcall -version=0x600+ TpCallbackSetEventOnCompletion(ptr ptr) +@ stdcall -version=0x600+ TpCallbackUnloadDllOnCompletion(ptr ptr) +@ stdcall -version=0x600+ TpCancelAsyncIoOperation(ptr) @ stub -version=0x600+ TpCaptureCaller @ stub -version=0x600+ TpCheckTerminateWorker @ stub -version=0x600+ TpDbgDumpHeapUsage @ stub -version=0x600+ TpDbgSetLogRoutine -@ stub -version=0x600+ TpDisassociateCallback -@ stub -version=0x600+ TpIsTimerSet -@ stub -version=0x600+ TpPostWork +@ stdcall -version=0x600+ TpDisassociateCallback(ptr) +@ stdcall -version=0x600+ TpIsTimerSet(ptr) +@ stdcall -version=0x600+ TpPostWork(ptr) +@ stdcall -version=0x600+ TpQueryPoolStackInformation(ptr ptr) @ stub -version=0x600+ TpReleaseAlpcCompletion -@ stub -version=0x600+ TpReleaseCleanupGroup -@ stub -version=0x600+ TpReleaseCleanupGroupMembers -@ stub -version=0x600+ TpReleaseIoCompletion -@ stub -version=0x600+ TpReleasePool -@ stub -version=0x600+ TpReleaseTimer -@ stub -version=0x600+ TpReleaseWait -@ stub -version=0x600+ TpReleaseWork -@ stub -version=0x600+ TpSetPoolMaxThreads -@ stub -version=0x600+ TpSetPoolMinThreads -@ stub -version=0x600+ TpSetTimer -@ stub -version=0x600+ TpSetWait -@ stub -version=0x600+ TpSimpleTryPost -@ stub -version=0x600+ TpStartAsyncIoOperation +@ stdcall -version=0x600+ TpReleaseCleanupGroup(ptr) +@ stdcall -version=0x600+ TpReleaseCleanupGroupMembers(ptr long ptr) +@ stdcall -version=0x600+ TpReleaseIoCompletion(ptr) +@ stdcall -version=0x600+ TpReleasePool(ptr) +@ stdcall -version=0x600+ TpReleaseTimer(ptr) +@ stdcall -version=0x600+ TpReleaseWait(ptr) +@ stdcall -version=0x600+ TpReleaseWork(ptr) +@ stdcall -version=0x600+ TpSetPoolMaxThreads(ptr long) +@ stdcall -version=0x600+ TpSetPoolMinThreads(ptr long) +@ stdcall -version=0x600+ TpSetPoolStackInformation(ptr ptr) +@ stdcall -version=0x600+ TpSetTimer(ptr ptr long long) +@ stdcall -version=0x600+ TpSetWait(ptr long ptr) +@ stdcall -version=0x600+ TpSimpleTryPost(ptr ptr ptr) +@ stdcall -version=0x600+ TpStartAsyncIoOperation(ptr) @ stub -version=0x600+ TpWaitForAlpcCompletion -@ stub -version=0x600+ TpWaitForIoCompletion -@ stub -version=0x600+ TpWaitForTimer -@ stub -version=0x600+ TpWaitForWait -@ stub -version=0x600+ TpWaitForWork +@ stdcall -version=0x600+ TpWaitForIoCompletion(ptr long) +@ stdcall -version=0x600+ TpWaitForTimer(ptr long) +@ stdcall -version=0x600+ TpWaitForWait(ptr long) +@ stdcall -version=0x600+ TpWaitForWork(ptr long) @ stdcall -ret64 VerSetConditionMask(double long long) @ stub -version=0x600+ WerCheckEventEscalation @ stub -version=0x600+ WerReportSQMEvent diff --git a/dll/ntdll/include/ntdllp.h b/dll/ntdll/include/ntdllp.h index bd5cc9ea91c69..c5ea35a276121 100644 --- a/dll/ntdll/include/ntdllp.h +++ b/dll/ntdll/include/ntdllp.h @@ -242,4 +242,9 @@ NTAPI RtlpInitializeKeyedEvent( VOID); +VOID +NTAPI +RtlpInitializeThreadPooling( + VOID); + /* EOF */ diff --git a/dll/ntdll/ldr/ldrinit.c b/dll/ntdll/ldr/ldrinit.c index 24730bb80da12..eba1e6a190e49 100644 --- a/dll/ntdll/ldr/ldrinit.c +++ b/dll/ntdll/ldr/ldrinit.c @@ -2405,10 +2405,10 @@ LdrpInitializeProcess(IN PCONTEXT Context, /* Check whether all static imports were properly loaded and return here */ if (!NT_SUCCESS(ImportStatus)) return ImportStatus; -#if (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) + /* Following two calls are for Vista+ support, required for winesync */ /* Initialize the keyed event for condition variables */ RtlpInitializeKeyedEvent(); -#endif + RtlpInitializeThreadPooling(); /* Initialize TLS */ Status = LdrpInitializeTls(); diff --git a/dll/ntdll/nt_0600/ntdll_vista.spec b/dll/ntdll/nt_0600/ntdll_vista.spec index 23fd88e02c457..d36c6449d8e4b 100644 --- a/dll/ntdll/nt_0600/ntdll_vista.spec +++ b/dll/ntdll/nt_0600/ntdll_vista.spec @@ -13,34 +13,5 @@ @ stdcall RtlRunOnceComplete(ptr long ptr) @ stdcall RtlRunOnceExecuteOnce(ptr ptr ptr ptr) -@ stdcall TpAllocCleanupGroup(ptr) -@ stdcall TpAllocPool(ptr ptr) -@ stdcall TpAllocTimer(ptr ptr ptr ptr) -@ stdcall TpAllocWait(ptr ptr ptr ptr) -@ stdcall TpAllocWork(ptr ptr ptr ptr) -@ stdcall TpCallbackLeaveCriticalSectionOnCompletion(ptr ptr) -@ stdcall TpCallbackMayRunLong(ptr) -@ stdcall TpCallbackReleaseMutexOnCompletion(ptr ptr) -@ stdcall TpCallbackReleaseSemaphoreOnCompletion(ptr ptr long) -@ stdcall TpCallbackSetEventOnCompletion(ptr ptr) -@ stdcall TpCallbackUnloadDllOnCompletion(ptr ptr) -@ stdcall TpDisassociateCallback(ptr) -@ stdcall TpIsTimerSet(ptr) -@ stdcall TpPostWork(ptr) -@ stdcall TpReleaseCleanupGroup(ptr) -@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr) -@ stdcall TpReleasePool(ptr) -@ stdcall TpReleaseTimer(ptr) -@ stdcall TpReleaseWait(ptr) -@ stdcall TpReleaseWork(ptr) -@ stdcall TpSetPoolMaxThreads(ptr long) -@ stdcall TpSetPoolMinThreads(ptr long) -@ stdcall TpSetTimer(ptr ptr long long) -@ stdcall TpSetWait(ptr long ptr) -@ stdcall TpSimpleTryPost(ptr ptr ptr) -@ stdcall TpWaitForTimer(ptr long) -@ stdcall TpWaitForWait(ptr long) -@ stdcall TpWaitForWork(ptr long) - @ stdcall RtlConnectToSm(ptr ptr long ptr) SmConnectToSm @ stdcall RtlSendMsgToSm(ptr ptr) SmSendMsgToSm diff --git a/media/doc/WINESYNC.txt b/media/doc/WINESYNC.txt index 665c8f1688a92..a950498e9313c 100644 --- a/media/doc/WINESYNC.txt +++ b/media/doc/WINESYNC.txt @@ -267,8 +267,8 @@ check Wine current sources first as it may already be fixed. sdk/lib/3rdparty/strmbase # Synced to WineStaging-3.3 -sdk/lib/rtl/actctx.c # Synced to wine-5.18 -sdk/lib/rtl/threadpool.c # Synced with wine-9.7 +sdk/lib/rtl/actctx.c # Partly synced with WineStaging-1.9.16 +sdk/lib/rtl/threadpool.c # Synced with Wine-9.7 advapi32 - dll/win32/advapi32/wine/cred.c # Synced to WineStaging-3.3 diff --git a/modules/rostests/winetests/ntdll/CMakeLists.txt b/modules/rostests/winetests/ntdll/CMakeLists.txt index c5a886481e790..dbcf21160392d 100644 --- a/modules/rostests/winetests/ntdll/CMakeLists.txt +++ b/modules/rostests/winetests/ntdll/CMakeLists.txt @@ -23,6 +23,7 @@ list(APPEND SOURCE rtlstr.c string.c testlist.c + threadpool.c time.c) if(ARCH STREQUAL "i386") diff --git a/modules/rostests/winetests/ntdll/testlist.c b/modules/rostests/winetests/ntdll/testlist.c index 989040c562aab..1c9edc99e8626 100644 --- a/modules/rostests/winetests/ntdll/testlist.c +++ b/modules/rostests/winetests/ntdll/testlist.c @@ -23,6 +23,7 @@ extern void func_rtl(void); extern void func_rtlbitmap(void); extern void func_rtlstr(void); extern void func_string(void); +extern void func_threadpool(void); extern void func_time(void); const struct test winetest_testlist[] = @@ -49,6 +50,7 @@ const struct test winetest_testlist[] = { "rtlbitmap", func_rtlbitmap }, { "rtlstr", func_rtlstr }, { "string", func_string }, + { "threadpool", func_threadpool}, { "time", func_time }, { 0, 0 } }; diff --git a/modules/rostests/winetests/ntdll/threadpool.c b/modules/rostests/winetests/ntdll/threadpool.c index af0b6673d392f..88f4e55fa9758 100644 --- a/modules/rostests/winetests/ntdll/threadpool.c +++ b/modules/rostests/winetests/ntdll/threadpool.c @@ -20,71 +20,95 @@ #include "ntdll_test.h" -static HMODULE hntdll = 0; +#ifdef __REACTOS__ +typedef void (CALLBACK *PTP_IO_CALLBACK)(PTP_CALLBACK_INSTANCE,void*,void*,IO_STATUS_BLOCK*,PTP_IO); +#endif + static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); +static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *); 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 *pTpCancelAsyncIoOperation)(TP_IO *); 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 NTSTATUS (WINAPI *pTpQueryPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *); static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *); static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID); +static void (WINAPI *pTpReleaseIoCompletion)(TP_IO *); static VOID (WINAPI *pTpReleasePool)(TP_POOL *); static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *); +static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *); static VOID (WINAPI *pTpReleaseWork)(TP_WORK *); static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD); +static NTSTATUS (WINAPI *pTpSetPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *); 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 *pTpStartAsyncIoOperation)(TP_IO *); +static void (WINAPI *pTpWaitForIoCompletion)(TP_IO *,BOOL); 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) \ +static void (WINAPI *pCancelThreadpoolIo)(TP_IO *); +static void (WINAPI *pCloseThreadpoolIo)(TP_IO *); +static TP_IO *(WINAPI *pCreateThreadpoolIo)(HANDLE, PTP_WIN32_IO_CALLBACK, void *, TP_CALLBACK_ENVIRON *); +static void (WINAPI *pStartThreadpoolIo)(TP_IO *); +static void (WINAPI *pWaitForThreadpoolIoCallbacks)(TP_IO *, BOOL); + +#define GET_PROC(func) \ do \ { \ - p ## func = (void *)GetProcAddress(hntdll, #func); \ + p ## func = (void *)GetProcAddress(module, #func); \ if (!p ## func) trace("Failed to get address for %s\n", #func); \ } \ while (0) static BOOL init_threadpool(void) { - hntdll = GetModuleHandleA("ntdll"); - if (!hntdll) - { - win_skip("Could not load ntdll\n"); - return FALSE; - } - - 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); - NTDLL_GET_PROC(TpDisassociateCallback); - NTDLL_GET_PROC(TpIsTimerSet); - NTDLL_GET_PROC(TpPostWork); - NTDLL_GET_PROC(TpReleaseCleanupGroup); - 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); + HMODULE module = GetModuleHandleA("ntdll"); + GET_PROC(TpAllocCleanupGroup); + GET_PROC(TpAllocIoCompletion); + GET_PROC(TpAllocPool); + GET_PROC(TpAllocTimer); + GET_PROC(TpAllocWait); + GET_PROC(TpAllocWork); + GET_PROC(TpCallbackMayRunLong); + GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); + GET_PROC(TpCancelAsyncIoOperation); + GET_PROC(TpDisassociateCallback); + GET_PROC(TpIsTimerSet); + GET_PROC(TpPostWork); + GET_PROC(TpQueryPoolStackInformation); + GET_PROC(TpReleaseCleanupGroup); + GET_PROC(TpReleaseCleanupGroupMembers); + GET_PROC(TpReleaseIoCompletion); + GET_PROC(TpReleasePool); + GET_PROC(TpReleaseTimer); + GET_PROC(TpReleaseWait); + GET_PROC(TpReleaseWork); + GET_PROC(TpSetPoolMaxThreads); + GET_PROC(TpSetPoolStackInformation); + GET_PROC(TpSetTimer); + GET_PROC(TpSetWait); + GET_PROC(TpSimpleTryPost); + GET_PROC(TpStartAsyncIoOperation); + GET_PROC(TpWaitForIoCompletion); + GET_PROC(TpWaitForTimer); + GET_PROC(TpWaitForWait); + GET_PROC(TpWaitForWork); + + module = GetModuleHandleA("kernel32"); + GET_PROC(CancelThreadpoolIo); + GET_PROC(CloseThreadpoolIo); + GET_PROC(CreateThreadpoolIo); + GET_PROC(StartThreadpoolIo); + GET_PROC(WaitForThreadpoolIoCallbacks); if (!pTpAllocPool) { @@ -101,7 +125,6 @@ static BOOL init_threadpool(void) static DWORD CALLBACK rtl_work_cb(void *userdata) { HANDLE semaphore = userdata; - trace("Running rtl_work callback\n"); ReleaseSemaphore(semaphore, 1, NULL); return 0; } @@ -113,32 +136,32 @@ static void test_RtlQueueWorkItem(void) DWORD result; semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEDEFAULT); - ok(!status, "RtlQueueWorkItem failed with status %x\n", status); + ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINIOTHREAD); - ok(!status, "RtlQueueWorkItem failed with status %x\n", status); + ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINPERSISTENTTHREAD); - ok(!status, "RtlQueueWorkItem failed with status %x\n", status); + ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTELONGFUNCTION); - ok(!status, "RtlQueueWorkItem failed with status %x\n", status); + ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_TRANSFER_IMPERSONATION); - ok(!status, "RtlQueueWorkItem failed with status %x\n", status); + ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); CloseHandle(semaphore); } @@ -157,8 +180,6 @@ static void CALLBACK rtl_wait_cb(void *userdata, BOOLEAN timeout) struct rtl_wait_info *info = userdata; DWORD result; - trace("Running rtl_wait callback\n"); - if (!timeout) InterlockedIncrement(&info->userdata); else @@ -169,7 +190,7 @@ static void CALLBACK rtl_wait_cb(void *userdata, BOOLEAN timeout) if (info->semaphore2) { result = WaitForSingleObject(info->semaphore2, 200); - ok(result == info->wait_result, "expected %u, got %u\n", info->wait_result, result); + ok(result == info->wait_result, "expected %lu, got %lu\n", info->wait_result, result); ReleaseSemaphore(info->semaphore1, 1, NULL); } } @@ -178,7 +199,6 @@ static HANDLE rtl_wait_apc_semaphore; static void CALLBACK rtl_wait_apc_cb(ULONG_PTR userdata) { - trace("Running rtl_wait_apc callback\n"); if (rtl_wait_apc_semaphore) ReleaseSemaphore(rtl_wait_apc_semaphore, 1, NULL); } @@ -189,7 +209,7 @@ static void test_RtlRegisterWait(void) struct rtl_wait_info info; HANDLE semaphores[2]; NTSTATUS status; - DWORD result; + DWORD result, threadid; semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL); ok(semaphores[0] != NULL, "failed to create semaphore\n"); @@ -205,196 +225,257 @@ static void test_RtlRegisterWait(void) wait1 = NULL; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ok(wait1 != NULL, "expected wait1 != NULL\n"); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); /* infinite timeout, signal the semaphore two times */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\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 == 2, "expected info.userdata = 2, got %u\n", info.userdata); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); /* repeat test with WT_EXECUTEONLYONCE */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); 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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); /* finite timeout, no event */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); /* finite timeout, with event */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); + result = WaitForSingleObject(semaphores[0], 100); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); + 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + Sleep(50); + status = RtlDeregisterWait(wait1); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); + + /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag */ + info.userdata = 0; + info.threadid = 0; + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ok(info.threadid && info.threadid != GetCurrentThreadId(), "unexpected wait thread id %lx\n", info.threadid); + threadid = info.threadid; + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + Sleep(50); + status = RtlDeregisterWait(wait1); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); + + info.userdata = 0; + info.threadid = 0; + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + Sleep(50); + status = RtlDeregisterWait(wait1); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); + + /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with 0 timeout */ + info.userdata = 0; + info.threadid = 0; + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); + ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + Sleep(50); + status = RtlDeregisterWait(wait1); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); + + /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with already signaled event */ + info.userdata = 0; + info.threadid = 0; + ReleaseSemaphore(semaphores[1], 1, NULL); + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); + result = WaitForSingleObject(semaphores[0], 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); /* test for IO threads */ info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINIOTHREAD); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); - ok(info.threadid != 0, "expected info.threadid != 0, got %u\n", info.threadid); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ok(info.threadid != 0, "expected info.threadid != 0, got %lu\n", info.threadid); thread = OpenThread(THREAD_SET_CONTEXT, FALSE, info.threadid); - ok(thread != NULL, "OpenThread failed with %u\n", GetLastError()); + ok(thread != NULL, "OpenThread failed with %lu\n", GetLastError()); rtl_wait_apc_semaphore = semaphores[0]; result = QueueUserAPC(rtl_wait_apc_cb, thread, 0); - ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError()); + ok(result != 0, "QueueUserAPC failed with %lu\n", GetLastError()); result = WaitForSingleObject(semaphores[0], 200); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); rtl_wait_apc_semaphore = 0; CloseHandle(thread); ReleaseSemaphore(semaphores[1], 1, NULL); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); - ok(info.threadid != 0, "expected info.threadid != 0, got %u\n", info.threadid); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ok(info.threadid != 0, "expected info.threadid != 0, got %lu\n", info.threadid); thread = OpenThread(THREAD_SET_CONTEXT, FALSE, info.threadid); - ok(thread != NULL, "OpenThread failed with %u\n", GetLastError()); + ok(thread != NULL, "OpenThread failed with %lu\n", GetLastError()); rtl_wait_apc_semaphore = semaphores[0]; result = QueueUserAPC(rtl_wait_apc_cb, thread, 0); - ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError()); + ok(result != 0, "QueueUserAPC failed with %lu\n", GetLastError()); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_TIMEOUT || broken(result == WAIT_OBJECT_0) /* >= Win Vista */, - "WaitForSingleObject returned %u\n", result); + "WaitForSingleObject returned %lu\n", result); rtl_wait_apc_semaphore = 0; CloseHandle(thread); ReleaseSemaphore(semaphores[1], 1, NULL); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWait(wait1); - ok(!status, "RtlDeregisterWait failed with status %x\n", status); + ok(!status, "RtlDeregisterWait failed with status %lx\n", status); /* test RtlDeregisterWaitEx before wait expired */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, NULL); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, event); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(event, 200); - todo_wine - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* test RtlDeregisterWaitEx after wait expired */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, NULL); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, event); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); - ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(event, 200); - todo_wine - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* test RtlDeregisterWaitEx while callback is running */ info.semaphore2 = semaphores[1]; @@ -402,58 +483,84 @@ static void test_RtlRegisterWait(void) info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWait(wait1); - ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %x\n", status); + ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + + info.userdata = 0; + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 1000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + status = RtlDeregisterWait(wait1); + ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 1000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, NULL); - ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %x\n", status); + ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); info.wait_result = WAIT_TIMEOUT; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphores[0], 1000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); + result = WaitForSingleObject(semaphores[0], 0); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + + info.wait_result = WAIT_TIMEOUT; + info.userdata = 0; + status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); - ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status); + ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); info.wait_result = WAIT_OBJECT_0; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); - ok(!status, "RtlRegisterWait failed with status %x\n", status); + ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, event); - ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %x\n", status); + ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(event, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); CloseHandle(semaphores[0]); CloseHandle(semaphores[1]); @@ -463,21 +570,23 @@ static void test_RtlRegisterWait(void) static void CALLBACK simple_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) { HANDLE semaphore = userdata; - trace("Running simple callback\n"); ReleaseSemaphore(semaphore, 1, NULL); } static void CALLBACK simple2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) { - trace("Running simple2 callback\n"); Sleep(50); InterlockedIncrement((LONG *)userdata); } static void test_tp_simple(void) { + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); + TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; +#ifndef __REACTOS__ TP_CALLBACK_ENVIRON_V3 environment3; +#endif TP_CLEANUP_GROUP *group; HANDLE semaphore; NTSTATUS status; @@ -487,21 +596,21 @@ static void test_tp_simple(void) int i; semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* post the callback using the default threadpool */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = NULL; status = pTpSimpleTryPost(simple_cb, semaphore, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* post the callback using the new threadpool */ @@ -509,20 +618,31 @@ static void test_tp_simple(void) environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(simple_cb, semaphore, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); +#ifndef __REACTOS__ // Windows 7 /* test with environment version 3 */ memset(&environment3, 0, sizeof(environment3)); environment3.Version = 3; environment3.Pool = pool; - environment3.CallbackPriority = TP_CALLBACK_PRIORITY_NORMAL; environment3.Size = sizeof(environment3); + + for (i = 0; i < 3; ++i) + { + environment3.CallbackPriority = TP_CALLBACK_PRIORITY_HIGH + i; + status = pTpSimpleTryPost(simple_cb, semaphore, (TP_CALLBACK_ENVIRON *)&environment3); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); + result = WaitForSingleObject(semaphore, 1000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + } + + environment3.CallbackPriority = 10; status = pTpSimpleTryPost(simple_cb, semaphore, (TP_CALLBACK_ENVIRON *)&environment3); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); - result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(status == STATUS_INVALID_PARAMETER || broken(!status) /* Vista does not support priorities */, + "TpSimpleTryPost failed with status %lx\n", status); +#endif /* test with invalid version number */ memset(&environment, 0, sizeof(environment)); @@ -531,17 +651,17 @@ static void test_tp_simple(void) status = pTpSimpleTryPost(simple_cb, semaphore, &environment); todo_wine ok(status == STATUS_INVALID_PARAMETER || broken(!status) /* Vista/2008 */, - "TpSimpleTryPost unexpectedly returned status %x\n", status); + "TpSimpleTryPost unexpectedly returned status %lx\n", status); if (!status) { result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } /* allocate a cleanup group for synchronization */ group = NULL; status = pTpAllocCleanupGroup(&group); - ok(!status, "TpAllocCleanupGroup failed with status %x\n", status); + ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n"); /* use cleanup group to wait for a simple callback */ @@ -551,9 +671,9 @@ static void test_tp_simple(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpSimpleTryPost(simple2_cb, &userdata, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); - ok(userdata == 1, "expected userdata = 1, got %u\n", userdata); + ok(userdata == 1, "expected userdata = 1, got %lu\n", userdata); /* test cancellation of pending simple callbacks */ userdata = 0; @@ -565,10 +685,26 @@ static void test_tp_simple(void) for (i = 0; i < 100; i++) { status = pTpSimpleTryPost(simple2_cb, &userdata, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); } pTpReleaseCleanupGroupMembers(group, TRUE, NULL); - ok(userdata < 100, "expected userdata < 100, got %u\n", userdata); + ok(userdata < 100, "expected userdata < 100, got %lu\n", userdata); + + /* test querying and setting the stack size */ + status = pTpQueryPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); + ok(stack_info.StackReserve == nt->OptionalHeader.SizeOfStackReserve, "expected default StackReserve, got %Ix\n", stack_info.StackReserve); + ok(stack_info.StackCommit == nt->OptionalHeader.SizeOfStackCommit, "expected default StackCommit, got %Ix\n", stack_info.StackCommit); + + /* threadpool does not validate the stack size values */ + stack_info.StackReserve = stack_info.StackCommit = 1; + status = pTpSetPoolStackInformation(pool, &stack_info); + ok(!status, "TpSetPoolStackInformation failed: %lx\n", status); + + status = pTpQueryPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); + ok(stack_info.StackReserve == 1, "expected 1 byte StackReserve, got %ld\n", (ULONG)stack_info.StackReserve); + ok(stack_info.StackCommit == 1, "expected 1 byte StackCommit, got %ld\n", (ULONG)stack_info.StackCommit); /* cleanup */ pTpReleaseCleanupGroup(group); @@ -578,14 +714,12 @@ static void test_tp_simple(void) static void CALLBACK work_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work) { - trace("Running work callback\n"); Sleep(100); InterlockedIncrement((LONG *)userdata); } static void CALLBACK work2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work) { - trace("Running work2 callback\n"); Sleep(100); InterlockedExchangeAdd((LONG *)userdata, 0x10000); } @@ -602,7 +736,7 @@ static void test_tp_work(void) /* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1); @@ -612,7 +746,7 @@ static void test_tp_work(void) environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, work_cb, &userdata, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); /* post 5 identical work items at once */ @@ -620,14 +754,14 @@ static void test_tp_work(void) for (i = 0; i < 5; i++) pTpPostWork(work); pTpWaitForWork(work, FALSE); - ok(userdata == 5, "expected userdata = 5, got %u\n", userdata); + ok(userdata == 5, "expected userdata = 5, got %lu\n", userdata); /* add more tasks and cancel them immediately */ userdata = 0; for (i = 0; i < 10; i++) pTpPostWork(work); pTpWaitForWork(work, TRUE); - ok(userdata < 10, "expected userdata < 10, got %u\n", userdata); + ok(userdata < 10, "expected userdata < 10, got %lu\n", userdata); /* cleanup */ pTpReleaseWork(work); @@ -647,14 +781,14 @@ static void test_tp_work_scheduler(void) /* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1); /* create a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); - ok(!status, "TpAllocCleanupGroup failed with status %x\n", status); + ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n"); /* the first work item has no cleanup group associated */ @@ -663,7 +797,7 @@ static void test_tp_work_scheduler(void) environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, work_cb, &userdata, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); /* allocate a second work item with a cleanup group */ @@ -673,7 +807,7 @@ static void test_tp_work_scheduler(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work2, work2_cb, &userdata, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work2 != NULL, "expected work2 != NULL\n"); /* the 'work' callbacks are not blocking execution of 'work2' callbacks */ @@ -685,8 +819,8 @@ static void test_tp_work_scheduler(void) Sleep(500); pTpWaitForWork(work, TRUE); pTpWaitForWork(work2, TRUE); - ok(userdata & 0xffff, "expected userdata & 0xffff != 0, got %u\n", userdata & 0xffff); - ok(userdata >> 16, "expected userdata >> 16 != 0, got %u\n", userdata >> 16); + ok(userdata & 0xffff, "expected userdata & 0xffff != 0, got %lu\n", userdata & 0xffff); + ok(userdata >> 16, "expected userdata >> 16 != 0, got %lu\n", userdata >> 16); /* test TpReleaseCleanupGroupMembers on a work item */ userdata = 0; @@ -696,8 +830,8 @@ static void test_tp_work_scheduler(void) pTpPostWork(work2); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); pTpWaitForWork(work, TRUE); - ok((userdata & 0xffff) < 10, "expected userdata & 0xffff < 10, got %u\n", userdata & 0xffff); - ok((userdata >> 16) == 3, "expected userdata >> 16 == 3, got %u\n", userdata >> 16); + ok((userdata & 0xffff) < 10, "expected userdata & 0xffff < 10, got %lu\n", userdata & 0xffff); + ok((userdata >> 16) == 3, "expected userdata >> 16 == 3, got %lu\n", userdata >> 16); /* cleanup */ pTpReleaseWork(work); @@ -708,7 +842,6 @@ static void test_tp_work_scheduler(void) static void CALLBACK simple_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) { HANDLE *semaphores = userdata; - trace("Running simple release callback\n"); ReleaseSemaphore(semaphores, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ } @@ -716,7 +849,6 @@ static void CALLBACK simple_release_cb(TP_CALLBACK_INSTANCE *instance, void *use static void CALLBACK work_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work) { HANDLE semaphore = userdata; - trace("Running work release callback\n"); ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseWork(work); @@ -725,7 +857,6 @@ static void CALLBACK work_release_cb(TP_CALLBACK_INSTANCE *instance, void *userd static void CALLBACK timer_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer) { HANDLE semaphore = userdata; - trace("Running timer release callback\n"); ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseTimer(timer); @@ -735,7 +866,6 @@ static void CALLBACK wait_release_cb(TP_CALLBACK_INSTANCE *instance, void *userd TP_WAIT *wait, TP_WAIT_RESULT result) { HANDLE semaphore = userdata; - trace("Running wait release callback\n"); ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseWait(wait); @@ -755,18 +885,18 @@ static void test_tp_group_wait(void) DWORD result; semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* allocate a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); - ok(!status, "TpAllocCleanupGroup failed with status %x\n", status); + ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n"); /* release work object during TpReleaseCleanupGroupMembers */ @@ -776,11 +906,11 @@ static void test_tp_group_wait(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, work_release_cb, semaphore, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); /* release timer object during TpReleaseCleanupGroupMembers */ @@ -790,12 +920,12 @@ static void test_tp_group_wait(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocTimer(&timer, timer_release_cb, semaphore, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n"); when.QuadPart = 0; pTpSetTimer(timer, &when, 0, 0); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); /* release wait object during TpReleaseCleanupGroupMembers */ @@ -805,12 +935,12 @@ static void test_tp_group_wait(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWait(&wait, wait_release_cb, semaphore, &environment); - ok(!status, "TpAllocWait failed with status %x\n", status); + ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait != NULL, "expected wait != NULL\n"); when.QuadPart = 0; pTpSetWait(wait, INVALID_HANDLE_VALUE, &when); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); /* cleanup */ @@ -828,17 +958,15 @@ static void CALLBACK simple_group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void DWORD result; int i; - trace("Running simple group cancel callback\n"); - status = pTpCallbackMayRunLong(instance); ok(status == STATUS_TOO_MANY_THREADS || broken(status == 1) /* Win Vista / 2008 */, - "expected STATUS_TOO_MANY_THREADS, got %08x\n", status); + "expected STATUS_TOO_MANY_THREADS, got %08lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); for (i = 0; i < 4; i++) { result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } ReleaseSemaphore(semaphores[1], 1, NULL); } @@ -848,17 +976,14 @@ static void CALLBACK work_group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void * HANDLE *semaphores = userdata; DWORD result; - trace("Running work group cancel callback\n"); - ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 200); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); } static void CALLBACK group_cancel_cleanup_release_cb(void *object, void *userdata) { HANDLE *semaphores = userdata; - trace("Running group cancel cleanup release callback\n"); group_cancel_tid = GetCurrentThreadId(); ok(object == (void *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", object); ReleaseSemaphore(semaphores[0], 1, NULL); @@ -867,7 +992,6 @@ static void CALLBACK group_cancel_cleanup_release_cb(void *object, void *userdat static void CALLBACK group_cancel_cleanup_release2_cb(void *object, void *userdata) { HANDLE *semaphores = userdata; - trace("Running group cancel cleanup release2 callback\n"); group_cancel_tid = GetCurrentThreadId(); ok(object == userdata, "expected %p, got %p\n", userdata, object); ReleaseSemaphore(semaphores[0], 1, NULL); @@ -875,7 +999,6 @@ static void CALLBACK group_cancel_cleanup_release2_cb(void *object, void *userda static void CALLBACK group_cancel_cleanup_increment_cb(void *object, void *userdata) { - trace("Running group cancel cleanup increment callback\n"); group_cancel_tid = GetCurrentThreadId(); InterlockedIncrement((LONG *)userdata); } @@ -921,21 +1044,21 @@ static void test_tp_group_cancel(void) int i; semaphores[0] = CreateSemaphoreA(NULL, 0, 4, NULL); - ok(semaphores[0] != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphores[0] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL); - ok(semaphores[1] != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphores[1] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1); /* allocate a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); - ok(!status, "TpAllocCleanupGroup failed with status %x\n", status); + ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n"); /* test execution of cancellation callback */ @@ -943,9 +1066,9 @@ static void test_tp_group_cancel(void) environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(simple_group_cancel_cb, semaphores, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); memset(&environment, 0, sizeof(environment)); environment.Version = 1; @@ -953,28 +1076,28 @@ static void test_tp_group_cancel(void) environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_release_cb; status = pTpSimpleTryPost(unexpected_simple_cb, (void *)0xdeadbeef, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); work = NULL; status = pTpAllocWork(&work, unexpected_work_cb, (void *)0xdeadbeef, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); timer = NULL; status = pTpAllocTimer(&timer, unexpected_timer_cb, (void *)0xdeadbeef, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n"); wait = NULL; status = pTpAllocWait(&wait, unexpected_wait_cb, (void *)0xdeadbeef, &environment); - ok(!status, "TpAllocWait failed with status %x\n", status); + ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait != NULL, "expected wait != NULL\n"); group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, semaphores); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n", + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid); /* test if cancellation callbacks are executed before or after wait */ @@ -985,19 +1108,19 @@ static void test_tp_group_cancel(void) environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_release2_cb; status = pTpAllocWork(&work, work_group_cancel_cb, semaphores, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); pTpPostWork(work); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, semaphores); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n", + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid); /* group cancel callback is not executed if object is destroyed while waiting */ @@ -1008,12 +1131,12 @@ static void test_tp_group_cancel(void) environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = unexpected_group_cancel_cleanup_cb; status = pTpAllocWork(&work, work_release_cb, semaphores[1], &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, TRUE, NULL); /* terminated simple callbacks should not trigger the group cancel callback */ @@ -1023,9 +1146,9 @@ static void test_tp_group_cancel(void) environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = unexpected_group_cancel_cleanup_cb; status = pTpSimpleTryPost(simple_release_cb, semaphores[1], &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, TRUE, semaphores); /* test cancellation callback for objects with multiple instances */ @@ -1036,7 +1159,7 @@ static void test_tp_group_cancel(void) environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_increment_cb; status = pTpAllocWork(&work, work_cb, &userdata, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); /* post 10 identical work items at once */ @@ -1047,9 +1170,9 @@ static void test_tp_group_cancel(void) /* check if we get multiple cancellation callbacks */ group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, &userdata2); - ok(userdata <= 5, "expected userdata <= 5, got %u\n", userdata); - ok(userdata2 == 1, "expected only one cancellation callback, got %u\n", userdata2); - ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n", + ok(userdata <= 5, "expected userdata <= 5, got %lu\n", userdata); + ok(userdata2 == 1, "expected only one cancellation callback, got %lu\n", userdata2); + ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid); /* cleanup */ @@ -1062,19 +1185,12 @@ static void test_tp_group_cancel(void) static void CALLBACK instance_semaphore_completion_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) { HANDLE *semaphores = userdata; - trace("Running instance completion callback\n"); pTpCallbackReleaseSemaphoreOnCompletion(instance, semaphores[0], 1); } static void CALLBACK instance_finalization_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) { HANDLE *semaphores = userdata; - DWORD result; - - trace("Running instance finalization callback\n"); - - result = WaitForSingleObject(semaphores[0], 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); ReleaseSemaphore(semaphores[1], 1, NULL); } @@ -1094,7 +1210,7 @@ static void test_tp_instance(void) /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* test for TpCallbackReleaseSemaphoreOnCompletion */ @@ -1102,9 +1218,9 @@ static void test_tp_instance(void) environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* test for finalization callback */ memset(&environment, 0, sizeof(environment)); @@ -1112,11 +1228,11 @@ static void test_tp_instance(void) environment.Pool = pool; environment.FinalizationCallback = instance_finalization_cb; status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* cleanup */ pTpReleasePool(pool); @@ -1129,11 +1245,9 @@ static void CALLBACK disassociate_cb(TP_CALLBACK_INSTANCE *instance, void *userd HANDLE *semaphores = userdata; DWORD result; - trace("Running disassociate callback\n"); - pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL); } @@ -1142,11 +1256,9 @@ static void CALLBACK disassociate2_cb(TP_CALLBACK_INSTANCE *instance, void *user HANDLE *semaphores = userdata; DWORD result; - trace("Running disassociate2 callback\n"); - pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL); } @@ -1155,11 +1267,9 @@ static void CALLBACK disassociate3_cb(TP_CALLBACK_INSTANCE *instance, void *user HANDLE *semaphores = userdata; DWORD result; - trace("Running disassociate3 callback\n"); - pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL); } @@ -1181,12 +1291,12 @@ static void test_tp_disassociate(void) /* allocate new threadpool and cleanup group */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); group = NULL; status = pTpAllocCleanupGroup(&group); - ok(!status, "TpAllocCleanupGroup failed with status %x\n", status); + ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n"); /* test TpDisassociateCallback on work objects without group */ @@ -1195,17 +1305,17 @@ static void test_tp_disassociate(void) environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); pTpWaitForWork(work, FALSE); result = WaitForSingleObject(semaphores[1], 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseWork(work); /* test TpDisassociateCallback on work objects with group (1) */ @@ -1215,17 +1325,17 @@ static void test_tp_disassociate(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); pTpWaitForWork(work, FALSE); result = WaitForSingleObject(semaphores[1], 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); /* test TpDisassociateCallback on work objects with group (2) */ @@ -1235,7 +1345,7 @@ static void test_tp_disassociate(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, disassociate2_cb, semaphores, &environment); - ok(!status, "TpAllocWork failed with status %x\n", status); + ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); @@ -1243,9 +1353,9 @@ static void test_tp_disassociate(void) ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* test TpDisassociateCallback on simple callbacks */ memset(&environment, 0, sizeof(environment)); @@ -1253,15 +1363,15 @@ static void test_tp_disassociate(void) environment.Pool = pool; environment.CleanupGroup = group; status = pTpSimpleTryPost(disassociate3_cb, semaphores, &environment); - ok(!status, "TpSimpleTryPost failed with status %x\n", status); + ok(!status, "TpSimpleTryPost failed with status %lx\n", status); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); /* cleanup */ pTpReleaseCleanupGroup(group); @@ -1273,7 +1383,6 @@ static void test_tp_disassociate(void) static void CALLBACK timer_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer) { HANDLE semaphore = userdata; - trace("Running timer callback\n"); ReleaseSemaphore(semaphore, 1, NULL); } @@ -1290,12 +1399,12 @@ static void test_tp_timer(void) int i; semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* allocate new timer */ @@ -1304,7 +1413,7 @@ static void test_tp_timer(void) environment.Version = 1; environment.Pool = pool; status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n"); success = pTpIsTimerSet(timer); @@ -1319,9 +1428,9 @@ static void test_tp_timer(void) pTpWaitForTimer(timer, FALSE); result = WaitForSingleObject(semaphore, 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 200); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n"); @@ -1335,9 +1444,9 @@ static void test_tp_timer(void) pTpWaitForTimer(timer, FALSE); result = WaitForSingleObject(semaphore, 100); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 200); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n"); @@ -1350,7 +1459,7 @@ static void test_tp_timer(void) pTpWaitForTimer(timer, FALSE); result = WaitForSingleObject(semaphore, 50); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n"); @@ -1364,7 +1473,7 @@ static void test_tp_timer(void) CloseHandle(semaphore); semaphore = CreateSemaphoreA(NULL, 0, 3, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* allocate a new timer */ timer = NULL; @@ -1372,7 +1481,7 @@ static void test_tp_timer(void) environment.Version = 1; environment.Pool = pool; status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n"); /* test a relative timeout repeated periodically */ @@ -1386,11 +1495,11 @@ static void test_tp_timer(void) for (i = 0; i < 3; i++) { result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } ticks = GetTickCount() - ticks; ok(ticks >= 500 && (ticks <= 700 || broken(ticks <= 750)) /* Win 7 */, - "expected approximately 600 ticks, got %u\n", ticks); + "expected approximately 600 ticks, got %lu\n", ticks); /* unset the timer */ pTpSetTimer(timer, NULL, 0, 0); @@ -1413,7 +1522,6 @@ struct window_length_info static void CALLBACK window_length_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer) { struct window_length_info *info = userdata; - trace("Running window length callback\n"); info->ticks = GetTickCount(); ReleaseSemaphore(info->semaphore, 1, NULL); } @@ -1431,12 +1539,12 @@ static void test_tp_window_length(void) BOOL merged; semaphore = CreateSemaphoreA(NULL, 0, 2, NULL); - ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError()); + ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* allocate two identical timers */ @@ -1447,13 +1555,13 @@ static void test_tp_window_length(void) timer1 = NULL; info1.semaphore = semaphore; status = pTpAllocTimer(&timer1, window_length_cb, &info1, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer1 != NULL, "expected timer1 != NULL\n"); timer2 = NULL; info2.semaphore = semaphore; status = pTpAllocTimer(&timer2, window_length_cb, &info2, &environment); - ok(!status, "TpAllocTimer failed with status %x\n", status); + ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer2 != NULL, "expected timer2 != NULL\n"); /* choose parameters so that timers are not merged */ @@ -1468,9 +1576,9 @@ static void test_tp_window_length(void) pTpSetTimer(timer1, &when, 0, 75); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); ok(info2.ticks >= info1.ticks + 75 || broken(info2.ticks < info1.ticks + 75) /* Win 2008 */, "expected that timers are not merged\n"); @@ -1487,9 +1595,9 @@ static void test_tp_window_length(void) pTpSetTimer(timer1, &when, 0, 200); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); merged = info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50; ok(merged || broken(!merged) /* Win 10 */, "expected that timers are merged\n"); @@ -1506,9 +1614,9 @@ static void test_tp_window_length(void) pTpSetTimer(timer2, &when, 0, 0); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); merged = info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50; todo_wine @@ -1531,14 +1639,12 @@ 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); + ok(0, "unexpected result %lu\n", result); ReleaseSemaphore(info->semaphore, 1, NULL); } @@ -1562,7 +1668,7 @@ static void test_tp_wait(void) /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* allocate new wait items */ @@ -1572,12 +1678,12 @@ static void test_tp_wait(void) wait1 = NULL; status = pTpAllocWait(&wait1, wait_cb, &info, &environment); - ok(!status, "TpAllocWait failed with status %x\n", status); + ok(!status, "TpAllocWait failed with status %lx\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(!status, "TpAllocWait failed with status %lx\n", status); ok(wait2 != NULL, "expected wait2 != NULL\n"); /* infinite timeout, signal the semaphore immediately */ @@ -1585,79 +1691,79 @@ static void test_tp_wait(void) 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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); pTpWaitForWait(wait1, TRUE); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */, - "WaitForSingleObject returned %u\n", result); + "WaitForSingleObject returned %lu\n", result); if (result == WAIT_OBJECT_0) - ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); else - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); pTpWaitForWait(wait1, TRUE); - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + "WaitForSingleObject returned %lu\n", result); if (result == WAIT_OBJECT_0) { - ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); } else { - ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } /* absolute timeout, no event */ @@ -1666,13 +1772,13 @@ static void test_tp_wait(void) 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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); /* absolute timeout, with event */ info.userdata = 0; @@ -1680,63 +1786,63 @@ static void test_tp_wait(void) 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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); CloseHandle(semaphores[1]); semaphores[1] = CreateSemaphoreW(NULL, 0, 2, NULL); @@ -1749,12 +1855,12 @@ static void test_tp_wait(void) Sleep(50); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); /* repeat test above with release count 2 */ info.userdata = 0; @@ -1763,12 +1869,12 @@ static void test_tp_wait(void) Sleep(50); result = ReleaseSemaphore(semaphores[1], 2, NULL); result = WaitForSingleObject(semaphores[0], 100); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\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); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); - ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); /* cleanup */ pTpReleaseWait(wait1); @@ -1793,12 +1899,13 @@ static void CALLBACK multi_wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdat else if (result == WAIT_TIMEOUT) multi_wait_info.result = 0x10000 | index; else - ok(0, "unexpected result %u\n", result); + ok(0, "unexpected result %lu\n", result); ReleaseSemaphore(multi_wait_info.semaphore, 1, NULL); } static void test_tp_multi_wait(void) { + TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; HANDLE semaphores[512]; TP_WAIT *waits[512]; @@ -1816,78 +1923,83 @@ static void test_tp_multi_wait(void) /* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); - ok(!status, "TpAllocPool failed with status %x\n", status); + ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); + /* many threads -> use the smallest stack possible */ + stack_info.StackReserve = 256 * 1024; + stack_info.StackCommit = 4 * 1024; + status = pTpSetPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; /* create semaphores and corresponding wait objects */ - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { semaphores[i] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[i] != NULL, "failed to create semaphore %i\n", i); waits[i] = NULL; status = pTpAllocWait(&waits[i], multi_wait_cb, (void *)(DWORD_PTR)i, &environment); - ok(!status, "TpAllocWait failed with status %x\n", status); + ok(!status, "TpAllocWait failed with status %lx\n", status); ok(waits[i] != NULL, "expected waits[%d] != NULL\n", i); pTpSetWait(waits[i], semaphores[i], NULL); } /* release all semaphores and wait for callback */ - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { multi_wait_info.result = 0; ReleaseSemaphore(semaphores[i], 1, NULL); - result = WaitForSingleObject(semaphore, 100); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(multi_wait_info.result == i, "expected result %d, got %u\n", i, multi_wait_info.result); + result = WaitForSingleObject(semaphore, 2000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(multi_wait_info.result == i, "expected result %d, got %lu\n", i, multi_wait_info.result); pTpSetWait(waits[i], semaphores[i], NULL); } /* repeat the same test in reverse order */ - for (i = sizeof(semaphores)/sizeof(semaphores[0]) - 1; i >= 0; i--) + for (i = ARRAY_SIZE(semaphores) - 1; i >= 0; i--) { multi_wait_info.result = 0; ReleaseSemaphore(semaphores[i], 1, NULL); - result = WaitForSingleObject(semaphore, 100); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); - ok(multi_wait_info.result == i, "expected result %d, got %u\n", i, multi_wait_info.result); + result = WaitForSingleObject(semaphore, 2000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); + ok(multi_wait_info.result == i, "expected result %d, got %lu\n", i, multi_wait_info.result); pTpSetWait(waits[i], semaphores[i], NULL); } /* test timeout of wait objects */ multi_wait_info.result = 0; - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { when.QuadPart = (ULONGLONG)50 * -10000; pTpSetWait(waits[i], semaphores[i], &when); } - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { - result = WaitForSingleObject(semaphore, 150); - ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); + result = WaitForSingleObject(semaphore, 2000); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } ok(multi_wait_info.result >> 16, "expected multi_wait_info.result >> 16 != 0\n"); /* destroy the wait objects and semaphores while waiting */ - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { pTpSetWait(waits[i], semaphores[i], NULL); } Sleep(50); - for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++) + for (i = 0; i < ARRAY_SIZE(semaphores); i++) { pTpReleaseWait(waits[i]); NtClose(semaphores[i]); @@ -1897,6 +2009,393 @@ static void test_tp_multi_wait(void) CloseHandle(semaphore); } +struct io_cb_ctx +{ + unsigned int count; + void *ovl; + NTSTATUS ret; + ULONG_PTR length; + TP_IO *io; +}; + +static void CALLBACK io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, + void *cvalue, IO_STATUS_BLOCK *iosb, TP_IO *io) +{ + struct io_cb_ctx *ctx = userdata; + ++ctx->count; + ctx->ovl = cvalue; + ctx->ret = iosb->Status; + ctx->length = iosb->Information; + ctx->io = io; +} + +static DWORD WINAPI io_wait_thread(void *arg) +{ + TP_IO *io = arg; + pTpWaitForIoCompletion(io, FALSE); + return 0; +} + +static void test_tp_io(void) +{ + TP_CALLBACK_ENVIRON environment = {.Version = 1}; +#ifdef __REACTOS__ + OVERLAPPED ovl = {0}, ovl2 = {0}; +#else + OVERLAPPED ovl = {}, ovl2 = {}; +#endif + HANDLE client, server, thread; + struct io_cb_ctx userdata; + char in[1], in2[1]; + const char out[1]; + NTSTATUS status; + DWORD ret_size; + TP_POOL *pool; + TP_IO *io; + BOOL ret; + + ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + status = pTpAllocPool(&pool, NULL); + ok(!status, "failed to allocate pool, status %#lx\n", status); + + server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); + client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError()); + + environment.Pool = pool; + io = NULL; + status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); + ok(!status, "got %#lx\n", status); + ok(!!io, "expected non-NULL TP_IO\n"); + + pTpWaitForIoCompletion(io, FALSE); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n"); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); + CloseHandle(thread); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 2, "callback ran %u times\n", userdata.count); + ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + /* The documentation is a bit unclear about passing TRUE to + * WaitForThreadpoolIoCallbacks()—"pending I/O requests are not canceled" + * [as with CancelIoEx()], but pending threadpool callbacks are, even those + * which have not yet reached the completion port [as with + * TpCancelAsyncIoOperation()]. */ + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + pTpWaitForIoCompletion(io, TRUE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + pTpStartAsyncIoOperation(io); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(ret, "wrong ret %d\n", ret); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, NULL, 1, NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_NOACCESS, "wrong error %lu\n", GetLastError()); + + pTpCancelAsyncIoOperation(io); + pTpWaitForIoCompletion(io, FALSE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + ret = CancelIo(server); + ok(ret, "CancelIo() failed, error %lu\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_CANCELLED, "got status %#lx\n", userdata.ret); + ok(!userdata.length, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + pTpCancelAsyncIoOperation(io); + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + if (0) + { + /* Add a sleep to check that callback is not called later. Commented out to + * save the test time. */ + Sleep(200); + } + ok(userdata.count == 0, "callback ran %u times\n", userdata.count); + + pTpReleaseIoCompletion(io); + CloseHandle(server); + + /* Test TPIO object destruction. */ + server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); + io = NULL; + status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); + ok(!status, "got %#lx\n", status); + + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(ret, "Got unexpected ret %#x.\n", ret); + pTpReleaseIoCompletion(io); + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(!ret, "Got unexpected ret %#x.\n", ret); + CloseHandle(server); + CloseHandle(client); + + server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); + client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError()); + + io = NULL; + status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); + ok(!status, "got %#lx\n", status); + pTpStartAsyncIoOperation(io); + pTpWaitForIoCompletion(io, TRUE); + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(ret, "Got unexpected ret %#x.\n", ret); + pTpReleaseIoCompletion(io); + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(ret, "Got unexpected ret %#x.\n", ret); + + if (0) + { + /* Object destruction will wait until one completion arrives (which was started but not cancelled). + * Commented out to save test time. */ + Sleep(1000); + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(ret, "Got unexpected ret %#x.\n", ret); + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + Sleep(2000); + ret = HeapValidate(GetProcessHeap(), 0, io); + ok(!ret, "Got unexpected ret %#x.\n", ret); + } + + CloseHandle(server); + CloseHandle(ovl.hEvent); + CloseHandle(client); + pTpReleasePool(pool); +} + +static void CALLBACK kernel32_io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, + void *ovl, ULONG ret, ULONG_PTR length, TP_IO *io) +{ + struct io_cb_ctx *ctx = userdata; + ++ctx->count; + ctx->ovl = ovl; + ctx->ret = ret; + ctx->length = length; + ctx->io = io; +} + +static void test_kernel32_tp_io(void) +{ + TP_CALLBACK_ENVIRON environment = {.Version = 1}; +#ifdef __REACTOS__ + OVERLAPPED ovl = {0}, ovl2 = {0}; +#else + OVERLAPPED ovl = {}, ovl2 = {}; +#endif + HANDLE client, server, thread; + struct io_cb_ctx userdata; + char in[1], in2[1]; + const char out[1]; + NTSTATUS status; + DWORD ret_size; + TP_POOL *pool; + TP_IO *io; + BOOL ret; + + ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + status = pTpAllocPool(&pool, NULL); + ok(!status, "failed to allocate pool, status %#lx\n", status); + + server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); + client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError()); + + environment.Pool = pool; + io = NULL; + io = pCreateThreadpoolIo(server, kernel32_io_cb, &userdata, &environment); + ok(!!io, "expected non-NULL TP_IO\n"); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + + userdata.count = 0; + pStartThreadpoolIo(io); + + thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n"); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); + CloseHandle(thread); + + userdata.count = 0; + pStartThreadpoolIo(io); + pStartThreadpoolIo(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 2, "callback ran %u times\n", userdata.count); + ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pStartThreadpoolIo(io); + pWaitForThreadpoolIoCallbacks(io, TRUE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + pStartThreadpoolIo(io); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(ret, "wrong ret %d\n", ret); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_SUCCESS, "got status %#lx\n", userdata.ret); + ok(userdata.length == 1, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pStartThreadpoolIo(io); + + ret = ReadFile(server, NULL, 1, NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_NOACCESS, "wrong error %lu\n", GetLastError()); + + pCancelThreadpoolIo(io); + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + userdata.count = 0; + pStartThreadpoolIo(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); + ret = CancelIo(server); + ok(ret, "CancelIo() failed, error %lu\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_OPERATION_ABORTED, "got status %#lx\n", userdata.ret); + ok(!userdata.length, "got length %Iu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + CloseHandle(ovl.hEvent); + CloseHandle(client); + CloseHandle(server); + pCloseThreadpoolIo(io); + pTpReleasePool(pool); +} + START_TEST(threadpool) { test_RtlQueueWorkItem(); @@ -1916,4 +2415,6 @@ START_TEST(threadpool) test_tp_window_length(); test_tp_wait(); test_tp_multi_wait(); + test_tp_io(); + test_kernel32_tp_io(); } diff --git a/sdk/include/ndk/rtlfuncs.h b/sdk/include/ndk/rtlfuncs.h index 7b128cd440bb1..c9135cfc90976 100644 --- a/sdk/include/ndk/rtlfuncs.h +++ b/sdk/include/ndk/rtlfuncs.h @@ -4748,24 +4748,29 @@ RtlSleepConditionVariableSRW( // // Synchronization functions // +NTSYSAPI VOID NTAPI RtlInitializeConditionVariable(OUT PRTL_CONDITION_VARIABLE ConditionVariable); +NTSYSAPI VOID NTAPI RtlWakeConditionVariable(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable); +NTSYSAPI VOID NTAPI RtlWakeAllConditionVariable(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable); +NTSYSAPI NTSTATUS NTAPI RtlSleepConditionVariableCS(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable, IN OUT PRTL_CRITICAL_SECTION CriticalSection, IN const PLARGE_INTEGER TimeOut OPTIONAL); +NTSYSAPI NTSTATUS NTAPI RtlSleepConditionVariableSRW(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable, diff --git a/sdk/include/xdk/winnt_old.h b/sdk/include/xdk/winnt_old.h index b3b1d98e1b8fc..95148483f80bc 100644 --- a/sdk/include/xdk/winnt_old.h +++ b/sdk/include/xdk/winnt_old.h @@ -4466,6 +4466,12 @@ typedef enum _TP_CALLBACK_PRIORITY { TP_CALLBACK_PRIORITY_COUNT = TP_CALLBACK_PRIORITY_INVALID } TP_CALLBACK_PRIORITY; +typedef struct _TP_POOL_STACK_INFORMATION +{ + SIZE_T StackReserve; + SIZE_T StackCommit; +} TP_POOL_STACK_INFORMATION,*PTP_POOL_STACK_INFORMATION; + typedef VOID (NTAPI *PTP_WORK_CALLBACK)( _Inout_ PTP_CALLBACK_INSTANCE Instance, diff --git a/sdk/lib/rtl/CMakeLists.txt b/sdk/lib/rtl/CMakeLists.txt index e8925ef779732..3e8d9d111b59a 100644 --- a/sdk/lib/rtl/CMakeLists.txt +++ b/sdk/lib/rtl/CMakeLists.txt @@ -8,19 +8,25 @@ add_definitions( if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") # Enable this again. CORE-17637 add_compile_options(-Wunused-result) + + add_compile_options(-Wno-incompatible-pointer-types) + add_compile_options(-Wno-missing-braces) endif() list(APPEND RTL_WINE_SOURCE actctx.c timerqueue.c - wait.c + threadpool.c ) +set_source_files_properties(threadpool.c PROPERTIES COMPILE_DEFINITIONS __WINESRC__) if(MSVC) # Silence warning C4267: 'initializing': conversion from 'size_t' to 'const int', possible loss of data set_source_files_properties(${RTL_WINE_SOURCE} PROPERTIES COMPILE_OPTIONS "/wd4267") endif() +include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine) + list(APPEND SOURCE ${RTL_WINE_SOURCE} access.c @@ -126,7 +132,6 @@ list(APPEND SOURCE_VISTA condvar.c runonce.c srw.c - threadpool.c utf8.c) add_library(rtl_vista ${SOURCE_VISTA}) diff --git a/sdk/lib/rtl/rtl.h b/sdk/lib/rtl/rtl.h index 037b2ee2c9b93..d818fcad4dc08 100644 --- a/sdk/lib/rtl/rtl.h +++ b/sdk/lib/rtl/rtl.h @@ -56,6 +56,7 @@ /* Use intrinsics for x86 and x64 */ #if defined(_M_IX86) || defined(_M_AMD64) +#ifndef InterlockedCompareExchange #define InterlockedCompareExchange _InterlockedCompareExchange #define InterlockedIncrement _InterlockedIncrement #define InterlockedDecrement _InterlockedDecrement @@ -64,5 +65,6 @@ #define InterlockedBitTestAndSet _interlockedbittestandset #define InterlockedBitTestAndSet64 _interlockedbittestandset64 #endif +#endif #endif /* RTL_H */ diff --git a/sdk/lib/rtl/rtl_vista.h b/sdk/lib/rtl/rtl_vista.h index af14ef4fcb64a..2fbd720f08655 100644 --- a/sdk/lib/rtl/rtl_vista.h +++ b/sdk/lib/rtl/rtl_vista.h @@ -17,4 +17,9 @@ /* Main RTL Header */ #include "rtl.h" +#define TRACE(fmt, ...) +#define WARN(fmt, ...) +#define FIXME(fmt, ...) +#define ERR(fmt, ...) + #endif /* RTL_VISTA_H */ diff --git a/sdk/lib/rtl/threadpool.c b/sdk/lib/rtl/threadpool.c index c6e40fc78f423..ed564d06cdeb1 100644 --- a/sdk/lib/rtl/threadpool.c +++ b/sdk/lib/rtl/threadpool.c @@ -19,6 +19,43 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ + +#ifdef __REACTOS__ +#include + +#include "wine/list.h" +#include +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_x) (sizeof((_x))/sizeof((_x)[0])) +#endif + +/* Winternal ?*/ + +typedef struct _THREAD_NAME_INFORMATION +{ + UNICODE_STRING ThreadName; +} THREAD_NAME_INFORMATION, *PTHREAD_NAME_INFORMATION; + +typedef void (CALLBACK *PNTAPCFUNC)(ULONG_PTR,ULONG_PTR,ULONG_PTR); /* FIXME: not the right name */ +typedef void (CALLBACK *PRTL_THREAD_START_ROUTINE)(LPVOID); /* FIXME: not the right name */ +typedef DWORD (CALLBACK *PRTL_WORK_ITEM_ROUTINE)(LPVOID); /* FIXME: not the right name */ +typedef void (NTAPI *RTL_WAITORTIMERCALLBACKFUNC)(PVOID,BOOLEAN); /* FIXME: not the right name */ +typedef VOID (CALLBACK *PRTL_OVERLAPPED_COMPLETION_ROUTINE)(DWORD,DWORD,LPVOID); + +typedef void (CALLBACK *PTP_IO_CALLBACK)(PTP_CALLBACK_INSTANCE,void*,void*,IO_STATUS_BLOCK*,PTP_IO); + +// More winternl maybe +NTSYSAPI NTSTATUS WINAPI TpSimpleTryPost(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); + +// Redefines +#define PRTL_WORK_ITEM_ROUTINE WORKERCALLBACKFUNC + +#define CRITICAL_SECTION RTL_CRITICAL_SECTION +#define GetProcessHeap() RtlGetProcessHeap() +#define GetCurrentProcess() NtCurrentProcess() +#define GetCurrentThread() NtCurrentThread() +#define GetCurrentThreadId() HandleToULong(NtCurrentTeb()->ClientId.UniqueThread) +#else #include #include #include @@ -33,6 +70,7 @@ #include "ntdll_misc.h" WINE_DEFAULT_DEBUG_CHANNEL(threadpool); +#endif /* * Old thread pooling API @@ -47,7 +85,9 @@ struct rtl_work_item #define EXPIRE_NEVER (~(ULONGLONG)0) #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */ +#ifndef __REACTOS__ static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug; +#endif static struct { @@ -57,15 +97,21 @@ static struct old_threadpool = { NULL, /* compl_port */ +#ifdef __REACTOS__ + {0}, /* threadpool_compl_cs */ +#else { &critsect_compl_debug, -1, 0, 0, 0, 0 }, /* threadpool_compl_cs */ +#endif }; +#ifndef __REACTOS__ static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug = { 0, 0, &old_threadpool.threadpool_compl_cs, { &critsect_compl_debug.ProcessLocksList, &critsect_compl_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_compl_cs") } }; +#endif struct timer_queue; struct queue_timer @@ -235,8 +281,10 @@ struct threadpool_group struct list members; }; +#ifndef __REACTOS__ /* global timerqueue object */ static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug; +#endif static struct { @@ -248,13 +296,22 @@ static struct } timerqueue = { +#ifdef __REACTOS__ + {0}, /* cs */ +#else { &timerqueue_debug, -1, 0, 0, 0, 0 }, /* cs */ +#endif 0, /* objcount */ FALSE, /* thread_running */ LIST_INIT( timerqueue.pending_timers ), /* pending_timers */ +#if __REACTOS__ + 0, +#else RTL_CONDITION_VARIABLE_INIT /* update_event */ +#endif }; +#ifndef __REACTOS__ static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug = { 0, 0, &timerqueue.cs, @@ -264,6 +321,7 @@ static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug = /* global waitqueue object */ static RTL_CRITICAL_SECTION_DEBUG waitqueue_debug; +#endif static struct { @@ -273,17 +331,23 @@ static struct } waitqueue = { +#ifdef __REACTOS__ + {0}, /* cs */ +#else { &waitqueue_debug, -1, 0, 0, 0, 0 }, /* cs */ +#endif 0, /* num_buckets */ LIST_INIT( waitqueue.buckets ) /* buckets */ }; +#ifndef __REACTOS__ static RTL_CRITICAL_SECTION_DEBUG waitqueue_debug = { 0, 0, &waitqueue.cs, { &waitqueue_debug.ProcessLocksList, &waitqueue_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": waitqueue.cs") } }; +#endif struct waitqueue_bucket { @@ -295,8 +359,10 @@ struct waitqueue_bucket BOOL alertable; }; +#ifndef __REACTOS__ /* global I/O completion queue object */ static RTL_CRITICAL_SECTION_DEBUG ioqueue_debug; +#endif static struct { @@ -308,15 +374,21 @@ static struct } ioqueue = { +#ifdef __REACTOS__ + .cs = {0}, +#else .cs = { &ioqueue_debug, -1, 0, 0, 0, 0 }, +#endif }; +#ifndef __REACTOS__ static RTL_CRITICAL_SECTION_DEBUG ioqueue_debug = { 0, 0, &ioqueue.cs, { &ioqueue_debug.ProcessLocksList, &ioqueue_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": ioqueue.cs") } }; +#endif static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool ) { @@ -361,7 +433,11 @@ static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CAL return (struct threadpool_instance *)instance; } +#ifdef __REACTOS__ +ULONG NTAPI threadpool_worker_proc(PVOID param ); +#else static void CALLBACK threadpool_worker_proc( void *param ); +#endif static void tp_object_submit( struct threadpool_object *object, BOOL signaled ); static void tp_object_execute( struct threadpool_object *object, BOOL wait_thread ); static void tp_object_prepare_shutdown( struct threadpool_object *object ); @@ -397,10 +473,12 @@ static BOOL array_reserve(void **elements, unsigned int *capacity, unsigned int static void set_thread_name(const WCHAR *name) { +#ifndef __REACTOS__ // This is impossible on non vista+ THREAD_NAME_INFORMATION info; RtlInitUnicodeString(&info.ThreadName, name); NtSetInformationThread(GetCurrentThread(), ThreadNameInformation, &info, sizeof(info)); +#endif } static void CALLBACK process_rtl_work_item( TP_CALLBACK_INSTANCE *instance, void *userdata ) @@ -472,7 +550,11 @@ static DWORD CALLBACK iocp_poller(LPVOID Arg) PRTL_OVERLAPPED_COMPLETION_ROUTINE callback; LPVOID overlapped; IO_STATUS_BLOCK iosb; +#ifdef __REACTOS__ + NTSTATUS res = NtRemoveIoCompletion( cport, (PVOID)&callback, (PVOID)&overlapped, &iosb, NULL ); +#else NTSTATUS res = NtRemoveIoCompletion( cport, (PULONG_PTR)&callback, (PULONG_PTR)&overlapped, &iosb, NULL ); +#endif if (res) { ERR("NtRemoveIoCompletion failed: 0x%lx\n", res); @@ -509,7 +591,11 @@ static DWORD CALLBACK iocp_poller(LPVOID Arg) * Failure: Any NTSTATUS code. * */ +#ifdef __REACTOS__ +NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PIO_APC_ROUTINE Function, ULONG Flags) +#else NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags) +#endif { IO_STATUS_BLOCK iosb; FILE_COMPLETION_INFORMATION info; @@ -539,10 +625,13 @@ NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_CO RtlLeaveCriticalSection(&old_threadpool.threadpool_compl_cs); if (res) return res; } - +#ifdef __REACTOS__ + info.Port = old_threadpool.compl_port; + info.Key = (PVOID)Function; +#else info.CompletionPort = old_threadpool.compl_port; info.CompletionKey = (ULONG_PTR)Function; - +#endif return NtSetInformationFile( FileHandle, &iosb, &info, sizeof(info), FileCompletionInformation ); } @@ -705,7 +794,11 @@ static ULONG queue_get_timeout(struct timer_queue *q) return timeout; } +#ifdef __REACTOS__ +ULONG NTAPI timer_queue_thread_proc(PVOID p) +#else static void WINAPI timer_queue_thread_proc(LPVOID p) +#endif { struct timer_queue *q = p; ULONG timeout_ms; @@ -746,6 +839,9 @@ static void WINAPI timer_queue_thread_proc(LPVOID p) q->magic = 0; RtlFreeHeap(GetProcessHeap(), 0, q); RtlExitUserThread( 0 ); +#ifdef __REACTOS__ + return STATUS_SUCCESS; +#endif } static void queue_destroy_timer(struct queue_timer *t) @@ -1052,7 +1148,11 @@ NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer, /*********************************************************************** * timerqueue_thread_proc (internal) */ +#ifdef __REACTOS__ +ULONG NTAPI timerqueue_thread_proc(PVOID param ) +#else static void CALLBACK timerqueue_thread_proc( void *param ) +#endif { ULONGLONG timeout_lower, timeout_upper, new_timeout; struct threadpool_object *other_timer; @@ -1139,6 +1239,9 @@ static void CALLBACK timerqueue_thread_proc( void *param ) TRACE( "terminating timer queue thread\n" ); RtlExitUserThread( 0 ); +#ifdef __REACTOS__ + return STATUS_SUCCESS; +#endif } /*********************************************************************** @@ -1240,7 +1343,11 @@ static void tp_timerqueue_unlock( struct threadpool_object *timer ) /*********************************************************************** * waitqueue_thread_proc (internal) */ +#ifdef __REACTOS__ +ULONG NTAPI waitqueue_thread_proc(PVOID param ) +#else static void CALLBACK waitqueue_thread_proc( void *param ) +#endif { struct threadpool_object *objects[MAXIMUM_WAITQUEUE_OBJECTS]; HANDLE handles[MAXIMUM_WAITQUEUE_OBJECTS + 1]; @@ -1512,12 +1619,20 @@ static void tp_waitqueue_unlock( struct threadpool_object *wait ) RtlLeaveCriticalSection( &waitqueue.cs ); } +#ifdef __REACTOS__ +ULONG NTAPI ioqueue_thread_proc(PVOID param ) +#else static void CALLBACK ioqueue_thread_proc( void *param ) +#endif { struct io_completion *completion; struct threadpool_object *io; IO_STATUS_BLOCK iosb; +#ifdef __REACTOS__ + PVOID key, value; +#else ULONG_PTR key, value; +#endif BOOL destroy, skip; NTSTATUS status; @@ -1581,7 +1696,11 @@ static void CALLBACK ioqueue_thread_proc( void *param ) completion = &io->u.io.completions[io->u.io.completion_count++]; completion->iosb = iosb; +#ifdef __REACTOS__ + completion->cvalue = (ULONG_PTR)value; +#else completion->cvalue = value; +#endif tp_object_submit( io, FALSE ); } @@ -1606,6 +1725,10 @@ static void CALLBACK ioqueue_thread_proc( void *param ) TRACE( "terminating I/O completion thread\n" ); RtlExitUserThread( 0 ); + +#ifdef __REACTOS__ + return STATUS_SUCCESS; +#endif } static NTSTATUS tp_ioqueue_lock( struct threadpool_object *io, HANDLE file ) @@ -1640,8 +1763,13 @@ static NTSTATUS tp_ioqueue_lock( struct threadpool_object *io, HANDLE file ) FILE_COMPLETION_INFORMATION info; IO_STATUS_BLOCK iosb; +#ifdef __REACTOS__ + info.Port = ioqueue.port; + info.Key = io; +#else info.CompletionPort = ioqueue.port; info.CompletionKey = (ULONG_PTR)io; +#endif status = NtSetInformationFile( file, &iosb, &info, sizeof(info), FileCompletionInformation ); } @@ -1663,7 +1791,11 @@ static NTSTATUS tp_ioqueue_lock( struct threadpool_object *io, HANDLE file ) */ static NTSTATUS tp_threadpool_alloc( struct threadpool **out ) { +#ifdef __REACTOS__ + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->ProcessEnvironmentBlock->ImageBaseAddress ); +#else IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); +#endif struct threadpool *pool; unsigned int i; @@ -1675,8 +1807,13 @@ static NTSTATUS tp_threadpool_alloc( struct threadpool **out ) pool->objcount = 0; pool->shutdown = FALSE; +#ifdef __REACTOS__ + RtlInitializeCriticalSection( &pool->cs ); +#else RtlInitializeCriticalSectionEx( &pool->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + pool->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": threadpool.cs"); +#endif for (i = 0; i < ARRAY_SIZE(pool->pools); ++i) list_init( &pool->pools[i] ); @@ -1728,8 +1865,9 @@ static BOOL tp_threadpool_release( struct threadpool *pool ) assert( !pool->objcount ); for (i = 0; i < ARRAY_SIZE(pool->pools); ++i) assert( list_empty( &pool->pools[i] ) ); - +#ifndef __REACTOS__ pool->cs.DebugInfo->Spare[0] = 0; +#endif RtlDeleteCriticalSection( &pool->cs ); RtlFreeHeap( GetProcessHeap(), 0, pool ); @@ -1750,6 +1888,7 @@ static NTSTATUS tp_threadpool_lock( struct threadpool **out, TP_CALLBACK_ENVIRON if (environment) { +#ifndef __REACTOS__ //Windows 7 stuff /* Validate environment parameters. */ if (environment->Version == 3) { @@ -1765,7 +1904,7 @@ static NTSTATUS tp_threadpool_lock( struct threadpool **out, TP_CALLBACK_ENVIRON return STATUS_INVALID_PARAMETER; } } - +#endif pool = (struct threadpool *)environment->Pool; } @@ -1786,7 +1925,7 @@ static NTSTATUS tp_threadpool_lock( struct threadpool **out, TP_CALLBACK_ENVIRON pool = default_threadpool; } - + RtlEnterCriticalSection( &pool->cs ); /* Make sure that the threadpool has at least one thread. */ @@ -1839,8 +1978,13 @@ static NTSTATUS tp_group_alloc( struct threadpool_group **out ) group->refcount = 1; group->shutdown = FALSE; +#ifdef __REACTOS__ + RtlInitializeCriticalSection( &group->cs ); +#else RtlInitializeCriticalSectionEx( &group->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + group->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": threadpool_group.cs"); +#endif list_init( &group->members ); @@ -1875,7 +2019,9 @@ static BOOL tp_group_release( struct threadpool_group *group ) assert( group->shutdown ); assert( list_empty( &group->members ) ); +#ifndef __REACTOS__ group->cs.DebugInfo->Spare[0] = 0; +#endif RtlDeleteCriticalSection( &group->cs ); RtlFreeHeap( GetProcessHeap(), 0, group ); @@ -1925,6 +2071,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa object->finalization_callback = environment->FinalizationCallback; object->may_run_long = environment->u.s.LongFunction != 0; object->race_dll = environment->RaceDll; +#ifndef __REACTOS__ //Windows 7 stuff if (environment->Version == 3) { TP_CALLBACK_ENVIRON_V3 *environment_v3 = (TP_CALLBACK_ENVIRON_V3 *)environment; @@ -1932,7 +2079,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa object->priority = environment_v3->CallbackPriority; assert( object->priority < ARRAY_SIZE(pool->pools) ); } - +#endif if (environment->ActivationContext) FIXME( "activation context not supported yet\n" ); @@ -2328,7 +2475,11 @@ static void tp_object_execute( struct threadpool_object *object, BOOL wait_threa /*********************************************************************** * threadpool_worker_proc (internal) */ +#ifdef __REACTOS__ +ULONG NTAPI threadpool_worker_proc(PVOID param ) +#else static void CALLBACK threadpool_worker_proc( void *param ) +#endif { struct threadpool *pool = param; LARGE_INTEGER timeout; @@ -2382,6 +2533,9 @@ static void CALLBACK threadpool_worker_proc( void *param ) TRACE( "terminating worker thread for pool %p\n", pool ); tp_threadpool_release( pool ); RtlExitUserThread( 0 ); +#ifdef __REACTOS__ + return STATUS_SUCCESS; +#endif } /*********************************************************************** @@ -3355,3 +3509,16 @@ NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle) { return RtlDeregisterWaitEx(WaitHandle, NULL); } + +#ifdef __REACTOS__ +VOID +NTAPI +RtlpInitializeThreadPooling( + VOID) +{ + RtlInitializeCriticalSection(&old_threadpool.threadpool_compl_cs); + RtlInitializeCriticalSection(&timerqueue.cs); + RtlInitializeCriticalSection(&waitqueue.cs); + RtlInitializeCriticalSection(&ioqueue.cs); +} +#endif diff --git a/sdk/lib/rtl/timerqueue.c b/sdk/lib/rtl/timerqueue.c index 596588bdafdf9..195059ae1eab0 100644 --- a/sdk/lib/rtl/timerqueue.c +++ b/sdk/lib/rtl/timerqueue.c @@ -64,405 +64,6 @@ struct timer_queue #define EXPIRE_NEVER (~(ULONGLONG) 0) #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */ -static void queue_remove_timer(struct queue_timer *t) -{ - /* We MUST hold the queue cs while calling this function. This ensures - that we cannot queue another callback for this timer. The runcount - being zero makes sure we don't have any already queued. */ - struct timer_queue *q = t->q; - - assert(t->runcount == 0); - assert(t->destroy); - - list_remove(&t->entry); - if (t->event) - NtSetEvent(t->event, NULL); - RtlFreeHeap(RtlGetProcessHeap(), 0, t); - - if (q->quit && list_empty(&q->timers)) - NtSetEvent(q->event, NULL); -} - -static void timer_cleanup_callback(struct queue_timer *t) -{ - struct timer_queue *q = t->q; - RtlEnterCriticalSection(&q->cs); - - assert(0 < t->runcount); - --t->runcount; - - if (t->destroy && t->runcount == 0) - queue_remove_timer(t); - - RtlLeaveCriticalSection(&q->cs); -} - -static VOID WINAPI timer_callback_wrapper(LPVOID p) -{ - struct queue_timer *t = p; - t->callback(t->param, TRUE); - timer_cleanup_callback(t); -} - -static inline ULONGLONG queue_current_time(void) -{ - LARGE_INTEGER now, freq; - NtQueryPerformanceCounter(&now, &freq); - return now.QuadPart * 1000 / freq.QuadPart; -} - -static void queue_add_timer(struct queue_timer *t, ULONGLONG time, - BOOL set_event) -{ - /* We MUST hold the queue cs while calling this function. */ - struct timer_queue *q = t->q; - struct list *ptr = &q->timers; - - assert(!q->quit || (t->destroy && time == EXPIRE_NEVER)); - - if (time != EXPIRE_NEVER) - LIST_FOR_EACH(ptr, &q->timers) - { - struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry); - if (time < cur->expire) - break; - } - list_add_before(ptr, &t->entry); - - t->expire = time; - - /* If we insert at the head of the list, we need to expire sooner - than expected. */ - if (set_event && &t->entry == list_head(&q->timers)) - NtSetEvent(q->event, NULL); -} - -static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time, - BOOL set_event) -{ - /* We MUST hold the queue cs while calling this function. */ - list_remove(&t->entry); - queue_add_timer(t, time, set_event); -} - -static void queue_timer_expire(struct timer_queue *q) -{ - struct queue_timer *t = NULL; - - RtlEnterCriticalSection(&q->cs); - if (list_head(&q->timers)) - { - ULONGLONG now, next; - t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); - if (!t->destroy && t->expire <= ((now = queue_current_time()))) - { - ++t->runcount; - if (t->period) - { - next = t->expire + t->period; - /* avoid trigger cascade if overloaded / hibernated */ - if (next < now) - next = now + t->period; - } - else - next = EXPIRE_NEVER; - queue_move_timer(t, next, FALSE); - } - else - t = NULL; - } - RtlLeaveCriticalSection(&q->cs); - - if (t) - { - if (t->flags & WT_EXECUTEINTIMERTHREAD) - timer_callback_wrapper(t); - else - { - ULONG flags - = (t->flags - & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD - | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION)); - NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags); - if (status != STATUS_SUCCESS) - timer_cleanup_callback(t); - } - } -} - -static ULONG queue_get_timeout(struct timer_queue *q) -{ - struct queue_timer *t; - ULONG timeout = INFINITE; - - RtlEnterCriticalSection(&q->cs); - if (list_head(&q->timers)) - { - t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); - assert(!t->destroy || t->expire == EXPIRE_NEVER); - - if (t->expire != EXPIRE_NEVER) - { - ULONGLONG time = queue_current_time(); - timeout = t->expire < time ? 0 : (ULONG)(t->expire - time); - } - } - RtlLeaveCriticalSection(&q->cs); - - return timeout; -} - -static DWORD WINAPI timer_queue_thread_proc(LPVOID p) -{ - struct timer_queue *q = p; - ULONG timeout_ms; - - timeout_ms = INFINITE; - for (;;) - { - LARGE_INTEGER timeout; - NTSTATUS status; - BOOL done = FALSE; - - status = NtWaitForSingleObject( - q->event, FALSE, get_nt_timeout(&timeout, timeout_ms)); - - if (status == STATUS_WAIT_0) - { - /* There are two possible ways to trigger the event. Either - we are quitting and the last timer got removed, or a new - timer got put at the head of the list so we need to adjust - our timeout. */ - RtlEnterCriticalSection(&q->cs); - if (q->quit && list_empty(&q->timers)) - done = TRUE; - RtlLeaveCriticalSection(&q->cs); - } - else if (status == STATUS_TIMEOUT) - queue_timer_expire(q); - - if (done) - break; - - timeout_ms = queue_get_timeout(q); - } - - NtClose(q->event); - RtlDeleteCriticalSection(&q->cs); - q->magic = 0; - RtlFreeHeap(RtlGetProcessHeap(), 0, q); - RtlpExitThreadFunc(STATUS_SUCCESS); - return 0; -} - -static void queue_destroy_timer(struct queue_timer *t) -{ - /* We MUST hold the queue cs while calling this function. */ - t->destroy = TRUE; - if (t->runcount == 0) - /* Ensure a timer is promptly removed. If callbacks are pending, - it will be removed after the last one finishes by the callback - cleanup wrapper. */ - queue_remove_timer(t); - else - /* Make sure no destroyed timer masks an active timer at the head - of the sorted list. */ - queue_move_timer(t, EXPIRE_NEVER, FALSE); -} - -/*********************************************************************** - * RtlCreateTimerQueue (NTDLL.@) - * - * Creates a timer queue object and returns a handle to it. - * - * PARAMS - * NewTimerQueue [O] The newly created queue. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - */ -NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue) -{ - NTSTATUS status; - struct timer_queue *q = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *q); - if (!q) - return STATUS_NO_MEMORY; - - RtlInitializeCriticalSection(&q->cs); - list_init(&q->timers); - q->quit = FALSE; - q->magic = TIMER_QUEUE_MAGIC; - status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); - if (status != STATUS_SUCCESS) - { - RtlFreeHeap(RtlGetProcessHeap(), 0, q); - return status; - } - status = RtlpStartThreadFunc(timer_queue_thread_proc, q, &q->thread); - if (status != STATUS_SUCCESS) - { - NtClose(q->event); - RtlFreeHeap(RtlGetProcessHeap(), 0, q); - return status; - } - - NtResumeThread(q->thread, NULL); - *NewTimerQueue = q; - return STATUS_SUCCESS; -} - -/*********************************************************************** - * RtlDeleteTimerQueueEx (NTDLL.@) - * - * Deletes a timer queue object. - * - * PARAMS - * TimerQueue [I] The timer queue to destroy. - * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, - * wait until all timers are finished firing before - * returning. Otherwise, return immediately and set the - * event when all timers are done. - * - * RETURNS - * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not. - * Failure: Any NTSTATUS code. - */ -NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) -{ - struct timer_queue *q = TimerQueue; - struct queue_timer *t, *temp; - HANDLE thread; - NTSTATUS status; - - if (!q || q->magic != TIMER_QUEUE_MAGIC) - return STATUS_INVALID_HANDLE; - - thread = q->thread; - - RtlEnterCriticalSection(&q->cs); - q->quit = TRUE; - if (list_head(&q->timers)) - /* When the last timer is removed, it will signal the timer thread to - exit... */ - LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry) - queue_destroy_timer(t); - else - /* However if we have none, we must do it ourselves. */ - NtSetEvent(q->event, NULL); - RtlLeaveCriticalSection(&q->cs); - - if (CompletionEvent == INVALID_HANDLE_VALUE) - { - NtWaitForSingleObject(thread, FALSE, NULL); - status = STATUS_SUCCESS; - } - else - { - if (CompletionEvent) - { - DPRINT1("asynchronous return on completion event unimplemented\n"); - NtWaitForSingleObject(thread, FALSE, NULL); - NtSetEvent(CompletionEvent, NULL); - } - status = STATUS_PENDING; - } - - NtClose(thread); - return status; -} - -static struct timer_queue *get_timer_queue(HANDLE TimerQueue) -{ - static struct timer_queue *default_timer_queue; - - if (TimerQueue) - return TimerQueue; - else - { - if (!default_timer_queue) - { - HANDLE q; - NTSTATUS status = RtlCreateTimerQueue(&q); - if (status == STATUS_SUCCESS) - { - PVOID p = InterlockedCompareExchangePointer( - (void **) &default_timer_queue, q, NULL); - if (p) - /* Got beat to the punch. */ - RtlDeleteTimerQueueEx(q, NULL); - } - } - return default_timer_queue; - } -} - -/*********************************************************************** - * RtlCreateTimer (NTDLL.@) - * - * Creates a new timer associated with the given queue. - * - * PARAMS - * NewTimer [O] The newly created timer. - * TimerQueue [I] The queue to hold the timer. - * Callback [I] The callback to fire. - * Parameter [I] The argument for the callback. - * DueTime [I] The delay, in milliseconds, before first firing the - * timer. - * Period [I] The period, in milliseconds, at which to fire the timer - * after the first callback. If zero, the timer will only - * fire once. It still needs to be deleted with - * RtlDeleteTimer. - * Flags [I] Flags controlling the execution of the callback. In - * addition to the WT_* thread pool flags (see - * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and - * WT_EXECUTEONLYONCE are supported. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - */ -NTSTATUS WINAPI RtlCreateTimer(HANDLE TimerQueue, PHANDLE NewTimer, - WAITORTIMERCALLBACKFUNC Callback, - PVOID Parameter, DWORD DueTime, DWORD Period, - ULONG Flags) -{ - NTSTATUS status; - struct queue_timer *t; - struct timer_queue *q = get_timer_queue(TimerQueue); - - if (!q) return STATUS_NO_MEMORY; - if (q->magic != TIMER_QUEUE_MAGIC) return STATUS_INVALID_HANDLE; - - t = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *t); - if (!t) - return STATUS_NO_MEMORY; - - t->q = q; - t->runcount = 0; - t->callback = Callback; - t->param = Parameter; - t->period = Period; - t->flags = Flags; - t->destroy = FALSE; - t->event = NULL; - - status = STATUS_SUCCESS; - RtlEnterCriticalSection(&q->cs); - if (q->quit) - status = STATUS_INVALID_HANDLE; - else - queue_add_timer(t, queue_current_time() + DueTime, TRUE); - RtlLeaveCriticalSection(&q->cs); - - if (status == STATUS_SUCCESS) - *NewTimer = t; - else - RtlFreeHeap(RtlGetProcessHeap(), 0, t); - - return status; -} - NTSTATUS WINAPI RtlSetTimer( @@ -483,100 +84,6 @@ RtlSetTimer( Flags); } -/*********************************************************************** - * RtlUpdateTimer (NTDLL.@) - * - * Changes the time at which a timer expires. - * - * PARAMS - * TimerQueue [I] The queue that holds the timer. - * Timer [I] The timer to update. - * DueTime [I] The delay, in milliseconds, before next firing the timer. - * Period [I] The period, in milliseconds, at which to fire the timer - * after the first callback. If zero, the timer will not - * refire once. It still needs to be deleted with - * RtlDeleteTimer. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - */ -NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer, - DWORD DueTime, DWORD Period) -{ - struct queue_timer *t = Timer; - struct timer_queue *q = t->q; - - RtlEnterCriticalSection(&q->cs); - /* Can't change a timer if it was once-only or destroyed. */ - if (t->expire != EXPIRE_NEVER) - { - t->period = Period; - queue_move_timer(t, queue_current_time() + DueTime, TRUE); - } - RtlLeaveCriticalSection(&q->cs); - - return STATUS_SUCCESS; -} - -/*********************************************************************** - * RtlDeleteTimer (NTDLL.@) - * - * Cancels a timer-queue timer. - * - * PARAMS - * TimerQueue [I] The queue that holds the timer. - * Timer [I] The timer to update. - * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, - * wait until the timer is finished firing all pending - * callbacks before returning. Otherwise, return - * immediately and set the timer is done. - * - * RETURNS - * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not, - or if the completion event is NULL. - * Failure: Any NTSTATUS code. - */ -NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer, - HANDLE CompletionEvent) -{ - struct queue_timer *t = Timer; - struct timer_queue *q; - NTSTATUS status = STATUS_PENDING; - HANDLE event = NULL; - - if (!Timer) - return STATUS_INVALID_PARAMETER_1; - q = t->q; - if (CompletionEvent == INVALID_HANDLE_VALUE) - { - status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); - if (status == STATUS_SUCCESS) - status = STATUS_PENDING; - } - else if (CompletionEvent) - event = CompletionEvent; - - RtlEnterCriticalSection(&q->cs); - t->event = event; - if (t->runcount == 0 && event) - status = STATUS_SUCCESS; - queue_destroy_timer(t); - RtlLeaveCriticalSection(&q->cs); - - if (CompletionEvent == INVALID_HANDLE_VALUE && event) - { - if (status == STATUS_PENDING) - { - NtWaitForSingleObject(event, FALSE, NULL); - status = STATUS_SUCCESS; - } - NtClose(event); - } - - return status; -} - /* * @implemented */ diff --git a/sdk/lib/rtl/wait.c b/sdk/lib/rtl/wait.c deleted file mode 100644 index bab26365d5c87..0000000000000 --- a/sdk/lib/rtl/wait.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS system libraries - * PURPOSE: Rtl user wait functions - * FILE: lib/rtl/wait.c - * PROGRAMERS: - * Alex Ionescu (alex@relsoft.net) - * Eric Kohl - * KJK::Hyperion - */ - -/* INCLUDES *****************************************************************/ - -#include - -#define NDEBUG -#include - -typedef struct _RTLP_WAIT -{ - HANDLE Object; - BOOLEAN CallbackInProgress; - HANDLE CancelEvent; - LONG DeleteCount; - HANDLE CompletionEvent; - ULONG Flags; - WAITORTIMERCALLBACKFUNC Callback; - PVOID Context; - ULONG Milliseconds; -} RTLP_WAIT, *PRTLP_WAIT; - -/* PRIVATE FUNCTIONS *******************************************************/ - -static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout ) -{ - if (timeout == INFINITE) return NULL; - pTime->QuadPart = (ULONGLONG)timeout * -10000; - return pTime; -} - -static VOID -NTAPI -Wait_thread_proc(LPVOID Arg) -{ - PRTLP_WAIT Wait = (PRTLP_WAIT) Arg; - NTSTATUS Status; - BOOLEAN alertable = (Wait->Flags & WT_EXECUTEINIOTHREAD) != 0; - HANDLE handles[2] = { Wait->CancelEvent, Wait->Object }; - LARGE_INTEGER timeout; - HANDLE completion_event; - -// TRACE("\n"); - - while (TRUE) - { - Status = NtWaitForMultipleObjects( 2, - handles, - WaitAny, - alertable, - get_nt_timeout( &timeout, Wait->Milliseconds ) ); - - if (Status == STATUS_WAIT_1 || Status == STATUS_TIMEOUT) - { - BOOLEAN TimerOrWaitFired; - - if (Status == STATUS_WAIT_1) - { - // TRACE( "object %p signaled, calling callback %p with context %p\n", - // Wait->Object, Wait->Callback, - // Wait->Context ); - TimerOrWaitFired = FALSE; - } - else - { - // TRACE( "wait for object %p timed out, calling callback %p with context %p\n", - // Wait->Object, Wait->Callback, - // Wait->Context ); - TimerOrWaitFired = TRUE; - } - Wait->CallbackInProgress = TRUE; - Wait->Callback( Wait->Context, TimerOrWaitFired ); - Wait->CallbackInProgress = FALSE; - - if (Wait->Flags & WT_EXECUTEONLYONCE) - break; - } - else if (Status != STATUS_USER_APC) - break; - } - - completion_event = Wait->CompletionEvent; - if (completion_event) NtSetEvent( completion_event, NULL ); - - if (InterlockedIncrement( &Wait->DeleteCount ) == 2 ) - { - NtClose( Wait->CancelEvent ); - RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); - } -} - - -/* FUNCTIONS ***************************************************************/ - - -/*********************************************************************** - * RtlRegisterWait - * - * Registers a wait for a handle to become signaled. - * - * PARAMS - * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it. - * Object [I] Object to wait to become signaled. - * Callback [I] Callback function to execute when the wait times out or the handle is signaled. - * Context [I] Context to pass to the callback function when it is executed. - * Milliseconds [I] Number of milliseconds to wait before timing out. - * Flags [I] Flags. See notes. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - * - * NOTES - * Flags can be one or more of the following: - *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread. - *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread. - *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent. - *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time. - *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token. - */ -NTSTATUS -NTAPI -RtlRegisterWait(PHANDLE NewWaitObject, - HANDLE Object, - WAITORTIMERCALLBACKFUNC Callback, - PVOID Context, - ULONG Milliseconds, - ULONG Flags) -{ - PRTLP_WAIT Wait; - NTSTATUS Status; - - //TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags ); - - Wait = RtlAllocateHeap( RtlGetProcessHeap(), 0, sizeof(RTLP_WAIT) ); - if (!Wait) - return STATUS_NO_MEMORY; - - Wait->Object = Object; - Wait->Callback = Callback; - Wait->Context = Context; - Wait->Milliseconds = Milliseconds; - Wait->Flags = Flags; - Wait->CallbackInProgress = FALSE; - Wait->DeleteCount = 0; - Wait->CompletionEvent = NULL; - - Status = NtCreateEvent( &Wait->CancelEvent, - EVENT_ALL_ACCESS, - NULL, - NotificationEvent, - FALSE ); - - if (Status != STATUS_SUCCESS) - { - RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); - return Status; - } - - Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD | - WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION); - - Status = RtlQueueWorkItem( Wait_thread_proc, - Wait, - Flags ); - - if (Status != STATUS_SUCCESS) - { - NtClose( Wait->CancelEvent ); - RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); - return Status; - } - - *NewWaitObject = Wait; - return Status; -} - -/*********************************************************************** - * RtlDeregisterWaitEx - * - * Cancels a wait operation and frees the resources associated with calling - * RtlRegisterWait(). - * - * PARAMS - * WaitObject [I] Handle to the wait object to free. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - */ -NTSTATUS -NTAPI -RtlDeregisterWaitEx(HANDLE WaitHandle, - HANDLE CompletionEvent) -{ - PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle; - NTSTATUS Status = STATUS_SUCCESS; - - //TRACE( "(%p)\n", WaitHandle ); - - NtSetEvent( Wait->CancelEvent, NULL ); - if (Wait->CallbackInProgress) - { - if (CompletionEvent != NULL) - { - if (CompletionEvent == INVALID_HANDLE_VALUE) - { - Status = NtCreateEvent( &CompletionEvent, - EVENT_ALL_ACCESS, - NULL, - NotificationEvent, - FALSE ); - - if (Status != STATUS_SUCCESS) - return Status; - - (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent ); - - if (Wait->CallbackInProgress) - NtWaitForSingleObject( CompletionEvent, FALSE, NULL ); - - NtClose( CompletionEvent ); - } - else - { - (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent ); - - if (Wait->CallbackInProgress) - Status = STATUS_PENDING; - } - } - else - Status = STATUS_PENDING; - } - - if (InterlockedIncrement( &Wait->DeleteCount ) == 2 ) - { - Status = STATUS_SUCCESS; - NtClose( Wait->CancelEvent ); - RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); - } - - return Status; -} - -/*********************************************************************** - * RtlDeregisterWait - * - * Cancels a wait operation and frees the resources associated with calling - * RtlRegisterWait(). - * - * PARAMS - * WaitObject [I] Handle to the wait object to free. - * - * RETURNS - * Success: STATUS_SUCCESS. - * Failure: Any NTSTATUS code. - */ -NTSTATUS -NTAPI -RtlDeregisterWait(HANDLE WaitHandle) -{ - return RtlDeregisterWaitEx(WaitHandle, NULL); -} - -/* EOF */ diff --git a/sdk/lib/rtl/workitem.c b/sdk/lib/rtl/workitem.c index 9ee1d6ac43ccb..f3e6bc380a34f 100644 --- a/sdk/lib/rtl/workitem.c +++ b/sdk/lib/rtl/workitem.c @@ -45,899 +45,6 @@ RtlpExitThread(IN NTSTATUS ExitStatus) PRTL_START_POOL_THREAD RtlpStartThreadFunc = RtlpStartThread; PRTL_EXIT_POOL_THREAD RtlpExitThreadFunc = RtlpExitThread; -#define MAX_WORKERTHREADS 0x100 -#define WORKERTHREAD_CREATION_THRESHOLD 0x5 - -typedef struct _RTLP_IOWORKERTHREAD -{ - LIST_ENTRY ListEntry; - HANDLE ThreadHandle; - ULONG Flags; -} RTLP_IOWORKERTHREAD, *PRTLP_IOWORKERTHREAD; - -typedef struct _RTLP_WORKITEM -{ - WORKERCALLBACKFUNC Function; - PVOID Context; - ULONG Flags; - HANDLE TokenHandle; -} RTLP_WORKITEM, *PRTLP_WORKITEM; - -static LONG ThreadPoolInitialized = 0; -static RTL_CRITICAL_SECTION ThreadPoolLock; -static PRTLP_IOWORKERTHREAD PersistentIoThread; -static LIST_ENTRY ThreadPoolIOWorkerThreadsList; -static HANDLE ThreadPoolCompletionPort; -static LONG ThreadPoolWorkerThreads; -static LONG ThreadPoolWorkerThreadsRequests; -static LONG ThreadPoolWorkerThreadsLongRequests; -static LONG ThreadPoolIOWorkerThreads; -static LONG ThreadPoolIOWorkerThreadsRequests; -static LONG ThreadPoolIOWorkerThreadsLongRequests; - -#define IsThreadPoolInitialized() (*((volatile LONG*)&ThreadPoolInitialized) == 1) - -static NTSTATUS -RtlpInitializeThreadPool(VOID) -{ - NTSTATUS Status = STATUS_SUCCESS; - LONG InitStatus; - - do - { - InitStatus = InterlockedCompareExchange(&ThreadPoolInitialized, - 2, - 0); - if (InitStatus == 0) - { - /* We're the first thread to initialize the thread pool */ - - InitializeListHead(&ThreadPoolIOWorkerThreadsList); - - PersistentIoThread = NULL; - - ThreadPoolWorkerThreads = 0; - ThreadPoolWorkerThreadsRequests = 0; - ThreadPoolWorkerThreadsLongRequests = 0; - ThreadPoolIOWorkerThreads = 0; - ThreadPoolIOWorkerThreadsRequests = 0; - ThreadPoolIOWorkerThreadsLongRequests = 0; - - /* Initialize the lock */ - Status = RtlInitializeCriticalSection(&ThreadPoolLock); - if (!NT_SUCCESS(Status)) - goto Finish; - - /* Create the complection port */ - Status = NtCreateIoCompletion(&ThreadPoolCompletionPort, - IO_COMPLETION_ALL_ACCESS, - NULL, - 0); - if (!NT_SUCCESS(Status)) - { - RtlDeleteCriticalSection(&ThreadPoolLock); - goto Finish; - } - -Finish: - /* Initialization done */ - InterlockedExchange(&ThreadPoolInitialized, - 1); - break; - } - else if (InitStatus == 2) - { - LARGE_INTEGER Timeout; - - /* Another thread is currently initializing the thread pool! - Poll after a short period of time to see if the initialization - was completed */ - - Timeout.QuadPart = -10000000LL; /* Wait for a second */ - NtDelayExecution(FALSE, - &Timeout); - } - } while (InitStatus != 1); - - return Status; -} - -static NTSTATUS -RtlpGetImpersonationToken(OUT PHANDLE TokenHandle) -{ - NTSTATUS Status; - - Status = NtOpenThreadToken(NtCurrentThread(), - TOKEN_IMPERSONATE, - TRUE, - TokenHandle); - if (Status == STATUS_NO_TOKEN || Status == STATUS_CANT_OPEN_ANONYMOUS) - { - *TokenHandle = NULL; - Status = STATUS_SUCCESS; - } - - return Status; -} - -static NTSTATUS -RtlpStartWorkerThread(PTHREAD_START_ROUTINE StartRoutine) -{ - NTSTATUS Status; - HANDLE ThreadHandle; - LARGE_INTEGER Timeout; - volatile LONG WorkerInitialized = 0; - - Timeout.QuadPart = -10000LL; /* Wait for 100ms */ - - /* Start the thread */ - Status = RtlpStartThreadFunc(StartRoutine, (PVOID)&WorkerInitialized, &ThreadHandle); - if (NT_SUCCESS(Status)) - { - NtResumeThread(ThreadHandle, NULL); - - /* Poll until the thread got a chance to initialize */ - while (WorkerInitialized == 0) - { - NtDelayExecution(FALSE, - &Timeout); - } - - NtClose(ThreadHandle); - } - - return Status; -} - -static VOID -NTAPI -RtlpExecuteWorkItem(IN OUT PVOID NormalContext, - IN OUT PVOID SystemArgument1, - IN OUT PVOID SystemArgument2) -{ - NTSTATUS Status; - BOOLEAN Impersonated = FALSE; - RTLP_WORKITEM WorkItem = *(volatile RTLP_WORKITEM *)SystemArgument2; - - RtlFreeHeap(RtlGetProcessHeap(), - 0, - SystemArgument2); - - if (WorkItem.TokenHandle != NULL) - { - Status = NtSetInformationThread(NtCurrentThread(), - ThreadImpersonationToken, - &WorkItem.TokenHandle, - sizeof(HANDLE)); - - NtClose(WorkItem.TokenHandle); - - if (NT_SUCCESS(Status)) - { - Impersonated = TRUE; - } - } - - _SEH2_TRY - { - DPRINT("RtlpExecuteWorkItem: Function: 0x%p Context: 0x%p ImpersonationToken: 0x%p\n", WorkItem.Function, WorkItem.Context, WorkItem.TokenHandle); - - /* Execute the function */ - WorkItem.Function(WorkItem.Context); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - DPRINT1("Exception 0x%x while executing IO work item 0x%p\n", _SEH2_GetExceptionCode(), WorkItem.Function); - } - _SEH2_END; - - if (Impersonated) - { - WorkItem.TokenHandle = NULL; - Status = NtSetInformationThread(NtCurrentThread(), - ThreadImpersonationToken, - &WorkItem.TokenHandle, - sizeof(HANDLE)); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to revert worker thread to self!!! Status: 0x%x\n", Status); - } - } - - /* update the requests counter */ - InterlockedDecrement(&ThreadPoolWorkerThreadsRequests); - - if (WorkItem.Flags & WT_EXECUTELONGFUNCTION) - { - InterlockedDecrement(&ThreadPoolWorkerThreadsLongRequests); - } -} - - -static NTSTATUS -RtlpQueueWorkerThread(IN OUT PRTLP_WORKITEM WorkItem) -{ - NTSTATUS Status = STATUS_SUCCESS; - - InterlockedIncrement(&ThreadPoolWorkerThreadsRequests); - - if (WorkItem->Flags & WT_EXECUTELONGFUNCTION) - { - InterlockedIncrement(&ThreadPoolWorkerThreadsLongRequests); - } - - if (WorkItem->Flags & WT_EXECUTEINPERSISTENTTHREAD) - { - Status = RtlpInitializeTimerThread(); - - if (NT_SUCCESS(Status)) - { - /* Queue an APC in the timer thread */ - Status = NtQueueApcThread(TimerThreadHandle, - RtlpExecuteWorkItem, - NULL, - NULL, - WorkItem); - } - } - else - { - /* Queue an IO completion message */ - Status = NtSetIoCompletion(ThreadPoolCompletionPort, - RtlpExecuteWorkItem, - WorkItem, - STATUS_SUCCESS, - 0); - } - - if (!NT_SUCCESS(Status)) - { - InterlockedDecrement(&ThreadPoolWorkerThreadsRequests); - - if (WorkItem->Flags & WT_EXECUTELONGFUNCTION) - { - InterlockedDecrement(&ThreadPoolWorkerThreadsLongRequests); - } - } - - return Status; -} - -static VOID -NTAPI -RtlpExecuteIoWorkItem(IN OUT PVOID NormalContext, - IN OUT PVOID SystemArgument1, - IN OUT PVOID SystemArgument2) -{ - NTSTATUS Status; - BOOLEAN Impersonated = FALSE; - PRTLP_IOWORKERTHREAD IoThread = (PRTLP_IOWORKERTHREAD)NormalContext; - RTLP_WORKITEM WorkItem = *(volatile RTLP_WORKITEM *)SystemArgument2; - - ASSERT(IoThread != NULL); - - RtlFreeHeap(RtlGetProcessHeap(), - 0, - SystemArgument2); - - if (WorkItem.TokenHandle != NULL) - { - Status = NtSetInformationThread(NtCurrentThread(), - ThreadImpersonationToken, - &WorkItem.TokenHandle, - sizeof(HANDLE)); - - NtClose(WorkItem.TokenHandle); - - if (NT_SUCCESS(Status)) - { - Impersonated = TRUE; - } - } - - _SEH2_TRY - { - DPRINT("RtlpExecuteIoWorkItem: Function: 0x%p Context: 0x%p ImpersonationToken: 0x%p\n", WorkItem.Function, WorkItem.Context, WorkItem.TokenHandle); - - /* Execute the function */ - WorkItem.Function(WorkItem.Context); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - DPRINT1("Exception 0x%x while executing IO work item 0x%p\n", _SEH2_GetExceptionCode(), WorkItem.Function); - } - _SEH2_END; - - if (Impersonated) - { - WorkItem.TokenHandle = NULL; - Status = NtSetInformationThread(NtCurrentThread(), - ThreadImpersonationToken, - &WorkItem.TokenHandle, - sizeof(HANDLE)); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to revert worker thread to self!!! Status: 0x%x\n", Status); - } - } - - /* remove the long function flag */ - if (WorkItem.Flags & WT_EXECUTELONGFUNCTION) - { - Status = RtlEnterCriticalSection(&ThreadPoolLock); - if (NT_SUCCESS(Status)) - { - IoThread->Flags &= ~WT_EXECUTELONGFUNCTION; - RtlLeaveCriticalSection(&ThreadPoolLock); - } - } - - /* update the requests counter */ - InterlockedDecrement(&ThreadPoolIOWorkerThreadsRequests); - - if (WorkItem.Flags & WT_EXECUTELONGFUNCTION) - { - InterlockedDecrement(&ThreadPoolIOWorkerThreadsLongRequests); - } -} - -static NTSTATUS -RtlpQueueIoWorkerThread(IN OUT PRTLP_WORKITEM WorkItem) -{ - PLIST_ENTRY CurrentEntry; - PRTLP_IOWORKERTHREAD IoThread = NULL; - NTSTATUS Status = STATUS_SUCCESS; - - if (WorkItem->Flags & WT_EXECUTEINPERSISTENTIOTHREAD) - { - if (PersistentIoThread != NULL) - { - /* We already have a persistent IO worker thread */ - IoThread = PersistentIoThread; - } - else - { - /* We're not aware of any persistent IO worker thread. Search for a unused - worker thread that doesn't have a long function queued */ - CurrentEntry = ThreadPoolIOWorkerThreadsList.Flink; - while (CurrentEntry != &ThreadPoolIOWorkerThreadsList) - { - IoThread = CONTAINING_RECORD(CurrentEntry, - RTLP_IOWORKERTHREAD, - ListEntry); - - if (!(IoThread->Flags & WT_EXECUTELONGFUNCTION)) - break; - - CurrentEntry = CurrentEntry->Flink; - } - - if (CurrentEntry != &ThreadPoolIOWorkerThreadsList) - { - /* Found a worker thread we can use. */ - ASSERT(IoThread != NULL); - - IoThread->Flags |= WT_EXECUTEINPERSISTENTIOTHREAD; - PersistentIoThread = IoThread; - } - else - { - DPRINT1("Failed to find a worker thread for the persistent IO thread!\n"); - return STATUS_NO_MEMORY; - } - } - } - else - { - /* Find a worker thread that is not currently executing a long function */ - CurrentEntry = ThreadPoolIOWorkerThreadsList.Flink; - while (CurrentEntry != &ThreadPoolIOWorkerThreadsList) - { - IoThread = CONTAINING_RECORD(CurrentEntry, - RTLP_IOWORKERTHREAD, - ListEntry); - - if (!(IoThread->Flags & WT_EXECUTELONGFUNCTION)) - { - /* if we're trying to queue a long function then make sure we're not dealing - with the persistent thread */ - if ((WorkItem->Flags & WT_EXECUTELONGFUNCTION) && !(IoThread->Flags & WT_EXECUTEINPERSISTENTIOTHREAD)) - { - /* found a candidate */ - break; - } - } - - CurrentEntry = CurrentEntry->Flink; - } - - if (CurrentEntry == &ThreadPoolIOWorkerThreadsList) - { - /* Couldn't find an appropriate thread, see if we can use the persistent thread (if it exists) for now */ - if (ThreadPoolIOWorkerThreads == 0) - { - DPRINT1("Failed to find a worker thread for the work item 0x%p!\n", WorkItem); - ASSERT(IsListEmpty(&ThreadPoolIOWorkerThreadsList)); - return STATUS_NO_MEMORY; - } - else - { - /* pick the first worker thread */ - CurrentEntry = ThreadPoolIOWorkerThreadsList.Flink; - IoThread = CONTAINING_RECORD(CurrentEntry, - RTLP_IOWORKERTHREAD, - ListEntry); - - /* Since this might be the persistent worker thread, don't run as a - long function */ - WorkItem->Flags &= ~WT_EXECUTELONGFUNCTION; - } - } - - /* Move the picked thread to the end of the list. Since we're always searching - from the beginning, this improves distribution of work items */ - RemoveEntryList(&IoThread->ListEntry); - InsertTailList(&ThreadPoolIOWorkerThreadsList, - &IoThread->ListEntry); - } - - ASSERT(IoThread != NULL); - - InterlockedIncrement(&ThreadPoolIOWorkerThreadsRequests); - - if (WorkItem->Flags & WT_EXECUTELONGFUNCTION) - { - /* We're about to queue a long function, mark the thread */ - IoThread->Flags |= WT_EXECUTELONGFUNCTION; - - InterlockedIncrement(&ThreadPoolIOWorkerThreadsLongRequests); - } - - /* It's time to queue the work item */ - Status = NtQueueApcThread(IoThread->ThreadHandle, - RtlpExecuteIoWorkItem, - IoThread, - NULL, - WorkItem); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to queue APC for work item 0x%p\n", WorkItem->Function); - InterlockedDecrement(&ThreadPoolIOWorkerThreadsRequests); - - if (WorkItem->Flags & WT_EXECUTELONGFUNCTION) - { - InterlockedDecrement(&ThreadPoolIOWorkerThreadsLongRequests); - } - } - - return Status; -} - -static BOOLEAN -RtlpIsIoPending(IN HANDLE ThreadHandle OPTIONAL) -{ - NTSTATUS Status; - ULONG IoPending; - BOOLEAN CreatedHandle = FALSE; - BOOLEAN IsIoPending = TRUE; - - if (ThreadHandle == NULL) - { - Status = NtDuplicateObject(NtCurrentProcess(), - NtCurrentThread(), - NtCurrentProcess(), - &ThreadHandle, - 0, - 0, - DUPLICATE_SAME_ACCESS); - if (!NT_SUCCESS(Status)) - { - return IsIoPending; - } - - CreatedHandle = TRUE; - } - - Status = NtQueryInformationThread(ThreadHandle, - ThreadIsIoPending, - &IoPending, - sizeof(IoPending), - NULL); - if (NT_SUCCESS(Status) && IoPending == 0) - { - IsIoPending = FALSE; - } - - if (CreatedHandle) - { - NtClose(ThreadHandle); - } - - return IsIoPending; -} - -static ULONG -NTAPI -RtlpIoWorkerThreadProc(IN PVOID Parameter) -{ - volatile RTLP_IOWORKERTHREAD ThreadInfo; - LARGE_INTEGER Timeout; - BOOLEAN Terminate; - NTSTATUS Status = STATUS_SUCCESS; - - if (InterlockedIncrement(&ThreadPoolIOWorkerThreads) > MAX_WORKERTHREADS) - { - /* Oops, too many worker threads... */ - goto InitFailed; - } - - /* Get a thread handle to ourselves */ - Status = NtDuplicateObject(NtCurrentProcess(), - NtCurrentThread(), - NtCurrentProcess(), - (PHANDLE)&ThreadInfo.ThreadHandle, - 0, - 0, - DUPLICATE_SAME_ACCESS); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create handle to own thread! Status: 0x%x\n", Status); - -InitFailed: - InterlockedDecrement(&ThreadPoolIOWorkerThreads); - - /* Signal initialization completion */ - InterlockedExchange((PLONG)Parameter, - 1); - - RtlpExitThreadFunc(Status); - return 0; - } - - ThreadInfo.Flags = 0; - - /* Insert the thread into the list */ - InsertHeadList((PLIST_ENTRY)&ThreadPoolIOWorkerThreadsList, - (PLIST_ENTRY)&ThreadInfo.ListEntry); - - /* Signal initialization completion */ - InterlockedExchange((PLONG)Parameter, - 1); - - for (;;) - { - Timeout.QuadPart = -50000000LL; /* Wait for 5 seconds by default */ - -Wait: - do - { - /* Perform an alertable wait, the work items are going to be executed as APCs */ - Status = NtDelayExecution(TRUE, - &Timeout); - - /* Loop as long as we executed an APC */ - } while (Status != STATUS_SUCCESS); - - /* We timed out, let's see if we're allowed to terminate */ - Terminate = FALSE; - - Status = RtlEnterCriticalSection(&ThreadPoolLock); - if (NT_SUCCESS(Status)) - { - if (ThreadInfo.Flags & WT_EXECUTEINPERSISTENTIOTHREAD) - { - /* This thread is supposed to be persistent. Don't terminate! */ - RtlLeaveCriticalSection(&ThreadPoolLock); - - Timeout.QuadPart = -0x7FFFFFFFFFFFFFFFLL; - goto Wait; - } - - /* FIXME - figure out an effective method to determine if it's appropriate to - lower the number of threads. For now let's always terminate if there's - at least one thread and no queued items. */ - Terminate = (*((volatile LONG*)&ThreadPoolIOWorkerThreads) - *((volatile LONG*)&ThreadPoolIOWorkerThreadsLongRequests) >= WORKERTHREAD_CREATION_THRESHOLD) && - (*((volatile LONG*)&ThreadPoolIOWorkerThreadsRequests) == 0); - - if (Terminate) - { - /* Prevent termination as long as IO is pending */ - Terminate = !RtlpIsIoPending(ThreadInfo.ThreadHandle); - } - - if (Terminate) - { - /* Rundown the thread and unlink it from the list */ - InterlockedDecrement(&ThreadPoolIOWorkerThreads); - RemoveEntryList((PLIST_ENTRY)&ThreadInfo.ListEntry); - } - - RtlLeaveCriticalSection(&ThreadPoolLock); - - if (Terminate) - { - /* Break the infinite loop and terminate */ - Status = STATUS_SUCCESS; - break; - } - } - else - { - DPRINT1("Failed to acquire the thread pool lock!!! Status: 0x%x\n", Status); - break; - } - } - - NtClose(ThreadInfo.ThreadHandle); - RtlpExitThreadFunc(Status); - return 0; -} - -static ULONG -NTAPI -RtlpWorkerThreadProc(IN PVOID Parameter) -{ - LARGE_INTEGER Timeout; - BOOLEAN Terminate; - PVOID SystemArgument2; - IO_STATUS_BLOCK IoStatusBlock; - ULONG TimeoutCount = 0; - PKNORMAL_ROUTINE ApcRoutine; - NTSTATUS Status = STATUS_SUCCESS; - - if (InterlockedIncrement(&ThreadPoolWorkerThreads) > MAX_WORKERTHREADS) - { - /* Signal initialization completion */ - InterlockedExchange((PLONG)Parameter, - 1); - - /* Oops, too many worker threads... */ - RtlpExitThreadFunc(Status); - return 0; - } - - /* Signal initialization completion */ - InterlockedExchange((PLONG)Parameter, - 1); - - for (;;) - { - Timeout.QuadPart = -50000000LL; /* Wait for 5 seconds by default */ - - /* Dequeue a completion message */ - Status = NtRemoveIoCompletion(ThreadPoolCompletionPort, - (PVOID*)&ApcRoutine, - &SystemArgument2, - &IoStatusBlock, - &Timeout); - - if (Status == STATUS_SUCCESS) - { - TimeoutCount = 0; - - _SEH2_TRY - { - /* Call the APC routine */ - ApcRoutine(NULL, - (PVOID)IoStatusBlock.Information, - SystemArgument2); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - (void)0; - } - _SEH2_END; - } - else - { - Terminate = FALSE; - - if (!NT_SUCCESS(RtlEnterCriticalSection(&ThreadPoolLock))) - continue; - - /* FIXME - this should be optimized, check if there's requests, etc */ - - if (Status == STATUS_TIMEOUT) - { - /* FIXME - we might want to optimize this */ - if (TimeoutCount++ > 2 && - *((volatile LONG*)&ThreadPoolWorkerThreads) - *((volatile LONG*)&ThreadPoolWorkerThreadsLongRequests) >= WORKERTHREAD_CREATION_THRESHOLD) - { - Terminate = TRUE; - } - } - else - Terminate = TRUE; - - RtlLeaveCriticalSection(&ThreadPoolLock); - - if (Terminate) - { - /* Prevent termination as long as IO is pending */ - Terminate = !RtlpIsIoPending(NULL); - } - - if (Terminate) - { - InterlockedDecrement(&ThreadPoolWorkerThreads); - Status = STATUS_SUCCESS; - break; - } - } - } - - RtlpExitThreadFunc(Status); - return 0; - -} - -/* - * @implemented - */ -NTSTATUS -NTAPI -RtlQueueWorkItem(IN WORKERCALLBACKFUNC Function, - IN PVOID Context OPTIONAL, - IN ULONG Flags) -{ - LONG FreeWorkers; - NTSTATUS Status; - PRTLP_WORKITEM WorkItem; - - DPRINT("RtlQueueWorkItem(0x%p, 0x%p, 0x%x)\n", Function, Context, Flags); - - /* Initialize the thread pool if not already initialized */ - if (!IsThreadPoolInitialized()) - { - Status = RtlpInitializeThreadPool(); - - if (!NT_SUCCESS(Status)) - return Status; - } - - /* Allocate a work item */ - WorkItem = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - sizeof(RTLP_WORKITEM)); - if (WorkItem == NULL) - return STATUS_NO_MEMORY; - - WorkItem->Function = Function; - WorkItem->Context = Context; - WorkItem->Flags = Flags; - - if (Flags & WT_TRANSFER_IMPERSONATION) - { - Status = RtlpGetImpersonationToken(&WorkItem->TokenHandle); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to get impersonation token! Status: 0x%x\n", Status); - goto Cleanup; - } - } - else - WorkItem->TokenHandle = NULL; - - Status = RtlEnterCriticalSection(&ThreadPoolLock); - if (NT_SUCCESS(Status)) - { - if (Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINUITHREAD | WT_EXECUTEINPERSISTENTIOTHREAD)) - { - /* FIXME - We should optimize the algorithm used to determine whether to grow the thread pool! */ - - FreeWorkers = ThreadPoolIOWorkerThreads - ThreadPoolIOWorkerThreadsLongRequests; - - if (((Flags & (WT_EXECUTEINPERSISTENTIOTHREAD | WT_EXECUTELONGFUNCTION)) == WT_EXECUTELONGFUNCTION) && - PersistentIoThread != NULL) - { - /* We shouldn't queue a long function into the persistent IO thread */ - FreeWorkers--; - } - - /* See if it's a good idea to grow the pool */ - if (ThreadPoolIOWorkerThreads < MAX_WORKERTHREADS && - (FreeWorkers <= 0 || ThreadPoolIOWorkerThreads - ThreadPoolIOWorkerThreadsRequests < WORKERTHREAD_CREATION_THRESHOLD)) - { - /* Grow the thread pool */ - Status = RtlpStartWorkerThread(RtlpIoWorkerThreadProc); - - if (!NT_SUCCESS(Status) && *((volatile LONG*)&ThreadPoolIOWorkerThreads) != 0) - { - /* We failed to create the thread, but there's at least one there so - we can at least queue the request */ - Status = STATUS_SUCCESS; - } - } - - if (NT_SUCCESS(Status)) - { - /* Queue a IO worker thread */ - Status = RtlpQueueIoWorkerThread(WorkItem); - } - } - else - { - /* FIXME - We should optimize the algorithm used to determine whether to grow the thread pool! */ - - FreeWorkers = ThreadPoolWorkerThreads - ThreadPoolWorkerThreadsLongRequests; - - /* See if it's a good idea to grow the pool */ - if (ThreadPoolWorkerThreads < MAX_WORKERTHREADS && - (FreeWorkers <= 0 || ThreadPoolWorkerThreads - ThreadPoolWorkerThreadsRequests < WORKERTHREAD_CREATION_THRESHOLD)) - { - /* Grow the thread pool */ - Status = RtlpStartWorkerThread(RtlpWorkerThreadProc); - - if (!NT_SUCCESS(Status) && *((volatile LONG*)&ThreadPoolWorkerThreads) != 0) - { - /* We failed to create the thread, but there's at least one there so - we can at least queue the request */ - Status = STATUS_SUCCESS; - } - } - - if (NT_SUCCESS(Status)) - { - /* Queue a normal worker thread */ - Status = RtlpQueueWorkerThread(WorkItem); - } - } - - RtlLeaveCriticalSection(&ThreadPoolLock); - } - - if (!NT_SUCCESS(Status)) - { - if (WorkItem->TokenHandle != NULL) - { - NtClose(WorkItem->TokenHandle); - } - -Cleanup: - RtlFreeHeap(RtlGetProcessHeap(), - 0, - WorkItem); - } - - return Status; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -RtlSetIoCompletionCallback(IN HANDLE FileHandle, - IN PIO_APC_ROUTINE Callback, - IN ULONG Flags) -{ - IO_STATUS_BLOCK IoStatusBlock; - FILE_COMPLETION_INFORMATION FileCompletionInfo; - NTSTATUS Status; - - DPRINT("RtlSetIoCompletionCallback(0x%p, 0x%p, 0x%x)\n", FileHandle, Callback, Flags); - - /* Initialize the thread pool if not already initialized */ - if (!IsThreadPoolInitialized()) - { - Status = RtlpInitializeThreadPool(); - if (!NT_SUCCESS(Status)) - return Status; - } - - FileCompletionInfo.Port = ThreadPoolCompletionPort; - FileCompletionInfo.Key = (PVOID)Callback; - - Status = NtSetInformationFile(FileHandle, - &IoStatusBlock, - &FileCompletionInfo, - sizeof(FileCompletionInfo), - FileCompletionInformation); - - return Status; -} - /* * @implemented */