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 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
136 while (--dwSpinCount)
137 {
138 dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
139 dwSpinCountExpected = 0;
140#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
141 if (sysinfo.dwNumberOfProcessors > 1)
142 dwSpinCountExpected = dwSpinCount + 1;
143#endif
144 if (dwPreviousSpinCount != dwSpinCountExpected)
145 {
146 printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
147 " (expected: %" PRIu32 ")\n",
148 dwPreviousSpinCount, dwSpinCountExpected);
149 goto fail;
150 }
151
152 DeleteCriticalSection(&critical);
153
154 if (dwSpinCount % 2 == 0)
155 {
156 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
157 goto fail;
158 }
159 else
160 {
161 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
162 goto fail;
163 }
164 }
165 DeleteCriticalSection(&critical);
166
173 InitializeCriticalSection(&critical);
174
175 int i = 0;
176 for (; i < 10; i++)
177 {
178 if (critical.RecursionCount != i)
179 {
180 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
181 critical.RecursionCount, i);
182 goto fail;
183 }
184 if (i % 2 == 0)
185 {
186 EnterCriticalSection(&critical);
187 }
188 else
189 {
190 if (TryEnterCriticalSection(&critical) == FALSE)
191 {
192 printf("CriticalSection failure: TryEnterCriticalSection failed where it should "
193 "not.\n");
194 goto fail;
195 }
196 }
197 if (critical.OwningThread != hMainThread)
198 {
199 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
200 i);
201 goto fail;
202 }
203 }
204 while (--i >= 0)
205 {
206 LeaveCriticalSection(&critical);
207 if (critical.RecursionCount != i)
208 {
209 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
210 critical.RecursionCount, i);
211 goto fail;
212 }
213 if (critical.OwningThread != (i ? hMainThread : nullptr))
214 {
215 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
216 i);
217 goto fail;
218 }
219 }
220 DeleteCriticalSection(&critical);
221
226 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
227
228 hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE));
229 if (!hThreads)
230 {
231 printf("Problem allocating memory\n");
232 goto fail;
233 }
234
235 for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
236 {
237 dwSpinCount = j * 100;
238 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
239 goto fail;
240
241 gTestValueVulnerable = 0;
242 gTestValueSerialized = 0;
243
244 /* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
245 bTest1Running = TRUE;
246 for (int i = 0; i < (int)dwThreadCount; i++)
247 {
248 if (!(hThreads[i] = CreateThread(nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
249 nullptr)))
250 {
251 printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i);
252 goto fail;
253 }
254 }
255
256 /* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
257 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
258 bTest1Running = FALSE;
259
260 for (int i = 0; i < (int)dwThreadCount; i++)
261 {
262 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
263 {
264 printf("CriticalSection failure: Failed to wait for thread #%d\n", i);
265 goto fail;
266 }
267 GetExitCodeThread(hThreads[i], &dwThreadExitCode);
268 if (dwThreadExitCode != 0)
269 {
270 printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i,
271 dwThreadExitCode);
272 goto fail;
273 }
274 (void)CloseHandle(hThreads[i]);
275 }
276
277 if (gTestValueVulnerable != gTestValueSerialized)
278 {
279 printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32
280 ")\n",
281 gTestValueVulnerable, gTestValueSerialized);
282 goto fail;
283 }
284
285 DeleteCriticalSection(&critical);
286 }
287
288 free((void*)hThreads);
289
294 InitializeCriticalSection(&critical);
295
296 if (TryEnterCriticalSection(&critical) == FALSE)
297 {
298 printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
299 goto fail;
300 }
301 /* This thread tries to call TryEnterCriticalSection which must fail */
302 if (!(hThread = CreateThread(nullptr, 0, TestSynchCritical_Test2, nullptr, 0, nullptr)))
303 {
304 printf("CriticalSection failure: Failed to create test_2 thread\n");
305 goto fail;
306 }
307 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
308 {
309 printf("CriticalSection failure: Failed to wait for thread\n");
310 goto fail;
311 }
312 GetExitCodeThread(hThread, &dwThreadExitCode);
313 if (dwThreadExitCode != 0)
314 {
315 printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n",
316 dwThreadExitCode);
317 goto fail;
318 }
319 (void)CloseHandle(hThread);
320
321 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
322 return 0;
323
324fail:
325 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
326 return 1;
327}
328
329int TestSynchCritical(int argc, char* argv[])
330{
331 BOOL bThreadTerminated = FALSE;
332 HANDLE hThread = nullptr;
333 DWORD dwThreadExitCode = 0;
334 DWORD dwDeadLockDetectionTimeMs = 0;
335
336 WINPR_UNUSED(argc);
337 WINPR_UNUSED(argv);
338
339 dwDeadLockDetectionTimeMs =
340 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
341
342 printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs);
343
344 if (!(hThread =
345 CreateThread(nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0, nullptr)))
346 {
347 printf("CriticalSection failure: Failed to create main thread\n");
348 return -1;
349 }
350
359 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
360 {
361 if (bThreadTerminated)
362 break;
363
364 Sleep(10);
365 }
366
367 if (!bThreadTerminated)
368 {
369 printf("CriticalSection failure: Possible dead lock detected\n");
370 return -1;
371 }
372
373 GetExitCodeThread(hThread, &dwThreadExitCode);
374 (void)CloseHandle(hThread);
375
376 if (dwThreadExitCode != 0)
377 {
378 return -1;
379 }
380
381 return 0;
382}