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 100
12#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
15static LONG gTestValueVulnerable = 0;
16static LONG gTestValueSerialized = 0;
18static 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");
44static UINT32 prand(UINT32 max)
49 if (winpr_RAND(&tmp,
sizeof(tmp)) < 0)
51 return tmp % (max - 1) + 1;
56static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
59 HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
61 PBOOL pbContinueRunning = (PBOOL)arg;
63 while (*pbContinueRunning)
65 EnterCriticalSection(&critical);
69 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
74 for (UINT32 i = 0; i < j; i++)
76 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
78 EnterCriticalSection(&critical);
80 for (UINT32 i = 0; i < j; i++)
82 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
84 LeaveCriticalSection(&critical);
87 if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
90 LeaveCriticalSection(&critical);
98static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
101 if (TryEnterCriticalSection(&critical) == TRUE)
103 LeaveCriticalSection(&critical);
109static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
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;
122 PBOOL pbThreadTerminated = (PBOOL)arg;
124 GetNativeSystemInfo(&sysinfo);
126 hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
135 InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
136 while (--dwSpinCount)
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;
144 if (dwPreviousSpinCount != dwSpinCountExpected)
146 printf(
"CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
147 " (expected: %" PRIu32
")\n",
148 dwPreviousSpinCount, dwSpinCountExpected);
152 DeleteCriticalSection(&critical);
154 if (dwSpinCount % 2 == 0)
156 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
161 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
165 DeleteCriticalSection(&critical);
173 InitializeCriticalSection(&critical);
178 if (critical.RecursionCount != i)
180 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
181 critical.RecursionCount, i);
186 EnterCriticalSection(&critical);
190 if (TryEnterCriticalSection(&critical) == FALSE)
192 printf(
"CriticalSection failure: TryEnterCriticalSection failed where it should "
197 if (critical.OwningThread != hMainThread)
199 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
206 LeaveCriticalSection(&critical);
207 if (critical.RecursionCount != i)
209 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
210 critical.RecursionCount, i);
213 if (critical.OwningThread != (i ? hMainThread : nullptr))
215 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
220 DeleteCriticalSection(&critical);
226 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
228 hThreads = (HANDLE*)calloc(dwThreadCount,
sizeof(HANDLE));
231 printf(
"Problem allocating memory\n");
235 for (
int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
237 dwSpinCount = j * 100;
238 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
241 gTestValueVulnerable = 0;
242 gTestValueSerialized = 0;
245 bTest1Running = TRUE;
246 for (
int i = 0; i < (int)dwThreadCount; i++)
248 if (!(hThreads[i] = CreateThread(
nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
251 printf(
"CriticalSection failure: Failed to create test_1 thread #%d\n", i);
257 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
258 bTest1Running = FALSE;
260 for (
int i = 0; i < (int)dwThreadCount; i++)
262 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
264 printf(
"CriticalSection failure: Failed to wait for thread #%d\n", i);
267 GetExitCodeThread(hThreads[i], &dwThreadExitCode);
268 if (dwThreadExitCode != 0)
270 printf(
"CriticalSection failure: Thread #%d returned error code %" PRIu32
"\n", i,
274 (void)CloseHandle(hThreads[i]);
277 if (gTestValueVulnerable != gTestValueSerialized)
279 printf(
"CriticalSection failure: unexpected test value %" PRId32
" (expected %" PRId32
281 gTestValueVulnerable, gTestValueSerialized);
285 DeleteCriticalSection(&critical);
288 free((
void*)hThreads);
294 InitializeCriticalSection(&critical);
296 if (TryEnterCriticalSection(&critical) == FALSE)
298 printf(
"CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
302 if (!(hThread = CreateThread(
nullptr, 0, TestSynchCritical_Test2,
nullptr, 0,
nullptr)))
304 printf(
"CriticalSection failure: Failed to create test_2 thread\n");
307 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
309 printf(
"CriticalSection failure: Failed to wait for thread\n");
312 GetExitCodeThread(hThread, &dwThreadExitCode);
313 if (dwThreadExitCode != 0)
315 printf(
"CriticalSection failure: Thread returned error code %" PRIu32
"\n",
319 (void)CloseHandle(hThread);
321 *pbThreadTerminated = TRUE;
325 *pbThreadTerminated = TRUE;
329int TestSynchCritical(
int argc,
char* argv[])
331 BOOL bThreadTerminated = FALSE;
332 HANDLE hThread =
nullptr;
333 DWORD dwThreadExitCode = 0;
334 DWORD dwDeadLockDetectionTimeMs = 0;
339 dwDeadLockDetectionTimeMs =
340 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
342 printf(
"Deadlock will be assumed after %" PRIu32
" ms.\n", dwDeadLockDetectionTimeMs);
345 CreateThread(
nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0,
nullptr)))
347 printf(
"CriticalSection failure: Failed to create main thread\n");
359 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
361 if (bThreadTerminated)
367 if (!bThreadTerminated)
369 printf(
"CriticalSection failure: Possible dead lock detected\n");
373 GetExitCodeThread(hThread, &dwThreadExitCode);
374 (void)CloseHandle(hThread);
376 if (dwThreadExitCode != 0)