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 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
137 while (--dwSpinCount)
139 dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
140 dwSpinCountExpected = 0;
141#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
142 if (sysinfo.dwNumberOfProcessors > 1)
143 dwSpinCountExpected = dwSpinCount + 1;
145 if (dwPreviousSpinCount != dwSpinCountExpected)
147 printf(
"CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
148 " (expected: %" PRIu32
")\n",
149 dwPreviousSpinCount, dwSpinCountExpected);
153 DeleteCriticalSection(&critical);
155 if (dwSpinCount % 2 == 0)
157 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
162 if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
166 DeleteCriticalSection(&critical);
174 InitializeCriticalSection(&critical);
179 if (critical.RecursionCount != i)
181 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
182 critical.RecursionCount, i);
187 EnterCriticalSection(&critical);
191 if (TryEnterCriticalSection(&critical) == FALSE)
193 printf(
"CriticalSection failure: TryEnterCriticalSection failed where it should "
198 if (critical.OwningThread != hMainThread)
200 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
207 LeaveCriticalSection(&critical);
208 if (critical.RecursionCount != i)
210 printf(
"CriticalSection failure: RecursionCount field is %" PRId32
" instead of %d.\n",
211 critical.RecursionCount, i);
214 if (critical.OwningThread != (i ? hMainThread : nullptr))
216 printf(
"CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
221 DeleteCriticalSection(&critical);
227 dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
229 hThreads = (HANDLE*)calloc(dwThreadCount,
sizeof(HANDLE));
232 printf(
"Problem allocating memory\n");
236 for (
int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
238 dwSpinCount = j * 100;
239 if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
242 gTestValueVulnerable = 0;
243 gTestValueSerialized = 0;
246 bTest1Running = TRUE;
247 for (
int i = 0; i < (int)dwThreadCount; i++)
249 if (!(hThreads[i] = CreateThread(
nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
252 printf(
"CriticalSection failure: Failed to create test_1 thread #%d\n", i);
258 Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
259 bTest1Running = FALSE;
261 for (
int i = 0; i < (int)dwThreadCount; i++)
263 if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
265 printf(
"CriticalSection failure: Failed to wait for thread #%d\n", i);
268 if (!GetExitCodeThread(hThreads[i], &dwThreadExitCode))
270 if (dwThreadExitCode != 0)
272 printf(
"CriticalSection failure: Thread #%d returned error code %" PRIu32
"\n", i,
276 (void)CloseHandle(hThreads[i]);
279 if (gTestValueVulnerable != gTestValueSerialized)
281 printf(
"CriticalSection failure: unexpected test value %" PRId32
" (expected %" PRId32
283 gTestValueVulnerable, gTestValueSerialized);
287 DeleteCriticalSection(&critical);
290 free((
void*)hThreads);
296 InitializeCriticalSection(&critical);
298 if (TryEnterCriticalSection(&critical) == FALSE)
300 printf(
"CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
304 if (!(hThread = CreateThread(
nullptr, 0, TestSynchCritical_Test2,
nullptr, 0,
nullptr)))
306 printf(
"CriticalSection failure: Failed to create test_2 thread\n");
309 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
311 printf(
"CriticalSection failure: Failed to wait for thread\n");
314 if (!GetExitCodeThread(hThread, &dwThreadExitCode))
316 if (dwThreadExitCode != 0)
318 printf(
"CriticalSection failure: Thread returned error code %" PRIu32
"\n",
322 (void)CloseHandle(hThread);
324 *pbThreadTerminated = TRUE;
328 *pbThreadTerminated = TRUE;
332int TestSynchCritical(
int argc,
char* argv[])
334 BOOL bThreadTerminated = FALSE;
335 HANDLE hThread =
nullptr;
336 DWORD dwThreadExitCode = 0;
337 DWORD dwDeadLockDetectionTimeMs = 0;
342 dwDeadLockDetectionTimeMs =
343 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
345 printf(
"Deadlock will be assumed after %" PRIu32
" ms.\n", dwDeadLockDetectionTimeMs);
348 CreateThread(
nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0,
nullptr)))
350 printf(
"CriticalSection failure: Failed to create main thread\n");
362 for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
364 if (bThreadTerminated)
370 if (!bThreadTerminated)
372 printf(
"CriticalSection failure: Possible dead lock detected\n");
376 if (!GetExitCodeThread(hThread, &dwThreadExitCode))
378 if (!CloseHandle(hThread))
381 if (dwThreadExitCode != 0)