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
Test_Init(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
ThreadProc1(_In_ LPVOID lpParameter)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
ThreadProc2(_In_ LPVOID lpParameter)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
Test_Acquire(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
START_TEST(RtlCriticalSection)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