FreeRDP
Loading...
Searching...
No Matches
TestSynchCritical.c
1
2#include <stdio.h>
3#include <winpr/crt.h>
4#include <winpr/crypto.h>
5#include <winpr/windows.h>
6#include <winpr/synch.h>
7#include <winpr/sysinfo.h>
8#include <winpr/thread.h>
9#include <winpr/interlocked.h>
10
11#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 100
12#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
13
14static CRITICAL_SECTION critical;
15static LONG gTestValueVulnerable = 0;
16static LONG gTestValueSerialized = 0;
17
18static BOOL TestSynchCritical_TriggerAndCheckRaceCondition(HANDLE OwningThread, LONG RecursionCount)
19{
20 /* if called unprotected this will hopefully trigger a race condition ... */
21 gTestValueVulnerable++;
22
23 if (critical.OwningThread != OwningThread)
24 {
25 printf("CriticalSection failure: OwningThread is invalid\n");
26 return FALSE;
27 }
28 if (critical.RecursionCount != RecursionCount)
29 {
30 printf("CriticalSection failure: RecursionCount is invalid\n");
31 return FALSE;
32 }
33
34 /* ... which we try to detect using the serialized counter */
35 if (gTestValueVulnerable != InterlockedIncrement(&gTestValueSerialized))
36 {
37 printf("CriticalSection failure: Data corruption detected\n");
38 return FALSE;
39 }
40
41 return TRUE;
42}
43
44static UINT32 prand(UINT32 max)
45{
46 UINT32 tmp = 0;
47 if (max <= 1)
48 return 1;
49 if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
50 return 0;
51 return tmp % (max - 1) + 1;
52}
53
54/* this thread function shall increment the global dwTestValue until the PBOOL passed in arg is
55 * FALSE */
56static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
57{
58 int rc = 0;
59 HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
60
61 PBOOL pbContinueRunning = (PBOOL)arg;
62
63 while (*pbContinueRunning)
64 {
65 EnterCriticalSection(&critical);
66
67 rc = 1;
68
69 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
70 return 1;
71
72 /* add some random recursion level */
73 UINT32 j = prand(5);
74 for (UINT32 i = 0; i < j; i++)
75 {
76 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
77 return 2;
78 EnterCriticalSection(&critical);
79 }
80 for (UINT32 i = 0; i < j; i++)
81 {
82 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
83 return 2;
84 LeaveCriticalSection(&critical);
85 }
86
87 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
88 return 3;
89
90 LeaveCriticalSection(&critical);
91 }
92
93 return 0;
94}
95
96/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock
97 */
98static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
99{
100 WINPR_UNUSED(arg);
101 if (TryEnterCriticalSection(&critical) == TRUE)
102 {
103 LeaveCriticalSection(&critical);
104 return 1;
105 }
106 return 0;
107}
108
109static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
110{
111 SYSTEM_INFO sysinfo;
112 DWORD dwPreviousSpinCount = 0;
113 DWORD dwSpinCount = 0;
114 DWORD dwSpinCountExpected = 0;
115 HANDLE hMainThread = nullptr;
116 HANDLE* hThreads = nullptr;
117 HANDLE hThread = nullptr;
118 DWORD dwThreadCount = 0;
119 DWORD dwThreadExitCode = 0;
120 BOOL bTest1Running = 0;
121
122 PBOOL pbThreadTerminated = (PBOOL)arg;
123
124 GetNativeSystemInfo(&sysinfo);
125
126 hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
127
134 dwSpinCount = 100;
135 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
136 goto fail;
137 while (--dwSpinCount)
138 {
139 dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
140 dwSpinCountExpected = 0;
141#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
142 if (sysinfo.dwNumberOfProcessors > 1)
143 dwSpinCountExpected = dwSpinCount + 1;
144#endif
145 if (dwPreviousSpinCount != dwSpinCountExpected)
146 {
147 printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
148 " (expected: %" PRIu32 ")\n",
149 dwPreviousSpinCount, dwSpinCountExpected);
150 goto fail;
151 }
152
153 DeleteCriticalSection(&critical);
154
155 if (dwSpinCount % 2 == 0)
156 {
157 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
158 goto fail;
159 }
160 else
161 {
162 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
163 goto fail;
164 }
165 }
166 DeleteCriticalSection(&critical);
167
174 InitializeCriticalSection(&critical);
175
176 int i = 0;
177 for (; i < 10; i++)
178 {
179 if (critical.RecursionCount != i)
180 {
181 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
182 critical.RecursionCount, i);
183 goto fail;
184 }
185 if (i % 2 == 0)
186 {
187 EnterCriticalSection(&critical);
188 }
189 else
190 {
191 if (TryEnterCriticalSection(&critical) == FALSE)
192 {
193 printf("CriticalSection failure: TryEnterCriticalSection failed where it should "
194 "not.\n");
195 goto fail;
196 }
197 }
198 if (critical.OwningThread != hMainThread)
199 {
200 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
201 i);
202 goto fail;
203 }
204 }
205 while (--i >= 0)
206 {
207 LeaveCriticalSection(&critical);
208 if (critical.RecursionCount != i)
209 {
210 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
211 critical.RecursionCount, i);
212 goto fail;
213 }
214 if (critical.OwningThread != (i ? hMainThread : nullptr))
215 {
216 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
217 i);
218 goto fail;
219 }
220 }
221 DeleteCriticalSection(&critical);
222
227 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
228
229 hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE));
230 if (!hThreads)
231 {
232 printf("Problem allocating memory\n");
233 goto fail;
234 }
235
236 for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
237 {
238 dwSpinCount = j * 100;
239 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
240 goto fail;
241
242 gTestValueVulnerable = 0;
243 gTestValueSerialized = 0;
244
245 /* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
246 bTest1Running = TRUE;
247 for (int i = 0; i < (int)dwThreadCount; i++)
248 {
249 if (!(hThreads[i] = CreateThread(nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
250 nullptr)))
251 {
252 printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i);
253 goto fail;
254 }
255 }
256
257 /* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
258 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
259 bTest1Running = FALSE;
260
261 for (int i = 0; i < (int)dwThreadCount; i++)
262 {
263 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
264 {
265 printf("CriticalSection failure: Failed to wait for thread #%d\n", i);
266 goto fail;
267 }
268 if (!GetExitCodeThread(hThreads[i], &dwThreadExitCode))
269 goto fail;
270 if (dwThreadExitCode != 0)
271 {
272 printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i,
273 dwThreadExitCode);
274 goto fail;
275 }
276 (void)CloseHandle(hThreads[i]);
277 }
278
279 if (gTestValueVulnerable != gTestValueSerialized)
280 {
281 printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32
282 ")\n",
283 gTestValueVulnerable, gTestValueSerialized);
284 goto fail;
285 }
286
287 DeleteCriticalSection(&critical);
288 }
289
290 free((void*)hThreads);
291
296 InitializeCriticalSection(&critical);
297
298 if (TryEnterCriticalSection(&critical) == FALSE)
299 {
300 printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
301 goto fail;
302 }
303 /* This thread tries to call TryEnterCriticalSection which must fail */
304 if (!(hThread = CreateThread(nullptr, 0, TestSynchCritical_Test2, nullptr, 0, nullptr)))
305 {
306 printf("CriticalSection failure: Failed to create test_2 thread\n");
307 goto fail;
308 }
309 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
310 {
311 printf("CriticalSection failure: Failed to wait for thread\n");
312 goto fail;
313 }
314 if (!GetExitCodeThread(hThread, &dwThreadExitCode))
315 goto fail;
316 if (dwThreadExitCode != 0)
317 {
318 printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n",
319 dwThreadExitCode);
320 goto fail;
321 }
322 (void)CloseHandle(hThread);
323
324 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
325 return 0;
326
327fail:
328 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
329 return 1;
330}
331
332int TestSynchCritical(int argc, char* argv[])
333{
334 BOOL bThreadTerminated = FALSE;
335 HANDLE hThread = nullptr;
336 DWORD dwThreadExitCode = 0;
337 DWORD dwDeadLockDetectionTimeMs = 0;
338
339 WINPR_UNUSED(argc);
340 WINPR_UNUSED(argv);
341
342 dwDeadLockDetectionTimeMs =
343 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
344
345 printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs);
346
347 if (!(hThread =
348 CreateThread(nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0, nullptr)))
349 {
350 printf("CriticalSection failure: Failed to create main thread\n");
351 return -1;
352 }
353
362 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
363 {
364 if (bThreadTerminated)
365 break;
366
367 Sleep(10);
368 }
369
370 if (!bThreadTerminated)
371 {
372 printf("CriticalSection failure: Possible dead lock detected\n");
373 return -1;
374 }
375
376 if (!GetExitCodeThread(hThread, &dwThreadExitCode))
377 return -1;
378 if (!CloseHandle(hThread))
379 return -1;
380
381 if (dwThreadExitCode != 0)
382 {
383 return -1;
384 }
385
386 return 0;
387}