https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1d59cf43af3ad38ea853b883468358f648d6da97
commit 1d59cf43af3ad38ea853b883468358f648d6da97 Author: Timo Kreuzer <timo.kreu...@reactos.org> AuthorDate: Mon Aug 14 22:07:15 2023 +0300 Commit: Timo Kreuzer <timo.kreu...@reactos.org> CommitDate: Thu Sep 7 08:36:48 2023 +0300 [NTDLL_APITEST] Add tests for critical sections --- modules/rostests/apitests/ntdll/CMakeLists.txt | 1 + .../rostests/apitests/ntdll/RtlCriticalSection.c | 411 +++++++++++++++++++++ modules/rostests/apitests/ntdll/testlist.c | 2 + sdk/include/xdk/winnt_old.h | 14 +- 4 files changed, 427 insertions(+), 1 deletion(-) diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 14c12de120e..85515a2efaf 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -59,6 +59,7 @@ list(APPEND SOURCE RtlBitmap.c RtlComputePrivatizedDllName_U.c RtlCopyMappedMemory.c + RtlCriticalSection.c RtlDebugInformation.c RtlDeleteAce.c RtlDetermineDosPathNameType.c diff --git a/modules/rostests/apitests/ntdll/RtlCriticalSection.c b/modules/rostests/apitests/ntdll/RtlCriticalSection.c new file mode 100644 index 00000000000..920cc3a97a3 --- /dev/null +++ b/modules/rostests/apitests/ntdll/RtlCriticalSection.c @@ -0,0 +1,411 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Test for Rtl Critical Section API + * COPYRIGHT: Copyright 2023 Timo Kreuzer <timo.kreu...@reactos.org> + */ + +#include "precomp.h" +#include <pseh/pseh2.h> + +SYSTEM_INFO g_SysInfo; +OSVERSIONINFOEXA g_VerInfo; +ULONG g_Version; +ULONG g_DefaultSpinCount; + +typedef +NTSTATUS +NTAPI +FN_RtlInitializeCriticalSectionEx( + _Out_ PRTL_CRITICAL_SECTION CriticalSection, + _In_ ULONG SpinCount, + _In_ ULONG Flags); + +FN_RtlInitializeCriticalSectionEx* pfnRtlInitializeCriticalSectionEx; +RTL_CRITICAL_SECTION CritSect; +HANDLE hEventThread1Ready, hEventThread1Cont; +HANDLE hEventThread2Ready, hEventThread2Cont; + +static +void +Test_Init(void) +{ + NTSTATUS Status; + BOOL HasDebugInfo = (g_Version <= _WIN32_WINNT_VISTA); + + _SEH2_TRY + { + RtlInitializeCriticalSection(NULL); + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); + + Status = RtlInitializeCriticalSection(&CritSect); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_long(CritSect.LockCount, -1); + ok_long(CritSect.RecursionCount, 0); + ok_ptr(CritSect.OwningThread, NULL); + ok_ptr(CritSect.LockSemaphore, NULL); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + if (HasDebugInfo) + { + ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); + ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + } + else + { + ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + } + + Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_long(CritSect.LockCount, -1); + ok_long(CritSect.RecursionCount, 0); + ok_ptr(CritSect.OwningThread, NULL); + ok_ptr(CritSect.LockSemaphore, NULL); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + if (HasDebugInfo) + { + ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); + ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + } + else + { + ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + } + + Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0xFF000000); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + + Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0x1234); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? 0x1234 : 0); + + if (pfnRtlInitializeCriticalSectionEx != NULL) + { + _SEH2_TRY + { + pfnRtlInitializeCriticalSectionEx(NULL, 0, 0); + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, 0); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_long(CritSect.LockCount, -1); + ok_long(CritSect.RecursionCount, 0); + ok_ptr(CritSect.OwningThread, NULL); + ok_ptr(CritSect.LockSemaphore, NULL); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + if (HasDebugInfo) + { + ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); + ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + if ((CritSect.DebugInfo != NULL) && (CritSect.DebugInfo != LongToPtr(-1))) + { + ok_int(CritSect.DebugInfo->Type, 0); + ok_int(CritSect.DebugInfo->CreatorBackTraceIndex, 0); + ok_int(CritSect.DebugInfo->CreatorBackTraceIndexHigh, 0); + ok_ptr(CritSect.DebugInfo->CriticalSection, &CritSect); + ok(CritSect.DebugInfo->ProcessLocksList.Flink != NULL, "Flink is NULL\n"); + ok(CritSect.DebugInfo->ProcessLocksList.Blink != NULL, "Blink is NULL\n"); + if ((CritSect.DebugInfo->ProcessLocksList.Flink != NULL) && + (CritSect.DebugInfo->ProcessLocksList.Blink != NULL)) + { + ok_ptr(CritSect.DebugInfo->ProcessLocksList.Flink->Blink, + &CritSect.DebugInfo->ProcessLocksList); + ok_ptr(CritSect.DebugInfo->ProcessLocksList.Blink->Flink, + &CritSect.DebugInfo->ProcessLocksList); + } + ok_long(CritSect.DebugInfo->EntryCount, 0); + ok_long(CritSect.DebugInfo->ContentionCount, 0); + ok_long(CritSect.DebugInfo->Flags, 0); + ok_int(CritSect.DebugInfo->SpareWORD, 0); + } + } + else + { + ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + } + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x00FFFFFF, 0); + ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? 0x00FFFFFF : 0); + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x01000000, 0); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x80000000, 0); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); + + _SEH2_TRY + { + Status = pfnRtlInitializeCriticalSectionEx(NULL, 0x12345678, 0); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); + + for (ULONG i = 0; i < 32; i++) + { + ULONG Flags = 1UL << i; + ULONG AllowedFlags = 0x07FFFFFF; + if (g_Version >= _WIN32_WINNT_WIN7) AllowedFlags |= 0x18000000; + NTSTATUS ExpectedStatus = (Flags & ~AllowedFlags) ? + STATUS_INVALID_PARAMETER_3 : STATUS_SUCCESS; + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, Flags); + ok(Status == ExpectedStatus, "Wrong Status (0x%lx) for Flags 0x%lx\n", + Status, Flags); + if (NT_SUCCESS(Status)) + { + ULONG SetFlags = Flags & 0x08000000; + if (g_Version >= _WIN32_WINNT_WIN7) SetFlags |= (Flags & 0x03000000); + ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? g_DefaultSpinCount | SetFlags : SetFlags); + } + } + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, 0xFFFFFFFF); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); + _SEH2_TRY + { + Status = pfnRtlInitializeCriticalSectionEx(NULL, 0, 0xFFFFFFFF); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x12345678, 0xFFFFFFFF); + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); + + Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + if (g_Version >= _WIN32_WINNT_WIN7) + { + ok_ntstatus(Status, STATUS_SUCCESS); + ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); + ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); + if ((CritSect.DebugInfo != NULL) && (CritSect.DebugInfo != LongToPtr(-1))) + { + ok_int(CritSect.DebugInfo->Type, 0); + ok_int(CritSect.DebugInfo->CreatorBackTraceIndex, 0); + ok_int(CritSect.DebugInfo->CreatorBackTraceIndexHigh, 0); + ok_ptr(CritSect.DebugInfo->CriticalSection, &CritSect); + ok(CritSect.DebugInfo->ProcessLocksList.Flink != NULL, "Flink is NULL\n"); + ok(CritSect.DebugInfo->ProcessLocksList.Blink != NULL, "Blink is NULL\n"); + if ((CritSect.DebugInfo->ProcessLocksList.Flink != NULL) && + (CritSect.DebugInfo->ProcessLocksList.Blink != NULL)) + { + ok_ptr(CritSect.DebugInfo->ProcessLocksList.Flink->Blink, + &CritSect.DebugInfo->ProcessLocksList); + ok_ptr(CritSect.DebugInfo->ProcessLocksList.Blink->Flink, + &CritSect.DebugInfo->ProcessLocksList); + } + ok_long(CritSect.DebugInfo->EntryCount, 0); + ok_long(CritSect.DebugInfo->ContentionCount, 0); + ok_long(CritSect.DebugInfo->Flags, 0); + ok_int(CritSect.DebugInfo->SpareWORD, 0); + } + } + else + { + ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); + } + } + else + { + skip("RtlInitializeCriticalSectionEx not available.\n"); + } +} + +static +DWORD +WINAPI +ThreadProc1( + _In_ LPVOID lpParameter) +{ + printf("ThreadProc1 starting\n"); + RtlEnterCriticalSection(&CritSect); + + SetEvent(hEventThread1Ready); + + printf("ThreadProc1 waiting\n"); + WaitForSingleObject(hEventThread1Cont, INFINITE); + printf("ThreadProc1 returned from wait\n"); + + RtlLeaveCriticalSection(&CritSect); + + return 0; +} + +static +DWORD +WINAPI +ThreadProc2( + _In_ LPVOID lpParameter) +{ + printf("ThreadProc2 starting\n"); + RtlEnterCriticalSection(&CritSect); + + SetEvent(hEventThread2Ready); + + printf("ThreadProc2 waiting\n"); + WaitForSingleObject(hEventThread2Cont, INFINITE); + printf("ThreadProc2 returned from wait\n"); + + RtlLeaveCriticalSection(&CritSect); + + return 0; +} + +static +void +Test_Acquire(void) +{ + DWORD dwThreadId1, dwThreadId2; + HANDLE hThread1, hThread2; + + RtlInitializeCriticalSection(&CritSect); + + // Acquire once + RtlEnterCriticalSection(&CritSect); + ok_long(CritSect.LockCount, -2); + ok_long(CritSect.RecursionCount, 1); + ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); + ok_ptr(CritSect.LockSemaphore, NULL); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + + // Acquire recursively + RtlEnterCriticalSection(&CritSect); + ok_long(CritSect.LockCount, -2); + ok_long(CritSect.RecursionCount, 2); + ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); + ok_ptr(CritSect.LockSemaphore, NULL); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + + hEventThread1Ready = CreateEvent(NULL, TRUE, FALSE, NULL); + hEventThread1Cont = CreateEvent(NULL, TRUE, FALSE, NULL); + hEventThread2Ready = CreateEvent(NULL, TRUE, FALSE, NULL); + hEventThread2Cont = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Create thread 1 and wait to it time to try to acquire the critical section + hThread1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, &dwThreadId1); + + // Wait up to 10 s + for (ULONG i = 0; (CritSect.LockCount == -2) && (i < 1000); i++) + { + Sleep(10); + } + + ok_long(CritSect.LockCount, -6); + ok_long(CritSect.RecursionCount, 2); + ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); + //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); // TODO: this behaves differently on different OS versions + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + + // Create thread 2 and wait to it time to try to acquire the critical section + hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, &dwThreadId2); + + // Wait up to 10 s + for (ULONG i = 0; (CritSect.LockCount == -6) && (i < 1000); i++) + { + Sleep(10); + } + + ok_long(CritSect.LockCount, -10); + ok_long(CritSect.RecursionCount, 2); + ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); + //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + + RtlLeaveCriticalSection(&CritSect); + ok_long(CritSect.LockCount, -10); + ok_long(CritSect.RecursionCount, 1); + RtlLeaveCriticalSection(&CritSect); + + // Wait until thread 1 has acquired the critical section + WaitForSingleObject(hEventThread1Ready, INFINITE); + + ok_long(CritSect.LockCount, -6); + ok_long(CritSect.RecursionCount, 1); + ok_ptr(CritSect.OwningThread, UlongToHandle(dwThreadId1)); + //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); + if (g_DefaultSpinCount != 0) + { + ok(CritSect.SpinCount <= g_DefaultSpinCount, "SpinCount increased\n"); + } + else + { + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + } + + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount ? g_DefaultSpinCount - 1 : 0); + + // Release thread 1, wait for thread 2 to acquire the critical section + SetEvent(hEventThread1Cont); + WaitForSingleObject(hEventThread2Ready, INFINITE); + + ok_long(CritSect.LockCount, -2); + ok_long(CritSect.RecursionCount, 1); + ok_ptr(CritSect.OwningThread, UlongToHandle(dwThreadId2)); + //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); + if (g_DefaultSpinCount != 0) + { + ok(CritSect.SpinCount <= g_DefaultSpinCount, "SpinCount increased\n"); + } + else + { + ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); + } + + // Release thread 2 + SetEvent(hEventThread2Cont); + + // To make Thomas happy :) + WaitForSingleObject(hThread1, INFINITE); + WaitForSingleObject(hThread2, INFINITE); +} + +START_TEST(RtlCriticalSection) +{ + HMODULE hmodNtDll = GetModuleHandleA("ntdll.dll"); + pfnRtlInitializeCriticalSectionEx = (FN_RtlInitializeCriticalSectionEx*) + GetProcAddress(hmodNtDll, "RtlInitializeCriticalSectionEx"); + + g_VerInfo.dwOSVersionInfoSize = sizeof(g_VerInfo); + GetVersionExA((LPOSVERSIONINFOA)&g_VerInfo); + g_Version = g_VerInfo.dwMajorVersion << 8 | g_VerInfo.dwMinorVersion; + printf("g_VerInfo: %lu.%lu.%lu ('%s')\n ", + g_VerInfo.dwMajorVersion, + g_VerInfo.dwMinorVersion, + g_VerInfo.dwBuildNumber, + g_VerInfo.szCSDVersion); + GetSystemInfo(&g_SysInfo); + printf("g_SysInfo.dwNumberOfProcessors = %lu\n", g_SysInfo.dwNumberOfProcessors); + + if ((g_Version >= _WIN32_WINNT_VISTA) && (g_SysInfo.dwNumberOfProcessors > 1)) + { + g_DefaultSpinCount = 0x20007d0; + } + else + { + g_DefaultSpinCount = 0; + } + + Test_Init(); + Test_Acquire(); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 7f80f42420b..2be58938238 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -55,6 +55,7 @@ extern void func_RtlAllocateHeap(void); extern void func_RtlBitmap(void); extern void func_RtlComputePrivatizedDllName_U(void); extern void func_RtlCopyMappedMemory(void); +extern void func_RtlCriticalSection(void); extern void func_RtlDebugInformation(void); extern void func_RtlDeleteAce(void); extern void func_RtlDetermineDosPathNameType(void); @@ -150,6 +151,7 @@ const struct test winetest_testlist[] = { "RtlBitmapApi", func_RtlBitmap }, { "RtlComputePrivatizedDllName_U", func_RtlComputePrivatizedDllName_U }, { "RtlCopyMappedMemory", func_RtlCopyMappedMemory }, + { "RtlCriticalSection", func_RtlCriticalSection }, { "RtlDebugInformation", func_RtlDebugInformation }, { "RtlDeleteAce", func_RtlDeleteAce }, { "RtlDetermineDosPathNameType", func_RtlDetermineDosPathNameType }, diff --git a/sdk/include/xdk/winnt_old.h b/sdk/include/xdk/winnt_old.h index 7e564277b9a..15d353fc9ef 100644 --- a/sdk/include/xdk/winnt_old.h +++ b/sdk/include/xdk/winnt_old.h @@ -1116,7 +1116,19 @@ typedef VOID (NTAPI *WORKERCALLBACKFUNC)(PVOID); #define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define IO_REPARSE_TAG_SYMLINK 0xA000000CL -#define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO 0x01000000 +#define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO 0x01000000 +#define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 0x02000000 +#define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT 0x04000000 +#define RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE 0x08000000 +#define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO 0x10000000 +#define RTL_CRITICAL_SECTION_ALL_FLAG_BITS 0xFF000000 +#define RTL_CRITICAL_SECTION_FLAG_RESERVED \ + (RTL_CRITICAL_SECTION_ALL_FLAG_BITS & \ + (~(RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO | \ + RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN | \ + RTL_CRITICAL_SECTION_FLAG_STATIC_INIT | \ + RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE | \ + RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO))) #ifndef RC_INVOKED