FreeRDP
TestSynchInit.c
1 #include <stdio.h>
2 #include <winpr/crt.h>
3 #include <winpr/crypto.h>
4 #include <winpr/synch.h>
5 #include <winpr/thread.h>
6 #include <winpr/interlocked.h>
7 
8 #define TEST_NUM_THREADS 100
9 #define TEST_NUM_FAILURES 10
10 
11 static INIT_ONCE initOnceTest = INIT_ONCE_STATIC_INIT;
12 
13 static HANDLE hStartEvent = NULL;
14 static LONG* pErrors = NULL;
15 static LONG* pTestThreadFunctionCalls = NULL;
16 static LONG* pTestOnceFunctionCalls = NULL;
17 static LONG* pInitOnceExecuteOnceCalls = NULL;
18 
19 static UINT32 prand(UINT32 max)
20 {
21  UINT32 tmp = 0;
22  if (max <= 1)
23  return 1;
24  winpr_RAND(&tmp, sizeof(tmp));
25  return tmp % (max - 1) + 1;
26 }
27 
28 static BOOL CALLBACK TestOnceFunction(PINIT_ONCE once, PVOID param, PVOID* context)
29 {
30  LONG calls = InterlockedIncrement(pTestOnceFunctionCalls) - 1;
31 
32  WINPR_UNUSED(once);
33  WINPR_UNUSED(param);
34  WINPR_UNUSED(context);
35 
36  /* simulate execution time */
37  Sleep(30 + prand(40));
38 
39  if (calls < TEST_NUM_FAILURES)
40  {
41  /* simulated error */
42  return FALSE;
43  }
44  if (calls == TEST_NUM_FAILURES)
45  {
46  return TRUE;
47  }
48  (void)fprintf(stderr, "%s: error: called again after success\n", __func__);
49  InterlockedIncrement(pErrors);
50  return FALSE;
51 }
52 
53 static DWORD WINAPI TestThreadFunction(LPVOID lpParam)
54 {
55  LONG calls = 0;
56  BOOL ok = 0;
57 
58  WINPR_UNUSED(lpParam);
59 
60  InterlockedIncrement(pTestThreadFunctionCalls);
61  if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
62  {
63  (void)fprintf(stderr, "%s: error: failed to wait for start event\n", __func__);
64  InterlockedIncrement(pErrors);
65  return 0;
66  }
67 
68  ok = InitOnceExecuteOnce(&initOnceTest, TestOnceFunction, NULL, NULL);
69  calls = InterlockedIncrement(pInitOnceExecuteOnceCalls);
70  if (!ok && calls > TEST_NUM_FAILURES)
71  {
72  (void)fprintf(stderr, "%s: InitOnceExecuteOnce failed unexpectedly\n", __func__);
73  InterlockedIncrement(pErrors);
74  }
75  return 0;
76 }
77 
78 int TestSynchInit(int argc, char* argv[])
79 {
80  HANDLE hThreads[TEST_NUM_THREADS];
81  DWORD dwCreatedThreads = 0;
82  BOOL result = FALSE;
83 
84  WINPR_UNUSED(argc);
85  WINPR_UNUSED(argv);
86 
87  pErrors = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
88  pTestThreadFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
89  pTestOnceFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
90  pInitOnceExecuteOnceCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
91 
92  if (!pErrors || !pTestThreadFunctionCalls || !pTestOnceFunctionCalls ||
93  !pInitOnceExecuteOnceCalls)
94  {
95  (void)fprintf(stderr, "error: _aligned_malloc failed\n");
96  goto out;
97  }
98 
99  *pErrors = 0;
100  *pTestThreadFunctionCalls = 0;
101  *pTestOnceFunctionCalls = 0;
102  *pInitOnceExecuteOnceCalls = 0;
103 
104  if (!(hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
105  {
106  (void)fprintf(stderr, "error creating start event\n");
107  InterlockedIncrement(pErrors);
108  goto out;
109  }
110 
111  for (DWORD i = 0; i < TEST_NUM_THREADS; i++)
112  {
113  if (!(hThreads[i] = CreateThread(NULL, 0, TestThreadFunction, NULL, 0, NULL)))
114  {
115  (void)fprintf(stderr, "error creating thread #%" PRIu32 "\n", i);
116  InterlockedIncrement(pErrors);
117  goto out;
118  }
119  dwCreatedThreads++;
120  }
121 
122  Sleep(100);
123  (void)SetEvent(hStartEvent);
124 
125  for (DWORD i = 0; i < dwCreatedThreads; i++)
126  {
127  if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
128  {
129  (void)fprintf(stderr, "error: error waiting for thread #%" PRIu32 "\n", i);
130  InterlockedIncrement(pErrors);
131  goto out;
132  }
133  }
134 
135  if (*pErrors == 0 && *pTestThreadFunctionCalls == TEST_NUM_THREADS &&
136  *pInitOnceExecuteOnceCalls == TEST_NUM_THREADS &&
137  *pTestOnceFunctionCalls == TEST_NUM_FAILURES + 1)
138  {
139  result = TRUE;
140  }
141 
142 out:
143  (void)fprintf(stderr, "Test result: %s\n", result ? "OK" : "ERROR");
144  (void)fprintf(stderr, "Error count: %" PRId32 "\n", pErrors ? *pErrors : -1);
145  (void)fprintf(stderr, "Threads created: %" PRIu32 "\n", dwCreatedThreads);
146  (void)fprintf(stderr, "TestThreadFunctionCalls: %" PRId32 "\n",
147  pTestThreadFunctionCalls ? *pTestThreadFunctionCalls : -1);
148  (void)fprintf(stderr, "InitOnceExecuteOnceCalls: %" PRId32 "\n",
149  pInitOnceExecuteOnceCalls ? *pInitOnceExecuteOnceCalls : -1);
150  (void)fprintf(stderr, "TestOnceFunctionCalls: %" PRId32 "\n",
151  pTestOnceFunctionCalls ? *pTestOnceFunctionCalls : -1);
152 
153  winpr_aligned_free(pErrors);
154  winpr_aligned_free(pTestThreadFunctionCalls);
155  winpr_aligned_free(pTestOnceFunctionCalls);
156  winpr_aligned_free(pInitOnceExecuteOnceCalls);
157 
158  (void)CloseHandle(hStartEvent);
159 
160  for (DWORD i = 0; i < dwCreatedThreads; i++)
161  {
162  (void)CloseHandle(hThreads[i]);
163  }
164 
165  return (result ? 0 : 1);
166 }