FreeRDP
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 
14 static CRITICAL_SECTION critical;
15 static LONG gTestValueVulnerable = 0;
16 static LONG gTestValueSerialized = 0;
17 
18 static 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 
44 static 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 passsed in arg is
54  * FALSE */
55 static 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  */
97 static 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 
108 static 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(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 
316 fail:
317  *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
318  return 1;
319 }
320 
321 int 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 }