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 | #include <palsuite.h> |
6 | |
7 | enum class SignalableObjectType |
8 | { |
9 | First = 0, |
10 | |
11 | Invalid = First, |
12 | ManualResetEvent, |
13 | AutoResetEvent, |
14 | Semaphore, |
15 | FullSemaphore, |
16 | Mutex, |
17 | UnlockedMutex, |
18 | |
19 | Last = UnlockedMutex |
20 | }; |
21 | |
22 | enum class WaitableObjectType |
23 | { |
24 | First = 0, |
25 | |
26 | Invalid = First, |
27 | ManualResetEvent, |
28 | UnsignaledManualResetEvent, |
29 | AutoResetEvent, |
30 | UnsignaledAutoResetEvent, |
31 | Semaphore, |
32 | EmptySemaphore, |
33 | Mutex, |
34 | LockedMutex, |
35 | |
36 | Last = LockedMutex |
37 | }; |
38 | |
39 | void operator ++(SignalableObjectType &objectType) |
40 | { |
41 | ++(int &)objectType; |
42 | } |
43 | |
44 | void operator ++(WaitableObjectType &objectType) |
45 | { |
46 | ++(int &)objectType; |
47 | } |
48 | |
49 | struct AssertionFailureException |
50 | { |
51 | const int lineNumber; |
52 | const char *const expression; |
53 | SignalableObjectType signalableObjectType; |
54 | WaitableObjectType waitableObjectType; |
55 | DWORD waitResult; |
56 | DWORD errorCode; |
57 | |
58 | AssertionFailureException(int lineNumber, const char *expression) |
59 | : lineNumber(lineNumber), |
60 | expression(expression), |
61 | signalableObjectType(SignalableObjectType::Invalid), |
62 | waitableObjectType(WaitableObjectType::Invalid), |
63 | waitResult(WAIT_OBJECT_0), |
64 | errorCode(ERROR_SUCCESS) |
65 | { |
66 | } |
67 | }; |
68 | |
69 | #define TestAssert(expression) \ |
70 | do \ |
71 | { \ |
72 | if (!(expression)) \ |
73 | { \ |
74 | throw AssertionFailureException(__LINE__, "" #expression ""); \ |
75 | } \ |
76 | } while (false) |
77 | |
78 | HANDLE CreateObjectToSignal(SignalableObjectType objectType) |
79 | { |
80 | switch (objectType) |
81 | { |
82 | case SignalableObjectType::Invalid: |
83 | return nullptr; |
84 | |
85 | case SignalableObjectType::ManualResetEvent: |
86 | return CreateEvent(nullptr, true, false, nullptr); |
87 | |
88 | case SignalableObjectType::AutoResetEvent: |
89 | return CreateEvent(nullptr, false, false, nullptr); |
90 | |
91 | case SignalableObjectType::Semaphore: |
92 | return CreateSemaphore(nullptr, 0, 1, nullptr); |
93 | |
94 | case SignalableObjectType::FullSemaphore: |
95 | return CreateSemaphore(nullptr, 1, 1, nullptr); |
96 | |
97 | case SignalableObjectType::Mutex: |
98 | return CreateMutex(nullptr, true, nullptr); |
99 | |
100 | case SignalableObjectType::UnlockedMutex: |
101 | return CreateMutex(nullptr, false, nullptr); |
102 | |
103 | default: |
104 | TestAssert(false); |
105 | } |
106 | } |
107 | |
108 | void VerifySignal(HANDLE h, SignalableObjectType objectType) |
109 | { |
110 | switch (objectType) |
111 | { |
112 | case SignalableObjectType::ManualResetEvent: |
113 | TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); |
114 | break; |
115 | |
116 | case SignalableObjectType::AutoResetEvent: |
117 | TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); |
118 | SetEvent(h); |
119 | break; |
120 | |
121 | case SignalableObjectType::Semaphore: |
122 | TestAssert(!ReleaseSemaphore(h, 1, nullptr)); |
123 | break; |
124 | |
125 | case SignalableObjectType::Mutex: |
126 | TestAssert(!ReleaseMutex(h)); |
127 | break; |
128 | |
129 | default: |
130 | TestAssert(false); |
131 | } |
132 | } |
133 | |
134 | void CloseObjectToSignal(HANDLE h, SignalableObjectType objectType) |
135 | { |
136 | if (objectType != SignalableObjectType::Invalid) |
137 | { |
138 | CloseHandle(h); |
139 | } |
140 | } |
141 | |
142 | HANDLE CreateObjectToWaitOn(WaitableObjectType objectType) |
143 | { |
144 | switch (objectType) |
145 | { |
146 | case WaitableObjectType::Invalid: |
147 | return nullptr; |
148 | |
149 | case WaitableObjectType::ManualResetEvent: |
150 | return CreateEvent(nullptr, true, true, nullptr); |
151 | |
152 | case WaitableObjectType::UnsignaledManualResetEvent: |
153 | return CreateEvent(nullptr, true, false, nullptr); |
154 | |
155 | case WaitableObjectType::AutoResetEvent: |
156 | return CreateEvent(nullptr, false, true, nullptr); |
157 | |
158 | case WaitableObjectType::UnsignaledAutoResetEvent: |
159 | return CreateEvent(nullptr, false, false, nullptr); |
160 | |
161 | case WaitableObjectType::Semaphore: |
162 | return CreateSemaphore(nullptr, 1, 1, nullptr); |
163 | |
164 | case WaitableObjectType::EmptySemaphore: |
165 | return CreateSemaphore(nullptr, 0, 1, nullptr); |
166 | |
167 | case WaitableObjectType::Mutex: |
168 | return CreateMutex(nullptr, false, nullptr); |
169 | |
170 | case WaitableObjectType::LockedMutex: |
171 | return CreateMutex(nullptr, true, nullptr); |
172 | |
173 | default: |
174 | TestAssert(false); |
175 | } |
176 | } |
177 | |
178 | void VerifyWait(HANDLE h, WaitableObjectType objectType) |
179 | { |
180 | switch (objectType) |
181 | { |
182 | case WaitableObjectType::ManualResetEvent: |
183 | case WaitableObjectType::UnsignaledManualResetEvent: |
184 | break; |
185 | |
186 | case WaitableObjectType::AutoResetEvent: |
187 | case WaitableObjectType::UnsignaledAutoResetEvent: |
188 | case WaitableObjectType::Semaphore: |
189 | case WaitableObjectType::EmptySemaphore: |
190 | TestAssert(WaitForSingleObject(h, 0) == WAIT_TIMEOUT); |
191 | break; |
192 | |
193 | case WaitableObjectType::Mutex: |
194 | TestAssert(ReleaseMutex(h)); |
195 | TestAssert(!ReleaseMutex(h)); |
196 | TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); |
197 | break; |
198 | |
199 | case WaitableObjectType::LockedMutex: |
200 | TestAssert(ReleaseMutex(h)); |
201 | TestAssert(ReleaseMutex(h)); |
202 | TestAssert(!ReleaseMutex(h)); |
203 | TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); |
204 | TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); |
205 | break; |
206 | |
207 | default: |
208 | TestAssert(false); |
209 | } |
210 | } |
211 | |
212 | void CloseObjectToWaitOn(HANDLE h, WaitableObjectType objectType) |
213 | { |
214 | switch (objectType) |
215 | { |
216 | case WaitableObjectType::ManualResetEvent: |
217 | case WaitableObjectType::UnsignaledManualResetEvent: |
218 | case WaitableObjectType::AutoResetEvent: |
219 | case WaitableObjectType::UnsignaledAutoResetEvent: |
220 | CloseHandle(h); |
221 | break; |
222 | |
223 | case WaitableObjectType::Semaphore: |
224 | case WaitableObjectType::EmptySemaphore: |
225 | ReleaseSemaphore(h, 1, nullptr); |
226 | CloseHandle(h); |
227 | break; |
228 | |
229 | case WaitableObjectType::Mutex: |
230 | ReleaseMutex(h); |
231 | CloseHandle(h); |
232 | break; |
233 | |
234 | case WaitableObjectType::LockedMutex: |
235 | ReleaseMutex(h); |
236 | ReleaseMutex(h); |
237 | CloseHandle(h); |
238 | break; |
239 | |
240 | default: |
241 | break; |
242 | } |
243 | } |
244 | |
245 | bool Verify(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType, DWORD waitResult, DWORD errorCode) |
246 | { |
247 | if (signalableObjectType == SignalableObjectType::Invalid || waitableObjectType == WaitableObjectType::Invalid) |
248 | { |
249 | TestAssert(waitResult == WAIT_FAILED); |
250 | TestAssert(errorCode == ERROR_INVALID_HANDLE); |
251 | return false; |
252 | } |
253 | |
254 | switch (signalableObjectType) |
255 | { |
256 | case SignalableObjectType::FullSemaphore: |
257 | TestAssert(waitResult == WAIT_FAILED); |
258 | TestAssert(errorCode == ERROR_TOO_MANY_POSTS); |
259 | return false; |
260 | |
261 | case SignalableObjectType::UnlockedMutex: |
262 | TestAssert(waitResult == WAIT_FAILED); |
263 | TestAssert(errorCode == ERROR_NOT_OWNER); |
264 | return false; |
265 | |
266 | default: |
267 | break; |
268 | } |
269 | |
270 | switch (waitableObjectType) |
271 | { |
272 | case WaitableObjectType::UnsignaledManualResetEvent: |
273 | case WaitableObjectType::UnsignaledAutoResetEvent: |
274 | case WaitableObjectType::EmptySemaphore: |
275 | TestAssert(waitResult == WAIT_TIMEOUT); |
276 | break; |
277 | |
278 | default: |
279 | TestAssert(waitResult == WAIT_OBJECT_0); |
280 | break; |
281 | } |
282 | TestAssert(errorCode == ERROR_SUCCESS); |
283 | return true; |
284 | } |
285 | |
286 | void Run(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType) |
287 | { |
288 | HANDLE objectToSignal = CreateObjectToSignal(signalableObjectType); |
289 | TestAssert(signalableObjectType == SignalableObjectType::Invalid || objectToSignal != nullptr); |
290 | HANDLE objectToWaitOn = CreateObjectToWaitOn(waitableObjectType); |
291 | TestAssert(waitableObjectType == WaitableObjectType::Invalid || objectToWaitOn != nullptr); |
292 | DWORD waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); |
293 | DWORD errorCode = waitResult == WAIT_FAILED ? GetLastError() : ERROR_SUCCESS; |
294 | |
295 | try |
296 | { |
297 | if (Verify(signalableObjectType, waitableObjectType, waitResult, errorCode)) |
298 | { |
299 | VerifySignal(objectToSignal, signalableObjectType); |
300 | VerifyWait(objectToWaitOn, waitableObjectType); |
301 | } |
302 | } |
303 | catch (AssertionFailureException ex) |
304 | { |
305 | ex.signalableObjectType = signalableObjectType; |
306 | ex.waitableObjectType = waitableObjectType; |
307 | ex.waitResult = waitResult; |
308 | ex.errorCode = errorCode; |
309 | throw ex; |
310 | } |
311 | } |
312 | |
313 | static bool s_apcCalled = false; |
314 | |
315 | void CALLBACK ApcCallback(ULONG_PTR dwParam) |
316 | { |
317 | s_apcCalled = true; |
318 | HANDLE *objects = (HANDLE *)dwParam; |
319 | HANDLE objectToSignal = objects[0]; |
320 | HANDLE objectToWaitOn = objects[1]; |
321 | TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred |
322 | TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet |
323 | SetEvent(objectToWaitOn); |
324 | } |
325 | |
326 | void Run() |
327 | { |
328 | for (SignalableObjectType signalableObjectType = SignalableObjectType::First; |
329 | signalableObjectType <= SignalableObjectType::Last; |
330 | ++signalableObjectType) |
331 | { |
332 | for (WaitableObjectType waitableObjectType = WaitableObjectType::First; |
333 | waitableObjectType <= WaitableObjectType::Last; |
334 | ++waitableObjectType) |
335 | { |
336 | Run(signalableObjectType, waitableObjectType); |
337 | } |
338 | } |
339 | |
340 | DWORD waitResult = WAIT_FAILED; |
341 | try |
342 | { |
343 | HANDLE objectToSignal = CreateObjectToSignal(SignalableObjectType::ManualResetEvent); |
344 | TestAssert(objectToSignal != nullptr); |
345 | HANDLE objectToWaitOn = CreateObjectToWaitOn(WaitableObjectType::AutoResetEvent); |
346 | TestAssert(objectToWaitOn != nullptr); |
347 | HANDLE objects[] = {objectToSignal, objectToWaitOn}; |
348 | |
349 | // Verify that a queued APC is not called if the wait is not alertable |
350 | QueueUserAPC(&ApcCallback, GetCurrentThread(), (ULONG_PTR)&objects); |
351 | waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, false); |
352 | TestAssert(waitResult == WAIT_OBJECT_0); |
353 | TestAssert(!s_apcCalled); |
354 | TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred |
355 | TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred |
356 | |
357 | // Verify that signal, call APC, wait, occur in that order |
358 | ResetEvent(objectToSignal); |
359 | SetEvent(objectToWaitOn); |
360 | waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); |
361 | TestAssert(waitResult == WAIT_IO_COMPLETION); |
362 | TestAssert(s_apcCalled); |
363 | TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred |
364 | TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet |
365 | s_apcCalled = false; |
366 | ResetEvent(objectToSignal); |
367 | SetEvent(objectToWaitOn); |
368 | waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); |
369 | TestAssert(waitResult == WAIT_OBJECT_0); |
370 | TestAssert(!s_apcCalled); |
371 | TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred |
372 | TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred |
373 | |
374 | CloseHandle(objectToSignal); |
375 | CloseHandle(objectToWaitOn); |
376 | } |
377 | catch (AssertionFailureException ex) |
378 | { |
379 | ex.signalableObjectType = SignalableObjectType::ManualResetEvent; |
380 | ex.waitableObjectType = WaitableObjectType::AutoResetEvent; |
381 | ex.waitResult = waitResult; |
382 | throw ex; |
383 | } |
384 | } |
385 | |
386 | int _cdecl main(int argc, char **argv) |
387 | { |
388 | if (PAL_Initialize(argc, argv) != 0) |
389 | { |
390 | return FAIL; |
391 | } |
392 | |
393 | int testReturnCode = PASS; |
394 | try |
395 | { |
396 | Run(); |
397 | } |
398 | catch (AssertionFailureException ex) |
399 | { |
400 | printf( |
401 | "SignalObjectAndWaitTest - Assertion failure (line %d, signalable object type %d, waitable object type %d, wait result 0x%x, error code %u): '%s'\n" , |
402 | ex.lineNumber, |
403 | ex.signalableObjectType, |
404 | ex.waitableObjectType, |
405 | ex.waitResult, |
406 | ex.errorCode, |
407 | ex.expression); |
408 | fflush(stdout); |
409 | testReturnCode = FAIL; |
410 | } |
411 | |
412 | PAL_TerminateEx(testReturnCode); |
413 | return testReturnCode; |
414 | } |
415 | |