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>
11 #define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 50
12 #define TEST_SYNC_CRITICAL_TEST1_RUNS 4
15 static LONG gTestValueVulnerable = 0;
16 static LONG gTestValueSerialized = 0;
18 static BOOL TestSynchCritical_TriggerAndCheckRaceCondition(HANDLE OwningThread, LONG RecursionCount)
21 gTestValueVulnerable++;
23 if (critical.OwningThread != OwningThread)
25 printf(
"CriticalSection failure: OwningThread is invalid\n");
28 if (critical.RecursionCount != RecursionCount)
30 printf(
"CriticalSection failure: RecursionCount is invalid\n");
35 if (gTestValueVulnerable != InterlockedIncrement(&gTestValueSerialized))
37 printf(
"CriticalSection failure: Data corruption detected\n");
44 static UINT32 prand(UINT32 max)
49 winpr_RAND(&tmp,
sizeof(tmp));
50 return tmp % (max - 1) + 1;
55 static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
58 HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
60 PBOOL pbContinueRunning = (PBOOL)arg;
62 while (*pbContinueRunning)
64 EnterCriticalSection(&critical);
68 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
73 for (UINT32 i = 0; i < j; i++)
75 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
77 EnterCriticalSection(&critical);
79 for (UINT32 i = 0; i < j; i++)
81 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
83 LeaveCriticalSection(&critical);
86 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
89 LeaveCriticalSection(&critical);
97 static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
100 if (TryEnterCriticalSection(&critical) == TRUE)
102 LeaveCriticalSection(&critical);
108 static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
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;
121 PBOOL pbThreadTerminated = (PBOOL)arg;
123 GetNativeSystemInfo(&sysinfo);
125 hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
134 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
135 while (--dwSpinCount)
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;
143 if (dwPreviousSpinCount != dwSpinCountExpected)
145 printf(
"CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
146 " (expected: %" PRIu32
")\n",
147 dwPreviousSpinCount, dwSpinCountExpected);
151 DeleteCriticalSection(&critical);
153 if (dwSpinCount % 2 == 0)
154 InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
156 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
158 DeleteCriticalSection(&critical);
166 InitializeCriticalSection(&critical);
171 if (critical.RecursionCount != i)
173 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
174 critical.RecursionCount, i);
179 EnterCriticalSection(&critical);
183 if (TryEnterCriticalSection(&critical) == FALSE)
185 printf(
"CriticalSection failure: TryEnterCriticalSection failed where it should "
190 if (critical.OwningThread != hMainThread)
192 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
199 LeaveCriticalSection(&critical);
200 if (critical.RecursionCount != i)
202 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
203 critical.RecursionCount, i);
206 if (critical.OwningThread != (i ? hMainThread : NULL))
208 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
213 DeleteCriticalSection(&critical);
219 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
221 hThreads = (HANDLE*)calloc(dwThreadCount,
sizeof(HANDLE));
224 printf(
"Problem allocating memory\n");
228 for (
int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
230 dwSpinCount = j * 100;
231 InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
233 gTestValueVulnerable = 0;
234 gTestValueSerialized = 0;
237 bTest1Running = TRUE;
238 for (
int i = 0; i < (int)dwThreadCount; i++)
241 CreateThread(NULL, 0, TestSynchCritical_Test1, &bTest1Running, 0, NULL)))
243 printf(
"CriticalSection failure: Failed to create test_1 thread #%d\n", i);
249 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
250 bTest1Running = FALSE;
252 for (
int i = 0; i < (int)dwThreadCount; i++)
254 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
256 printf(
"CriticalSection failure: Failed to wait for thread #%d\n", i);
259 GetExitCodeThread(hThreads[i], &dwThreadExitCode);
260 if (dwThreadExitCode != 0)
262 printf(
"CriticalSection failure: Thread #%d returned error code %" PRIu32
"\n", i,
266 (void)CloseHandle(hThreads[i]);
269 if (gTestValueVulnerable != gTestValueSerialized)
271 printf(
"CriticalSection failure: unexpected test value %" PRId32
" (expected %" PRId32
273 gTestValueVulnerable, gTestValueSerialized);
277 DeleteCriticalSection(&critical);
286 InitializeCriticalSection(&critical);
288 if (TryEnterCriticalSection(&critical) == FALSE)
290 printf(
"CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
294 if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Test2, NULL, 0, NULL)))
296 printf(
"CriticalSection failure: Failed to create test_2 thread\n");
299 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
301 printf(
"CriticalSection failure: Failed to wait for thread\n");
304 GetExitCodeThread(hThread, &dwThreadExitCode);
305 if (dwThreadExitCode != 0)
307 printf(
"CriticalSection failure: Thread returned error code %" PRIu32
"\n",
311 (void)CloseHandle(hThread);
313 *pbThreadTerminated = TRUE;
317 *pbThreadTerminated = TRUE;
321 int TestSynchCritical(
int argc,
char* argv[])
323 BOOL bThreadTerminated = FALSE;
324 HANDLE hThread = NULL;
325 DWORD dwThreadExitCode = 0;
326 DWORD dwDeadLockDetectionTimeMs = 0;
331 dwDeadLockDetectionTimeMs =
332 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
334 printf(
"Deadlock will be assumed after %" PRIu32
" ms.\n", dwDeadLockDetectionTimeMs);
336 if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Main, &bThreadTerminated, 0, NULL)))
338 printf(
"CriticalSection failure: Failed to create main thread\n");
350 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
352 if (bThreadTerminated)
358 if (!bThreadTerminated)
360 printf(
"CriticalSection failure: Possible dead lock detected\n");
364 GetExitCodeThread(hThread, &dwThreadExitCode);
365 (void)CloseHandle(hThread);
367 if (dwThreadExitCode != 0)