1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*=====================================================================
6**
7** Source: CriticalSectionFunctions/test8/test8.c
8**
9** Pyrpose: Ensure critical section functionality is working by
10** having multiple threads racing on a CS under different
11** scenarios
12**
13**
14**===================================================================*/
15#include <palsuite.h>
16
17#define MAX_THREAD_COUNT 128
18#define DEFAULT_THREAD_COUNT 10
19#define DEFAULT_LOOP_COUNT 1000
20
21#ifndef MIN
22#define MIN(a,b) (((a)<(b)) ? (a) : (b))
23#endif
24
25int g_iThreadCount = DEFAULT_THREAD_COUNT;
26int g_iLoopCount = DEFAULT_LOOP_COUNT;
27volatile LONG g_lCriticalCount = 0;
28HANDLE g_hEvStart = NULL;
29
30CRITICAL_SECTION g_cs;
31DWORD PALAPI Thread(LPVOID lpParam)
32{
33 int i, j, iLpCnt;
34 DWORD dwRet = 0;
35 DWORD dwTid = GetCurrentThreadId();
36 LONG lRet;
37 BOOL bSleepInside;
38 BOOL bSleepOutside;
39
40 Trace("[tid=%u] Thread starting\n", dwTid);
41
42 dwRet = WaitForSingleObject(g_hEvStart, INFINITE);
43 if (WAIT_OBJECT_0 != dwRet)
44 {
45 Fail("WaitForSingleObject returned unexpected %u [GetLastError()=%u]\n",
46 dwRet, GetLastError());
47 }
48
49 for (j=0;j<8;j++)
50 {
51 bSleepInside = 2 & j;
52 bSleepOutside = 4 & j;
53
54 iLpCnt = g_iLoopCount;
55 if (bSleepInside || bSleepOutside)
56 {
57 iLpCnt /= 10;
58 }
59
60 for (i=0;i<iLpCnt;i++)
61 {
62 EnterCriticalSection(&g_cs);
63 if (1 & i)
64 {
65 // Simple increment on odd iterations
66 lRet = (g_lCriticalCount += 1);
67 }
68 else
69 {
70 // Interlocked increment on even iterations
71 lRet = InterlockedIncrement(&g_lCriticalCount);
72 }
73
74 if (1 != lRet || 1 != g_lCriticalCount)
75 {
76 Fail("Detected %d threads in area protected by critical section "
77 "[expected: 1 thread]\n", g_lCriticalCount);
78 }
79 if (bSleepInside)
80 {
81 Sleep(rand()%10);
82 }
83 if (1 != g_lCriticalCount)
84 {
85 Fail("Detected %d threads inside area protected by critical section "
86 "[expected: 1 thread]\n", (int)g_lCriticalCount);
87 }
88
89 if (1 & i)
90 {
91 // Simple decrement on odd iterations
92 lRet = (g_lCriticalCount -= 1);
93 }
94 else
95 {
96 // Interlocked decrement on even iterations
97 lRet = InterlockedDecrement(&g_lCriticalCount);
98 }
99 LeaveCriticalSection(&g_cs);
100
101 if (bSleepOutside)
102 {
103 Sleep(rand()%10);
104 }
105 }
106 }
107
108 Trace("[tid=%u] Thread done\n", dwTid);
109
110 return 0;
111}
112
113int __cdecl main(int argc, char **argv)
114{
115 DWORD dwThreadId;
116 DWORD dwRet;
117 HANDLE hThreads[MAX_THREAD_COUNT] = { 0 };
118 int iThreadCount = 0;
119 int i, iVal;
120
121 if ((PAL_Initialize(argc,argv)) != 0)
122 {
123 return(FAIL);
124 }
125
126 srand(time(NULL));
127
128 for (i=1; i<argc; i++)
129 {
130 if ('-' == *argv[i])
131 {
132 switch(*(argv[i]+1))
133 {
134 case 'n':
135 if (i < argc-1)
136 {
137 i += 1;
138 iVal = atoi(argv[i]);
139 if (0 < iVal)
140 {
141 g_iLoopCount = iVal;
142 }
143 }
144 break;
145 case 't':
146 if (i < argc-1)
147 {
148 i += 1;
149 iVal = atoi(argv[i]);
150 if (0 < iVal && MAX_THREAD_COUNT >= iVal)
151 {
152 g_iThreadCount = iVal;
153 }
154 }
155 break;
156 default:
157 break;
158 }
159 }
160 }
161
162 Trace ("Iterations:\t%d\n", g_iLoopCount);
163 Trace ("Threads:\t%d\n", g_iThreadCount);
164
165 g_hEvStart = CreateEvent(NULL, TRUE, FALSE, NULL);
166
167 if (g_hEvStart == NULL)
168 {
169 Fail("CreateEvent call failed. GetLastError "
170 "returned %u.\n", GetLastError());
171 }
172
173 InitializeCriticalSection(&g_cs);
174
175 for (i=0;i<g_iThreadCount;i++)
176 {
177 hThreads[iThreadCount] = CreateThread(NULL,
178 0,
179 &Thread,
180 (LPVOID) NULL,
181 0,
182 &dwThreadId);
183 if (NULL != hThreads[iThreadCount])
184 {
185 iThreadCount++;
186 }
187 }
188
189 Sleep(100);
190
191 Trace("Created %d client threads\n", g_iThreadCount);
192
193 if (2 > iThreadCount)
194 {
195 Fail("Failed to create minimum number if threads, i.e. 2\n");
196 }
197
198 if (!SetEvent(g_hEvStart))
199 {
200 Fail("SetEvent failed [GetLastError()=%u]\n", GetLastError());
201 }
202
203 for (i=0; i<iThreadCount; i+=64)
204 {
205 dwRet = WaitForMultipleObjects(MIN(iThreadCount-i,64),
206 hThreads+i,
207 TRUE,
208 INFINITE);
209 if (WAIT_OBJECT_0 != dwRet)
210 {
211 Fail("Wait for all threads failed\n");
212 }
213 }
214
215 PAL_Terminate();
216 return (PASS);
217}
218