1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Test for Rtl Critical Section API 5 * COPYRIGHT: Copyright 2023 Timo Kreuzer <timo.kreuzer@reactos.org> 6 */ 7 8 #include "precomp.h" 9 #include <pseh/pseh2.h> 10 11 SYSTEM_INFO g_SysInfo; 12 OSVERSIONINFOEXA g_VerInfo; 13 ULONG g_Version; 14 ULONG g_DefaultSpinCount; 15 16 typedef 17 NTSTATUS 18 NTAPI 19 FN_RtlInitializeCriticalSectionEx( 20 _Out_ PRTL_CRITICAL_SECTION CriticalSection, 21 _In_ ULONG SpinCount, 22 _In_ ULONG Flags); 23 24 FN_RtlInitializeCriticalSectionEx* pfnRtlInitializeCriticalSectionEx; 25 RTL_CRITICAL_SECTION CritSect; 26 HANDLE hEventThread1Ready, hEventThread1Cont; 27 HANDLE hEventThread2Ready, hEventThread2Cont; 28 29 static 30 void 31 Test_Init(void) 32 { 33 NTSTATUS Status; 34 BOOL HasDebugInfo = (g_Version <= _WIN32_WINNT_VISTA); 35 36 _SEH2_TRY 37 { 38 RtlInitializeCriticalSection(NULL); 39 Status = STATUS_SUCCESS; 40 } 41 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 42 { 43 Status = _SEH2_GetExceptionCode(); 44 } 45 _SEH2_END; 46 ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); 47 48 Status = RtlInitializeCriticalSection(&CritSect); 49 ok_ntstatus(Status, STATUS_SUCCESS); 50 ok_long(CritSect.LockCount, -1); 51 ok_long(CritSect.RecursionCount, 0); 52 ok_ptr(CritSect.OwningThread, NULL); 53 ok_ptr(CritSect.LockSemaphore, NULL); 54 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 55 if (HasDebugInfo) 56 { 57 ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); 58 ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 59 } 60 else 61 { 62 ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 63 } 64 65 Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0); 66 ok_ntstatus(Status, STATUS_SUCCESS); 67 ok_long(CritSect.LockCount, -1); 68 ok_long(CritSect.RecursionCount, 0); 69 ok_ptr(CritSect.OwningThread, NULL); 70 ok_ptr(CritSect.LockSemaphore, NULL); 71 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 72 if (HasDebugInfo) 73 { 74 ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); 75 ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 76 } 77 else 78 { 79 ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 80 } 81 82 Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0xFF000000); 83 ok_ntstatus(Status, STATUS_SUCCESS); 84 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 85 86 Status = RtlInitializeCriticalSectionAndSpinCount(&CritSect, 0x1234); 87 ok_ntstatus(Status, STATUS_SUCCESS); 88 ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? 0x1234 : 0); 89 90 if (pfnRtlInitializeCriticalSectionEx != NULL) 91 { 92 _SEH2_TRY 93 { 94 pfnRtlInitializeCriticalSectionEx(NULL, 0, 0); 95 Status = STATUS_SUCCESS; 96 } 97 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 98 { 99 Status = _SEH2_GetExceptionCode(); 100 } 101 _SEH2_END; 102 ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); 103 104 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, 0); 105 ok_ntstatus(Status, STATUS_SUCCESS); 106 ok_long(CritSect.LockCount, -1); 107 ok_long(CritSect.RecursionCount, 0); 108 ok_ptr(CritSect.OwningThread, NULL); 109 ok_ptr(CritSect.LockSemaphore, NULL); 110 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 111 if (HasDebugInfo) 112 { 113 ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); 114 ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 115 if ((CritSect.DebugInfo != NULL) && (CritSect.DebugInfo != LongToPtr(-1))) 116 { 117 ok_int(CritSect.DebugInfo->Type, 0); 118 ok_int(CritSect.DebugInfo->CreatorBackTraceIndex, 0); 119 ok_int(CritSect.DebugInfo->CreatorBackTraceIndexHigh, 0); 120 ok_ptr(CritSect.DebugInfo->CriticalSection, &CritSect); 121 ok(CritSect.DebugInfo->ProcessLocksList.Flink != NULL, "Flink is NULL\n"); 122 ok(CritSect.DebugInfo->ProcessLocksList.Blink != NULL, "Blink is NULL\n"); 123 if ((CritSect.DebugInfo->ProcessLocksList.Flink != NULL) && 124 (CritSect.DebugInfo->ProcessLocksList.Blink != NULL)) 125 { 126 ok_ptr(CritSect.DebugInfo->ProcessLocksList.Flink->Blink, 127 &CritSect.DebugInfo->ProcessLocksList); 128 ok_ptr(CritSect.DebugInfo->ProcessLocksList.Blink->Flink, 129 &CritSect.DebugInfo->ProcessLocksList); 130 } 131 ok_long(CritSect.DebugInfo->EntryCount, 0); 132 ok_long(CritSect.DebugInfo->ContentionCount, 0); 133 ok_long(CritSect.DebugInfo->Flags, 0); 134 ok_int(CritSect.DebugInfo->SpareWORD, 0); 135 } 136 } 137 else 138 { 139 ok(CritSect.DebugInfo == LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 140 } 141 142 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x00FFFFFF, 0); 143 ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? 0x00FFFFFF : 0); 144 145 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x01000000, 0); 146 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); 147 148 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x80000000, 0); 149 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); 150 151 _SEH2_TRY 152 { 153 Status = pfnRtlInitializeCriticalSectionEx(NULL, 0x12345678, 0); 154 } 155 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 156 { 157 Status = _SEH2_GetExceptionCode(); 158 } 159 _SEH2_END; 160 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_2); 161 162 for (ULONG i = 0; i < 32; i++) 163 { 164 ULONG Flags = 1UL << i; 165 ULONG AllowedFlags = 0x07FFFFFF; 166 if (g_Version >= _WIN32_WINNT_WIN7) AllowedFlags |= 0x18000000; 167 NTSTATUS ExpectedStatus = (Flags & ~AllowedFlags) ? 168 STATUS_INVALID_PARAMETER_3 : STATUS_SUCCESS; 169 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, Flags); 170 ok(Status == ExpectedStatus, "Wrong Status (0x%lx) for Flags 0x%lx\n", 171 Status, Flags); 172 if (NT_SUCCESS(Status)) 173 { 174 ULONG SetFlags = Flags & 0x08000000; 175 if (g_Version >= _WIN32_WINNT_WIN7) SetFlags |= (Flags & 0x03000000); 176 ok_size_t(CritSect.SpinCount, (g_SysInfo.dwNumberOfProcessors > 1) ? g_DefaultSpinCount | SetFlags : SetFlags); 177 } 178 } 179 180 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, 0xFFFFFFFF); 181 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); 182 _SEH2_TRY 183 { 184 Status = pfnRtlInitializeCriticalSectionEx(NULL, 0, 0xFFFFFFFF); 185 } 186 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 187 { 188 Status = _SEH2_GetExceptionCode(); 189 } 190 _SEH2_END; 191 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); 192 193 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0x12345678, 0xFFFFFFFF); 194 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); 195 196 Status = pfnRtlInitializeCriticalSectionEx(&CritSect, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); 197 if (g_Version >= _WIN32_WINNT_WIN7) 198 { 199 ok_ntstatus(Status, STATUS_SUCCESS); 200 ok(CritSect.DebugInfo != NULL, "DebugInfo is %p\n", CritSect.DebugInfo); 201 ok(CritSect.DebugInfo != LongToPtr(-1), "DebugInfo is %p\n", CritSect.DebugInfo); 202 if ((CritSect.DebugInfo != NULL) && (CritSect.DebugInfo != LongToPtr(-1))) 203 { 204 ok_int(CritSect.DebugInfo->Type, 0); 205 ok_int(CritSect.DebugInfo->CreatorBackTraceIndex, 0); 206 ok_int(CritSect.DebugInfo->CreatorBackTraceIndexHigh, 0); 207 ok_ptr(CritSect.DebugInfo->CriticalSection, &CritSect); 208 ok(CritSect.DebugInfo->ProcessLocksList.Flink != NULL, "Flink is NULL\n"); 209 ok(CritSect.DebugInfo->ProcessLocksList.Blink != NULL, "Blink is NULL\n"); 210 if ((CritSect.DebugInfo->ProcessLocksList.Flink != NULL) && 211 (CritSect.DebugInfo->ProcessLocksList.Blink != NULL)) 212 { 213 ok_ptr(CritSect.DebugInfo->ProcessLocksList.Flink->Blink, 214 &CritSect.DebugInfo->ProcessLocksList); 215 ok_ptr(CritSect.DebugInfo->ProcessLocksList.Blink->Flink, 216 &CritSect.DebugInfo->ProcessLocksList); 217 } 218 ok_long(CritSect.DebugInfo->EntryCount, 0); 219 ok_long(CritSect.DebugInfo->ContentionCount, 0); 220 ok_long(CritSect.DebugInfo->Flags, 0); 221 ok_int(CritSect.DebugInfo->SpareWORD, 0); 222 } 223 } 224 else 225 { 226 ok_ntstatus(Status, STATUS_INVALID_PARAMETER_3); 227 } 228 } 229 else 230 { 231 skip("RtlInitializeCriticalSectionEx not available.\n"); 232 } 233 } 234 235 static 236 DWORD 237 WINAPI 238 ThreadProc1( 239 _In_ LPVOID lpParameter) 240 { 241 printf("ThreadProc1 starting\n"); 242 RtlEnterCriticalSection(&CritSect); 243 244 SetEvent(hEventThread1Ready); 245 246 printf("ThreadProc1 waiting\n"); 247 WaitForSingleObject(hEventThread1Cont, INFINITE); 248 printf("ThreadProc1 returned from wait\n"); 249 250 RtlLeaveCriticalSection(&CritSect); 251 252 return 0; 253 } 254 255 static 256 DWORD 257 WINAPI 258 ThreadProc2( 259 _In_ LPVOID lpParameter) 260 { 261 printf("ThreadProc2 starting\n"); 262 RtlEnterCriticalSection(&CritSect); 263 264 SetEvent(hEventThread2Ready); 265 266 printf("ThreadProc2 waiting\n"); 267 WaitForSingleObject(hEventThread2Cont, INFINITE); 268 printf("ThreadProc2 returned from wait\n"); 269 270 RtlLeaveCriticalSection(&CritSect); 271 272 return 0; 273 } 274 275 static 276 void 277 Test_Acquire(void) 278 { 279 DWORD dwThreadId1, dwThreadId2; 280 HANDLE hThread1, hThread2; 281 282 RtlInitializeCriticalSection(&CritSect); 283 284 // Acquire once 285 RtlEnterCriticalSection(&CritSect); 286 ok_long(CritSect.LockCount, -2); 287 ok_long(CritSect.RecursionCount, 1); 288 ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); 289 ok_ptr(CritSect.LockSemaphore, NULL); 290 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 291 292 // Acquire recursively 293 RtlEnterCriticalSection(&CritSect); 294 ok_long(CritSect.LockCount, -2); 295 ok_long(CritSect.RecursionCount, 2); 296 ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); 297 ok_ptr(CritSect.LockSemaphore, NULL); 298 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 299 300 hEventThread1Ready = CreateEvent(NULL, TRUE, FALSE, NULL); 301 hEventThread1Cont = CreateEvent(NULL, TRUE, FALSE, NULL); 302 hEventThread2Ready = CreateEvent(NULL, TRUE, FALSE, NULL); 303 hEventThread2Cont = CreateEvent(NULL, TRUE, FALSE, NULL); 304 305 // Create thread 1 and wait to it time to try to acquire the critical section 306 hThread1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, &dwThreadId1); 307 308 // Wait up to 10 s 309 for (ULONG i = 0; (CritSect.LockCount == -2) && (i < 1000); i++) 310 { 311 Sleep(10); 312 } 313 314 ok_long(CritSect.LockCount, -6); 315 ok_long(CritSect.RecursionCount, 2); 316 ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); 317 //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); // TODO: this behaves differently on different OS versions 318 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 319 320 // Create thread 2 and wait to it time to try to acquire the critical section 321 hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, &dwThreadId2); 322 323 // Wait up to 10 s 324 for (ULONG i = 0; (CritSect.LockCount == -6) && (i < 1000); i++) 325 { 326 Sleep(10); 327 } 328 329 ok_long(CritSect.LockCount, -10); 330 ok_long(CritSect.RecursionCount, 2); 331 ok_ptr(CritSect.OwningThread, UlongToHandle(GetCurrentThreadId())); 332 //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); 333 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 334 335 RtlLeaveCriticalSection(&CritSect); 336 ok_long(CritSect.LockCount, -10); 337 ok_long(CritSect.RecursionCount, 1); 338 RtlLeaveCriticalSection(&CritSect); 339 340 // Wait until thread 1 has acquired the critical section 341 WaitForSingleObject(hEventThread1Ready, INFINITE); 342 343 ok_long(CritSect.LockCount, -6); 344 ok_long(CritSect.RecursionCount, 1); 345 ok_ptr(CritSect.OwningThread, UlongToHandle(dwThreadId1)); 346 //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); 347 if (g_DefaultSpinCount != 0) 348 { 349 ok(CritSect.SpinCount <= g_DefaultSpinCount, "SpinCount increased\n"); 350 } 351 else 352 { 353 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 354 } 355 356 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount ? g_DefaultSpinCount - 1 : 0); 357 358 // Release thread 1, wait for thread 2 to acquire the critical section 359 SetEvent(hEventThread1Cont); 360 WaitForSingleObject(hEventThread2Ready, INFINITE); 361 362 ok_long(CritSect.LockCount, -2); 363 ok_long(CritSect.RecursionCount, 1); 364 ok_ptr(CritSect.OwningThread, UlongToHandle(dwThreadId2)); 365 //ok_ptr(CritSect.LockSemaphore, LongToPtr(-1)); 366 if (g_DefaultSpinCount != 0) 367 { 368 ok(CritSect.SpinCount <= g_DefaultSpinCount, "SpinCount increased\n"); 369 } 370 else 371 { 372 ok_size_t(CritSect.SpinCount, g_DefaultSpinCount); 373 } 374 375 // Release thread 2 376 SetEvent(hEventThread2Cont); 377 378 // To make Thomas happy :) 379 WaitForSingleObject(hThread1, INFINITE); 380 WaitForSingleObject(hThread2, INFINITE); 381 } 382 383 START_TEST(RtlCriticalSection) 384 { 385 HMODULE hmodNtDll = GetModuleHandleA("ntdll.dll"); 386 pfnRtlInitializeCriticalSectionEx = (FN_RtlInitializeCriticalSectionEx*) 387 GetProcAddress(hmodNtDll, "RtlInitializeCriticalSectionEx"); 388 389 g_VerInfo.dwOSVersionInfoSize = sizeof(g_VerInfo); 390 GetVersionExA((LPOSVERSIONINFOA)&g_VerInfo); 391 g_Version = g_VerInfo.dwMajorVersion << 8 | g_VerInfo.dwMinorVersion; 392 printf("g_VerInfo: %lu.%lu.%lu ('%s')\n ", 393 g_VerInfo.dwMajorVersion, 394 g_VerInfo.dwMinorVersion, 395 g_VerInfo.dwBuildNumber, 396 g_VerInfo.szCSDVersion); 397 GetSystemInfo(&g_SysInfo); 398 printf("g_SysInfo.dwNumberOfProcessors = %lu\n", g_SysInfo.dwNumberOfProcessors); 399 400 if ((g_Version >= _WIN32_WINNT_VISTA) && (g_SysInfo.dwNumberOfProcessors > 1)) 401 { 402 g_DefaultSpinCount = 0x20007d0; 403 } 404 else 405 { 406 g_DefaultSpinCount = 0; 407 } 408 409 Test_Init(); 410 Test_Acquire(); 411 } 412