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: CreateMutexA_ReleaseMutex/test1/CreateMutexA.c |
8 | ** |
9 | ** Purpose: This test case tests whether a Mutex object created |
10 | ** with CreateMutex really works by mutually excluding |
11 | ** threads from accessing a data structure at the same |
12 | ** time. Here we have a buffer that can be filled or |
13 | ** emptied, we use a Mutex object to ensure that one |
14 | ** operation cannot be started until the other is |
15 | ** finished. If one operation detects that the other |
16 | ** has not finished, it fails. There is a Producer |
17 | ** thread which will try to fill the buffer 25 times, |
18 | ** and a consumer thread which try to empty the buffer |
19 | ** 25 times. If either the fill or empty operations |
20 | ** fails because the Mutex failed to mutually exclude |
21 | ** them, the corresponding thread will set an error |
22 | ** flag and return. This will cause the test case to |
23 | ** fail. |
24 | ** |
25 | ** To increase the probability of identifying problems, |
26 | ** the Fill opeartion has been slowed down with a call |
27 | ** to Sleep. This ensures that one operation will try |
28 | ** to access the shared buffer while the other is in |
29 | ** progress. |
30 | ** |
31 | ** NOTE: this test case also serves as a test case for |
32 | ** WaitForSingleObject. |
33 | ** |
34 | ** |
35 | ** Dependencies: CreateThread |
36 | ** ReleaseMutex |
37 | ** WaitForSingleObject |
38 | ** WaitForMultipleObjects |
39 | ** Sleep |
40 | ** memset |
41 | ** |
42 | |
43 | ** |
44 | **=========================================================*/ |
45 | |
46 | #define UNICODE |
47 | #include <palsuite.h> |
48 | |
49 | /* Define some values that we will using many times */ |
50 | #define MAIN_BUF_SIZE 40 |
51 | #define NUM_OF_CYCLES 40 |
52 | |
53 | /* Buffer Operation return codes */ |
54 | #define OP_OK 0 |
55 | #define OP_ERR 1 |
56 | #define OP_NONE 2 |
57 | |
58 | |
59 | HANDLE hMutex; /* handle to mutex */ |
60 | |
61 | BOOL bProdErr; /* Producer error Flag */ |
62 | BOOL bConErr; /* Consumer error Flag */ |
63 | |
64 | /* Test Buffer */ |
65 | char Buffer[MAIN_BUF_SIZE]; |
66 | |
67 | /* |
68 | * EmptyBuffer implements the empty operation for test buffer. |
69 | */ |
70 | int |
71 | EmptyBuffer() |
72 | { |
73 | int i; |
74 | |
75 | if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED) |
76 | { |
77 | Fail("ERROR: WaitForSingleObject failed.\n" ); |
78 | } |
79 | |
80 | /* Check to see if the buffer is already completely empty */ |
81 | for (i=0; i<MAIN_BUF_SIZE && Buffer[i] == 0; i++); |
82 | if (i == MAIN_BUF_SIZE) |
83 | { |
84 | /* Its empty so just return */ |
85 | if (ReleaseMutex(hMutex) == FALSE) |
86 | { |
87 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
88 | } |
89 | return OP_NONE; |
90 | } |
91 | |
92 | /* Its not empty so we must empty it. */ |
93 | for (i=0; i<MAIN_BUF_SIZE; i++) |
94 | { |
95 | /* Check for empty slots if we find one then the */ |
96 | /* fill operation did no finish. return an error */ |
97 | if (Buffer[i] == 0) |
98 | { |
99 | if (ReleaseMutex(hMutex) == FALSE) |
100 | { |
101 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
102 | } |
103 | return OP_ERR; |
104 | } |
105 | |
106 | Buffer[i] = 0; |
107 | } |
108 | |
109 | if (ReleaseMutex(hMutex) == FALSE) |
110 | { |
111 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
112 | } |
113 | return OP_OK; |
114 | } |
115 | |
116 | /* |
117 | * FillBuffer implements the fill operation for test buffer. |
118 | */ |
119 | int |
120 | FillBuffer() |
121 | { |
122 | int i; |
123 | |
124 | if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED) |
125 | { |
126 | Fail("ERROR: WaitForSingleObject failed.\n" ); |
127 | } |
128 | |
129 | /* Check to see if the buffer is already completely full */ |
130 | for (i=0; i<MAIN_BUF_SIZE && Buffer[i] != 0; i++); |
131 | if (i == MAIN_BUF_SIZE) |
132 | { |
133 | /* Its full so just return */ |
134 | if (ReleaseMutex(hMutex) == FALSE) |
135 | { |
136 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
137 | } |
138 | return OP_NONE; |
139 | } |
140 | |
141 | /* Its not full so we must fill it. */ |
142 | for (i=0; i<MAIN_BUF_SIZE; i++) |
143 | { |
144 | /* Check for filled slots if we find one then the */ |
145 | /* empty operation did not finish. return an error */ |
146 | if (Buffer[i] == 1) |
147 | { |
148 | if (ReleaseMutex(hMutex) == FALSE) |
149 | { |
150 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
151 | } |
152 | return OP_ERR; |
153 | } |
154 | |
155 | Buffer[i] = 1; |
156 | Sleep(10); |
157 | } |
158 | |
159 | if (ReleaseMutex(hMutex) == FALSE) |
160 | { |
161 | Fail("ERROR: ReleaseMutex Failed.\n" ); |
162 | } |
163 | return OP_OK; |
164 | } |
165 | |
166 | |
167 | |
168 | |
169 | /* |
170 | * Producer thread function. |
171 | */ |
172 | DWORD PALAPI Producer(LPVOID lpParam) |
173 | { |
174 | int n = 0; |
175 | int ret; |
176 | |
177 | while (n < NUM_OF_CYCLES) |
178 | { |
179 | if (bConErr == TRUE) |
180 | { |
181 | /* The consumer ran into an error so we'll stop */ |
182 | return 0; |
183 | } |
184 | |
185 | ret = FillBuffer(); |
186 | |
187 | if (ret == OP_OK) |
188 | { |
189 | n++; |
190 | } |
191 | else if (ret == OP_ERR) |
192 | { |
193 | bProdErr = TRUE; |
194 | return 0; |
195 | } |
196 | } |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | /* |
202 | * Consumer thread function. |
203 | */ |
204 | DWORD PALAPI Consumer( LPVOID lpParam ) |
205 | { |
206 | int n = 0; |
207 | int ret; |
208 | |
209 | while (n < NUM_OF_CYCLES) |
210 | { |
211 | if (bProdErr == TRUE) |
212 | { |
213 | /* The consumer ran into an error so we'll stop */ |
214 | return 0; |
215 | } |
216 | |
217 | ret = EmptyBuffer(); |
218 | |
219 | if (ret == OP_OK) |
220 | { |
221 | n++; |
222 | } |
223 | else if (ret == OP_ERR) |
224 | { |
225 | bConErr = TRUE; |
226 | return 0; |
227 | } |
228 | } |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | |
234 | int __cdecl main (int argc, char **argv) |
235 | { |
236 | DWORD dwThreadId; |
237 | DWORD dwWaitRet; |
238 | |
239 | HANDLE hThread1; /* handle to consumer thread */ |
240 | HANDLE hThread2; /* handle to producer thread */ |
241 | HANDLE handleArray[2]; |
242 | |
243 | |
244 | if(0 != (PAL_Initialize(argc, argv))) |
245 | { |
246 | return ( FAIL ); |
247 | } |
248 | |
249 | /* Initialize our error flags */ |
250 | bProdErr = FALSE; |
251 | bConErr = FALSE; |
252 | |
253 | /* |
254 | * Initialize the Buffer to be empty |
255 | */ |
256 | memset(Buffer, 0, MAIN_BUF_SIZE); |
257 | |
258 | /* |
259 | * Create Mutex |
260 | */ |
261 | hMutex = CreateMutexA (NULL, FALSE, NULL); |
262 | |
263 | if (NULL == hMutex) |
264 | { |
265 | Fail("hMutex = CreateMutexA() - returned NULL\n" |
266 | "Failing Test.\nGetLastError returned %u\n" , GetLastError()); |
267 | } |
268 | |
269 | |
270 | /* |
271 | * Create the Producer thread |
272 | */ |
273 | hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Producer, |
274 | 0, 0, &dwThreadId); |
275 | |
276 | if ( NULL == hThread1 ) |
277 | { |
278 | CloseHandle(hMutex); |
279 | |
280 | Fail("CreateThread() returned NULL. Failing test.\n" |
281 | "GetLastError returned %u\n" , GetLastError()); |
282 | } |
283 | |
284 | /* |
285 | * Create the Consumer thread |
286 | */ |
287 | hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Consumer, |
288 | 0, 0, &dwThreadId); |
289 | |
290 | if ( NULL == hThread2 ) |
291 | { |
292 | CloseHandle(hMutex); |
293 | |
294 | /* Set the error flag and give thread1 some time to exit */ |
295 | bConErr = FALSE; |
296 | Sleep(250); |
297 | |
298 | Fail("CreateThread() returned NULL. Failing test.\n" |
299 | "GetLastError returned %u\n" , GetLastError()); |
300 | } |
301 | |
302 | /* |
303 | * Wait for both threads to complete (Max 45 Seconds) |
304 | */ |
305 | handleArray[0] = hThread1; |
306 | handleArray[1] = hThread2; |
307 | dwWaitRet = WaitForMultipleObjects (2, handleArray, TRUE, 450000); |
308 | if (dwWaitRet == WAIT_FAILED) |
309 | { |
310 | Fail("ERROR: WaitForMultipleObjects failed.\n" ); |
311 | } |
312 | else if (dwWaitRet == WAIT_TIMEOUT) |
313 | { |
314 | /* Set the error flags and give the threads some time to exit */ |
315 | bProdErr = FALSE; |
316 | bConErr = FALSE; |
317 | Sleep(250); |
318 | |
319 | Fail("ERROR: Timeout interval exceeded.\n" ); |
320 | } |
321 | |
322 | /* |
323 | * Clean up |
324 | */ |
325 | if (CloseHandle(hThread1) == FALSE || |
326 | CloseHandle(hThread2) == FALSE || |
327 | CloseHandle(hMutex) == FALSE) |
328 | { |
329 | Fail("ERROR: CloseHandle failed.\n" ); |
330 | } |
331 | |
332 | |
333 | /* |
334 | * Check our error flags |
335 | */ |
336 | if (bProdErr == TRUE || bConErr == TRUE) |
337 | { |
338 | Fail("ERROR: A collision occurred, so the mutex failed.\n" ); |
339 | } |
340 | |
341 | PAL_Terminate(); |
342 | return ( PASS ); |
343 | |
344 | } |
345 | |