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 | |
25 | int g_iThreadCount = DEFAULT_THREAD_COUNT; |
26 | int g_iLoopCount = DEFAULT_LOOP_COUNT; |
27 | volatile LONG g_lCriticalCount = 0; |
28 | HANDLE g_hEvStart = NULL; |
29 | |
30 | CRITICAL_SECTION g_cs; |
31 | DWORD 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 | |
113 | int __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 | |