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
7enum 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
22enum 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
39void operator ++(SignalableObjectType &objectType)
40{
41 ++(int &)objectType;
42}
43
44void operator ++(WaitableObjectType &objectType)
45{
46 ++(int &)objectType;
47}
48
49struct 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
78HANDLE 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
108void 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
134void CloseObjectToSignal(HANDLE h, SignalableObjectType objectType)
135{
136 if (objectType != SignalableObjectType::Invalid)
137 {
138 CloseHandle(h);
139 }
140}
141
142HANDLE 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
178void 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
212void 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
245bool 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
286void 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
313static bool s_apcCalled = false;
314
315void 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
326void 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
386int _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