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 50
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 winpr_RAND(&tmp, sizeof(tmp));
50 return tmp % (max - 1) + 1;
51}
52
53/* this thread function shall increment the global dwTestValue until the PBOOL passed in arg is
54 * FALSE */
55static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
56{
57 int rc = 0;
58 HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
59
60 PBOOL pbContinueRunning = (PBOOL)arg;
61
62 while (*pbContinueRunning)
63 {
64 EnterCriticalSection(&critical);
65
66 rc = 1;
67
68 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
69 return 1;
70
71 /* add some random recursion level */
72 UINT32 j = prand(5);
73 for (UINT32 i = 0; i < j; i++)
74 {
75 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
76 return 2;
77 EnterCriticalSection(&critical);
78 }
79 for (UINT32 i = 0; i < j; i++)
80 {
81 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
82 return 2;
83 LeaveCriticalSection(&critical);
84 }
85
86 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
87 return 3;
88
89 LeaveCriticalSection(&critical);
90 }
91
92 return 0;
93}
94
95/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock
96 */
97static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
98{
99 WINPR_UNUSED(arg);
100 if (TryEnterCriticalSection(&critical) == TRUE)
101 {
102 LeaveCriticalSection(&critical);
103 return 1;
104 }
105 return 0;
106}
107
108static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
109{
110 SYSTEM_INFO sysinfo;
111 DWORD dwPreviousSpinCount = 0;
112 DWORD dwSpinCount = 0;
113 DWORD dwSpinCountExpected = 0;
114 HANDLE hMainThread = NULL;
115 HANDLE* hThreads = NULL;
116 HANDLE hThread = NULL;
117 DWORD dwThreadCount = 0;
118 DWORD dwThreadExitCode = 0;
119 BOOL bTest1Running = 0;
120
121 PBOOL pbThreadTerminated = (PBOOL)arg;
122
123 GetNativeSystemInfo(&sysinfo);
124
125 hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
126
133 dwSpinCount = 100;
134 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
135 while (--dwSpinCount)
136 {
137 dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
138 dwSpinCountExpected = 0;
139#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
140 if (sysinfo.dwNumberOfProcessors > 1)
141 dwSpinCountExpected = dwSpinCount + 1;
142#endif
143 if (dwPreviousSpinCount != dwSpinCountExpected)
144 {
145 printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
146 " (expected: %" PRIu32 ")\n",
147 dwPreviousSpinCount, dwSpinCountExpected);
148 goto fail;
149 }
150
151 DeleteCriticalSection(&critical);
152
153 if (dwSpinCount % 2 == 0)
154 InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
155 else
156 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
157 }
158 DeleteCriticalSection(&critical);
159
166 InitializeCriticalSection(&critical);
167
168 int i = 0;
169 for (; i < 10; i++)
170 {
171 if (critical.RecursionCount != i)
172 {
173 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
174 critical.RecursionCount, i);
175 goto fail;
176 }
177 if (i % 2 == 0)
178 {
179 EnterCriticalSection(&critical);
180 }
181 else
182 {
183 if (TryEnterCriticalSection(&critical) == FALSE)
184 {
185 printf("CriticalSection failure: TryEnterCriticalSection failed where it should "
186 "not.\n");
187 goto fail;
188 }
189 }
190 if (critical.OwningThread != hMainThread)
191 {
192 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
193 i);
194 goto fail;
195 }
196 }
197 while (--i >= 0)
198 {
199 LeaveCriticalSection(&critical);
200 if (critical.RecursionCount != i)
201 {
202 printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
203 critical.RecursionCount, i);
204 goto fail;
205 }
206 if (critical.OwningThread != (i ? hMainThread : NULL))
207 {
208 printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
209 i);
210 goto fail;
211 }
212 }
213 DeleteCriticalSection(&critical);
214
219 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
220
221 hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE));
222 if (!hThreads)
223 {
224 printf("Problem allocating memory\n");
225 goto fail;
226 }
227
228 for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
229 {
230 dwSpinCount = j * 100;
231 InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
232
233 gTestValueVulnerable = 0;
234 gTestValueSerialized = 0;
235
236 /* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
237 bTest1Running = TRUE;
238 for (int i = 0; i < (int)dwThreadCount; i++)
239 {
240 if (!(hThreads[i] =
241 CreateThread(NULL, 0, TestSynchCritical_Test1, &bTest1Running, 0, NULL)))
242 {
243 printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i);
244 goto fail;
245 }
246 }
247
248 /* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
249 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
250 bTest1Running = FALSE;
251
252 for (int i = 0; i < (int)dwThreadCount; i++)
253 {
254 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
255 {
256 printf("CriticalSection failure: Failed to wait for thread #%d\n", i);
257 goto fail;
258 }
259 GetExitCodeThread(hThreads[i], &dwThreadExitCode);
260 if (dwThreadExitCode != 0)
261 {
262 printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i,
263 dwThreadExitCode);
264 goto fail;
265 }
266 (void)CloseHandle(hThreads[i]);
267 }
268
269 if (gTestValueVulnerable != gTestValueSerialized)
270 {
271 printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32
272 ")\n",
273 gTestValueVulnerable, gTestValueSerialized);
274 goto fail;
275 }
276
277 DeleteCriticalSection(&critical);
278 }
279
280 free((void*)hThreads);
281
286 InitializeCriticalSection(&critical);
287
288 if (TryEnterCriticalSection(&critical) == FALSE)
289 {
290 printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
291 goto fail;
292 }
293 /* This thread tries to call TryEnterCriticalSection which must fail */
294 if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Test2, NULL, 0, NULL)))
295 {
296 printf("CriticalSection failure: Failed to create test_2 thread\n");
297 goto fail;
298 }
299 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
300 {
301 printf("CriticalSection failure: Failed to wait for thread\n");
302 goto fail;
303 }
304 GetExitCodeThread(hThread, &dwThreadExitCode);
305 if (dwThreadExitCode != 0)
306 {
307 printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n",
308 dwThreadExitCode);
309 goto fail;
310 }
311 (void)CloseHandle(hThread);
312
313 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
314 return 0;
315
316fail:
317 *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
318 return 1;
319}
320
321int TestSynchCritical(int argc, char* argv[])
322{
323 BOOL bThreadTerminated = FALSE;
324 HANDLE hThread = NULL;
325 DWORD dwThreadExitCode = 0;
326 DWORD dwDeadLockDetectionTimeMs = 0;
327
328 WINPR_UNUSED(argc);
329 WINPR_UNUSED(argv);
330
331 dwDeadLockDetectionTimeMs =
332 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
333
334 printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs);
335
336 if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Main, &bThreadTerminated, 0, NULL)))
337 {
338 printf("CriticalSection failure: Failed to create main thread\n");
339 return -1;
340 }
341
350 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
351 {
352 if (bThreadTerminated)
353 break;
354
355 Sleep(10);
356 }
357
358 if (!bThreadTerminated)
359 {
360 printf("CriticalSection failure: Possible dead lock detected\n");
361 return -1;
362 }
363
364 GetExitCodeThread(hThread, &dwThreadExitCode);
365 (void)CloseHandle(hThread);
366
367 if (dwThreadExitCode != 0)
368 {
369 return -1;
370 }
371
372 return 0;
373}