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 | |
8 | |
9 | Module Name: |
10 | |
11 | wait.cpp |
12 | |
13 | Abstract: |
14 | |
15 | Implementation of waiting functions as described in |
16 | the WIN32 API |
17 | |
18 | Revision History: |
19 | |
20 | |
21 | |
22 | --*/ |
23 | |
24 | #include "pal/thread.hpp" |
25 | #include "pal/synchobjects.hpp" |
26 | #include "pal/handlemgr.hpp" |
27 | #include "pal/event.hpp" |
28 | #include "pal/mutex.hpp" |
29 | #include "pal/semaphore.hpp" |
30 | #include "pal/malloc.hpp" |
31 | #include "pal/dbgmsg.h" |
32 | |
33 | SET_DEFAULT_DEBUG_CHANNEL(SYNC); |
34 | |
35 | #define MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE (MAXIMUM_WAIT_OBJECTS / 4) |
36 | |
37 | using namespace CorUnix; |
38 | |
39 | static PalObjectTypeId sg_rgWaitObjectsIds[] = |
40 | { |
41 | otiAutoResetEvent, |
42 | otiManualResetEvent, |
43 | otiMutex, |
44 | otiNamedMutex, |
45 | otiSemaphore, |
46 | otiProcess, |
47 | otiThread |
48 | }; |
49 | static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds, |
50 | sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0])); |
51 | |
52 | static PalObjectTypeId sg_rgSignalableObjectIds[] = |
53 | { |
54 | otiAutoResetEvent, |
55 | otiManualResetEvent, |
56 | otiMutex, |
57 | otiNamedMutex, |
58 | otiSemaphore |
59 | }; |
60 | static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, _countof(sg_rgSignalableObjectIds)); |
61 | |
62 | /*++ |
63 | Function: |
64 | WaitForSingleObject |
65 | |
66 | See MSDN doc. |
67 | --*/ |
68 | DWORD |
69 | PALAPI |
70 | WaitForSingleObject(IN HANDLE hHandle, |
71 | IN DWORD dwMilliseconds) |
72 | { |
73 | DWORD dwRet; |
74 | |
75 | PERF_ENTRY(WaitForSingleObject); |
76 | ENTRY("WaitForSingleObject(hHandle=%p, dwMilliseconds=%u)\n" , |
77 | hHandle, dwMilliseconds); |
78 | |
79 | CPalThread * pThread = InternalGetCurrentThread(); |
80 | |
81 | dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, |
82 | dwMilliseconds, FALSE); |
83 | |
84 | LOGEXIT("WaitForSingleObject returns DWORD %u\n" , dwRet); |
85 | PERF_EXIT(WaitForSingleObject); |
86 | return dwRet; |
87 | } |
88 | |
89 | |
90 | /*++ |
91 | Function: |
92 | WaitForSingleObjectPrioritized |
93 | |
94 | Similar to WaitForSingleObject, except uses a LIFO release policy for waiting threads by prioritizing new waiters (registering |
95 | them at the beginning of the wait queue rather than at the end). |
96 | --*/ |
97 | DWORD |
98 | PALAPI |
99 | PAL_WaitForSingleObjectPrioritized(IN HANDLE hHandle, |
100 | IN DWORD dwMilliseconds) |
101 | { |
102 | DWORD dwRet; |
103 | |
104 | PERF_ENTRY(PAL_WaitForSingleObjectPrioritized); |
105 | ENTRY("PAL_WaitForSingleObjectPrioritized(hHandle=%p, dwMilliseconds=%u)\n" , |
106 | hHandle, dwMilliseconds); |
107 | |
108 | CPalThread * pThread = InternalGetCurrentThread(); |
109 | |
110 | dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, |
111 | dwMilliseconds, FALSE, TRUE /* bPrioritize */); |
112 | |
113 | LOGEXIT("PAL_WaitForSingleObjectPrioritized returns DWORD %u\n" , dwRet); |
114 | PERF_EXIT(PAL_WaitForSingleObjectPrioritized); |
115 | return dwRet; |
116 | } |
117 | |
118 | |
119 | /*++ |
120 | Function: |
121 | WaitForSingleObjectEx |
122 | |
123 | See MSDN doc. |
124 | --*/ |
125 | DWORD |
126 | PALAPI |
127 | WaitForSingleObjectEx(IN HANDLE hHandle, |
128 | IN DWORD dwMilliseconds, |
129 | IN BOOL bAlertable) |
130 | { |
131 | DWORD dwRet; |
132 | |
133 | PERF_ENTRY(WaitForSingleObjectEx); |
134 | ENTRY("WaitForSingleObjectEx(hHandle=%p, dwMilliseconds=%u, bAlertable=%s)\n" , |
135 | hHandle, dwMilliseconds, bAlertable ? "TRUE" : "FALSE" ); |
136 | |
137 | CPalThread * pThread = InternalGetCurrentThread(); |
138 | |
139 | dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, |
140 | dwMilliseconds, bAlertable); |
141 | |
142 | LOGEXIT("WaitForSingleObjectEx returns DWORD %u\n" , dwRet); |
143 | PERF_EXIT(WaitForSingleObjectEx); |
144 | return dwRet; |
145 | } |
146 | |
147 | |
148 | /*++ |
149 | Function: |
150 | WaitForMultipleObjects |
151 | |
152 | See MSDN doc. |
153 | |
154 | --*/ |
155 | DWORD |
156 | PALAPI |
157 | WaitForMultipleObjects(IN DWORD nCount, |
158 | IN CONST HANDLE *lpHandles, |
159 | IN BOOL bWaitAll, |
160 | IN DWORD dwMilliseconds) |
161 | { |
162 | DWORD dwRet; |
163 | |
164 | PERF_ENTRY(WaitForMultipleObjects); |
165 | ENTRY("WaitForMultipleObjects(nCount=%d, lpHandles=%p," |
166 | " bWaitAll=%d, dwMilliseconds=%u)\n" , |
167 | nCount, lpHandles, bWaitAll, dwMilliseconds); |
168 | |
169 | CPalThread * pThread = InternalGetCurrentThread(); |
170 | |
171 | dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, |
172 | bWaitAll, dwMilliseconds, FALSE); |
173 | |
174 | LOGEXIT("WaitForMultipleObjects returns DWORD %u\n" , dwRet); |
175 | PERF_EXIT(WaitForMultipleObjects); |
176 | return dwRet; |
177 | } |
178 | |
179 | /*++ |
180 | Function: |
181 | WaitForMultipleObjectsEx |
182 | |
183 | See MSDN doc for info about this function. |
184 | --*/ |
185 | DWORD |
186 | PALAPI |
187 | WaitForMultipleObjectsEx(IN DWORD nCount, |
188 | IN CONST HANDLE *lpHandles, |
189 | IN BOOL bWaitAll, |
190 | IN DWORD dwMilliseconds, |
191 | IN BOOL bAlertable) |
192 | { |
193 | DWORD dwRet; |
194 | |
195 | PERF_ENTRY(WaitForMultipleObjectsEx); |
196 | ENTRY("WaitForMultipleObjectsEx(nCount=%d, lpHandles=%p," |
197 | " bWaitAll=%d, dwMilliseconds=%u, bAlertable=%s)\n" , |
198 | nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable ? "TRUE" : "FALSE" ); |
199 | |
200 | CPalThread * pThread = InternalGetCurrentThread(); |
201 | |
202 | dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, bWaitAll, |
203 | dwMilliseconds, bAlertable); |
204 | |
205 | LOGEXIT("WaitForMultipleObjectsEx returns DWORD %u\n" , dwRet); |
206 | PERF_EXIT(WaitForMultipleObjectsEx); |
207 | return dwRet; |
208 | } |
209 | |
210 | /*++ |
211 | Function: |
212 | SignalObjectAndWait |
213 | |
214 | See MSDN doc for info about this function. |
215 | --*/ |
216 | DWORD |
217 | PALAPI |
218 | SignalObjectAndWait( |
219 | IN HANDLE hObjectToSignal, |
220 | IN HANDLE hObjectToWaitOn, |
221 | IN DWORD dwMilliseconds, |
222 | IN BOOL bAlertable) |
223 | { |
224 | PERF_ENTRY(SignalObjectAndWait); |
225 | ENTRY( |
226 | "SignalObjectAndWait(hObjectToSignal=%p, hObjectToWaitOn=%p, dwMilliseconds=%u, bAlertable=%s)\n" , |
227 | hObjectToSignal, |
228 | hObjectToWaitOn, |
229 | dwMilliseconds, |
230 | bAlertable ? "TRUE" : "FALSE" ); |
231 | |
232 | CPalThread *thread = InternalGetCurrentThread(); |
233 | DWORD result = InternalSignalObjectAndWait(thread, hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); |
234 | |
235 | LOGEXIT("SignalObjectAndWait returns DWORD %u\n" , result); |
236 | PERF_EXIT(SignalObjectAndWait); |
237 | return result; |
238 | } |
239 | |
240 | /*++ |
241 | Function: |
242 | Sleep |
243 | |
244 | See MSDN doc. |
245 | --*/ |
246 | VOID |
247 | PALAPI |
248 | Sleep(IN DWORD dwMilliseconds) |
249 | { |
250 | PERF_ENTRY(Sleep); |
251 | ENTRY("Sleep(dwMilliseconds=%u)\n" , dwMilliseconds); |
252 | |
253 | CPalThread * pThread = InternalGetCurrentThread(); |
254 | |
255 | DWORD internalSleepRet = InternalSleepEx(pThread, dwMilliseconds, FALSE); |
256 | |
257 | if (internalSleepRet != 0) |
258 | { |
259 | ERROR("Sleep(dwMilliseconds=%u) failed [error=%u]\n" , dwMilliseconds, internalSleepRet); |
260 | pThread->SetLastError(internalSleepRet); |
261 | } |
262 | |
263 | LOGEXIT("Sleep returns VOID\n" ); |
264 | PERF_EXIT(Sleep); |
265 | } |
266 | |
267 | |
268 | /*++ |
269 | Function: |
270 | SleepEx |
271 | |
272 | See MSDN doc. |
273 | --*/ |
274 | DWORD |
275 | PALAPI |
276 | SleepEx(IN DWORD dwMilliseconds, |
277 | IN BOOL bAlertable) |
278 | { |
279 | DWORD dwRet; |
280 | |
281 | PERF_ENTRY(SleepEx); |
282 | ENTRY("SleepEx(dwMilliseconds=%u, bAlertable=%d)\n" , dwMilliseconds, bAlertable); |
283 | |
284 | CPalThread * pThread = InternalGetCurrentThread(); |
285 | |
286 | dwRet = InternalSleepEx(pThread, dwMilliseconds, bAlertable); |
287 | |
288 | LOGEXIT("SleepEx returns DWORD %u\n" , dwRet); |
289 | PERF_EXIT(SleepEx); |
290 | |
291 | return dwRet; |
292 | } |
293 | |
294 | /*++ |
295 | Function: |
296 | QueueUserAPC |
297 | |
298 | See MSDN doc. |
299 | --*/ |
300 | DWORD |
301 | PALAPI |
302 | QueueUserAPC( |
303 | PAPCFUNC pfnAPC, |
304 | HANDLE hThread, |
305 | ULONG_PTR dwData) |
306 | { |
307 | CPalThread * pCurrentThread = NULL; |
308 | CPalThread * pTargetThread = NULL; |
309 | IPalObject * pTargetThreadObject = NULL; |
310 | PAL_ERROR palErr; |
311 | DWORD dwRet; |
312 | |
313 | PERF_ENTRY(QueueUserAPC); |
314 | ENTRY("QueueUserAPC(pfnAPC=%p, hThread=%p, dwData=%#x)\n" , |
315 | pfnAPC, hThread, dwData); |
316 | |
317 | /* NOTE: Windows does not check the validity of pfnAPC, even if it is |
318 | NULL. It just does an access violation later on when the APC call |
319 | is attempted */ |
320 | |
321 | pCurrentThread = InternalGetCurrentThread(); |
322 | |
323 | palErr = InternalGetThreadDataFromHandle( |
324 | pCurrentThread, |
325 | hThread, |
326 | 0, // THREAD_SET_CONTEXT |
327 | &pTargetThread, |
328 | &pTargetThreadObject |
329 | ); |
330 | |
331 | if (NO_ERROR != palErr) |
332 | { |
333 | ERROR("Unable to obtain thread data for handle %p (error %x)!\n" , |
334 | hThread, palErr); |
335 | goto QueueUserAPC_exit; |
336 | } |
337 | |
338 | |
339 | palErr = g_pSynchronizationManager->QueueUserAPC(pCurrentThread, pTargetThread, |
340 | pfnAPC, dwData); |
341 | |
342 | QueueUserAPC_exit: |
343 | if (pTargetThreadObject) |
344 | { |
345 | pTargetThreadObject->ReleaseReference(pCurrentThread); |
346 | } |
347 | |
348 | dwRet = (NO_ERROR == palErr) ? 1 : 0; |
349 | |
350 | LOGEXIT("QueueUserAPC returns DWORD %d\n" , dwRet); |
351 | PERF_EXIT(QueueUserAPC); |
352 | return dwRet; |
353 | } |
354 | |
355 | DWORD CorUnix::InternalWaitForMultipleObjectsEx( |
356 | CPalThread * pThread, |
357 | DWORD nCount, |
358 | CONST HANDLE *lpHandles, |
359 | BOOL bWaitAll, |
360 | DWORD dwMilliseconds, |
361 | BOOL bAlertable, |
362 | BOOL bPrioritize) |
363 | { |
364 | DWORD dwRet = WAIT_FAILED; |
365 | PAL_ERROR palErr = NO_ERROR; |
366 | int i, iSignaledObjCount, iSignaledObjIndex = -1; |
367 | bool fWAll = (bool)bWaitAll, fNeedToBlock = false; |
368 | bool fAbandoned = false; |
369 | WaitType wtWaitType; |
370 | |
371 | IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL }; |
372 | ISynchWaitController * pISyncStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL }; |
373 | IPalObject ** ppIPalObjs = pIPalObjStackArray; |
374 | ISynchWaitController ** ppISyncWaitCtrlrs = pISyncStackArray; |
375 | |
376 | if ((nCount == 0) || (nCount > MAXIMUM_WAIT_OBJECTS)) |
377 | { |
378 | ppIPalObjs = NULL; // make delete at the end safe |
379 | ppISyncWaitCtrlrs = NULL; // make delete at the end safe |
380 | ERROR("Invalid object count=%d [range: 1 to %d]\n" , |
381 | nCount, MAXIMUM_WAIT_OBJECTS) |
382 | pThread->SetLastError(ERROR_INVALID_PARAMETER); |
383 | goto WFMOExIntExit; |
384 | } |
385 | else if (nCount == 1) |
386 | { |
387 | fWAll = false; // makes no difference when nCount is 1 |
388 | wtWaitType = SingleObject; |
389 | } |
390 | else |
391 | { |
392 | wtWaitType = fWAll ? MultipleObjectsWaitAll : MultipleObjectsWaitOne; |
393 | if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE) |
394 | { |
395 | ppIPalObjs = InternalNewArray<IPalObject*>(nCount); |
396 | ppISyncWaitCtrlrs = InternalNewArray<ISynchWaitController*>(nCount); |
397 | if ((NULL == ppIPalObjs) || (NULL == ppISyncWaitCtrlrs)) |
398 | { |
399 | ERROR("Out of memory allocating internal structures\n" ); |
400 | pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
401 | goto WFMOExIntExit; |
402 | } |
403 | } |
404 | } |
405 | |
406 | palErr = g_pObjectManager->ReferenceMultipleObjectsByHandleArray(pThread, |
407 | (VOID **)lpHandles, |
408 | nCount, |
409 | &sg_aotWaitObject, |
410 | SYNCHRONIZE, |
411 | ppIPalObjs); |
412 | if (NO_ERROR != palErr) |
413 | { |
414 | ERROR("Unable to obtain object for some or all of the handles [error=%u]\n" , |
415 | palErr); |
416 | if (palErr == ERROR_INVALID_HANDLE) |
417 | pThread->SetLastError(ERROR_INVALID_HANDLE); |
418 | else |
419 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
420 | goto WFMOExIntExit; |
421 | } |
422 | |
423 | if (nCount > 1) |
424 | { |
425 | // Check for any cross-process sync objects. "Wait for any" and "wait for all" operations are not supported on |
426 | // cross-process sync objects in the PAL. |
427 | for (DWORD i = 0; i < nCount; ++i) |
428 | { |
429 | if (ppIPalObjs[i]->GetObjectType()->GetId() == otiNamedMutex) |
430 | { |
431 | ERROR("Attempt to wait for any or all handles including a cross-process sync object" , ERROR_NOT_SUPPORTED); |
432 | pThread->SetLastError(ERROR_NOT_SUPPORTED); |
433 | goto WFMOExIntCleanup; |
434 | } |
435 | } |
436 | } |
437 | else if (ppIPalObjs[0]->GetObjectType()->GetId() == otiNamedMutex) |
438 | { |
439 | SharedMemoryProcessDataHeader * = |
440 | SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(ppIPalObjs[0]); |
441 | _ASSERTE(processDataHeader != nullptr); |
442 | try |
443 | { |
444 | MutexTryAcquireLockResult tryAcquireLockResult = |
445 | static_cast<NamedMutexProcessData *>(processDataHeader->GetData())->TryAcquireLock(dwMilliseconds); |
446 | switch (tryAcquireLockResult) |
447 | { |
448 | case MutexTryAcquireLockResult::AcquiredLock: |
449 | dwRet = WAIT_OBJECT_0; |
450 | break; |
451 | |
452 | case MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned: |
453 | dwRet = WAIT_ABANDONED_0; |
454 | break; |
455 | |
456 | case MutexTryAcquireLockResult::TimedOut: |
457 | dwRet = WAIT_TIMEOUT; |
458 | break; |
459 | |
460 | default: |
461 | _ASSERTE(false); |
462 | break; |
463 | } |
464 | } |
465 | catch (SharedMemoryException ex) |
466 | { |
467 | pThread->SetLastError(ex.GetErrorCode()); |
468 | } |
469 | goto WFMOExIntCleanup; |
470 | } |
471 | |
472 | if (fWAll) |
473 | { |
474 | // For a wait-all operation, check for duplicate wait objects in the array. This just uses a brute-force O(n^2) |
475 | // algorithm, but since MAXIMUM_WAIT_OBJECTS is small, the worst case is not so bad, and the average case would involve |
476 | // significantly fewer items. |
477 | for (DWORD i = 0; i < nCount - 1; ++i) |
478 | { |
479 | IPalObject *const objectToCheck = ppIPalObjs[i]; |
480 | for (DWORD j = i + 1; j < nCount; ++j) |
481 | { |
482 | if (ppIPalObjs[j] == objectToCheck) |
483 | { |
484 | ERROR("Duplicate handle provided for a wait-all operation [error=%u]\n" , ERROR_INVALID_PARAMETER); |
485 | pThread->SetLastError(ERROR_INVALID_PARAMETER); |
486 | goto WFMOExIntCleanup; |
487 | } |
488 | } |
489 | } |
490 | } |
491 | |
492 | palErr = g_pSynchronizationManager->GetSynchWaitControllersForObjects( |
493 | pThread, ppIPalObjs, nCount, ppISyncWaitCtrlrs); |
494 | if (NO_ERROR != palErr) |
495 | { |
496 | ERROR("Unable to obtain ISynchWaitController interface for some or all " |
497 | "of the objects [error=%u]\n" , palErr); |
498 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
499 | goto WFMOExIntCleanup; |
500 | } |
501 | |
502 | if (bAlertable) |
503 | { |
504 | // First check for pending APC. We need to do that while holding the global |
505 | // synch lock implicitely grabbed by GetSynchWaitControllersForObjects |
506 | if (g_pSynchronizationManager->AreAPCsPending(pThread)) |
507 | { |
508 | // If there is any pending APC we need to release the |
509 | // implicit global synch lock before calling into it |
510 | for (i = 0; (i < (int)nCount) && (NULL != ppISyncWaitCtrlrs[i]); i++) |
511 | { |
512 | ppISyncWaitCtrlrs[i]->ReleaseController(); |
513 | ppISyncWaitCtrlrs[i] = NULL; |
514 | } |
515 | palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); |
516 | if (NO_ERROR == palErr) |
517 | { |
518 | dwRet = WAIT_IO_COMPLETION; |
519 | } |
520 | else |
521 | { |
522 | ASSERT("Awakened for APC, but no APC is pending\n" ); |
523 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
524 | dwRet = WAIT_FAILED; |
525 | } |
526 | goto WFMOExIntCleanup; |
527 | } |
528 | } |
529 | |
530 | iSignaledObjCount = 0; |
531 | iSignaledObjIndex = -1; |
532 | for (i=0;i<(int)nCount;i++) |
533 | { |
534 | bool fValue; |
535 | palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fAbandoned); |
536 | if (NO_ERROR != palErr) |
537 | { |
538 | ERROR("ISynchWaitController::CanThreadWaitWithoutBlocking() failed for " |
539 | "%d-th object [handle=%p error=%u]\n" , i, lpHandles[i], palErr); |
540 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
541 | goto WFMOExIntReleaseControllers; |
542 | } |
543 | if (fValue) |
544 | { |
545 | iSignaledObjCount++; |
546 | iSignaledObjIndex = i; |
547 | if (!fWAll) |
548 | break; |
549 | } |
550 | } |
551 | |
552 | fNeedToBlock = (iSignaledObjCount == 0) || (fWAll && (iSignaledObjCount < (int)nCount)); |
553 | if (!fNeedToBlock) |
554 | { |
555 | // At least one object signaled, or bWaitAll==TRUE and all object signaled. |
556 | // No need to wait, let's unsignal the object(s) and return without blocking |
557 | int iStartIdx, iEndIdx; |
558 | |
559 | if (fWAll) |
560 | { |
561 | iStartIdx = 0; |
562 | iEndIdx = nCount; |
563 | } |
564 | else |
565 | { |
566 | iStartIdx = iSignaledObjIndex; |
567 | iEndIdx = iStartIdx + 1; |
568 | } |
569 | |
570 | // Unsignal objects |
571 | if( iStartIdx < 0 ) |
572 | { |
573 | ERROR("Buffer underflow due to iStartIdx < 0" ); |
574 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
575 | dwRet = WAIT_FAILED; |
576 | goto WFMOExIntCleanup; |
577 | } |
578 | for (i = iStartIdx; i < iEndIdx; i++) |
579 | { |
580 | palErr = ppISyncWaitCtrlrs[i]->ReleaseWaitingThreadWithoutBlocking(); |
581 | if (NO_ERROR != palErr) |
582 | { |
583 | ERROR("ReleaseWaitingThreadWithoutBlocking() failed for %d-th " |
584 | "object [handle=%p error=%u]\n" , |
585 | i, lpHandles[i], palErr); |
586 | pThread->SetLastError(palErr); |
587 | goto WFMOExIntReleaseControllers; |
588 | } |
589 | } |
590 | |
591 | dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0); |
592 | } |
593 | else if (0 == dwMilliseconds) |
594 | { |
595 | // Not enough objects signaled, but timeout is zero: no actual wait |
596 | dwRet = WAIT_TIMEOUT; |
597 | fNeedToBlock = false; |
598 | } |
599 | else |
600 | { |
601 | // Register the thread for waiting on all objects |
602 | for (i=0;i<(int)nCount;i++) |
603 | { |
604 | palErr = ppISyncWaitCtrlrs[i]->RegisterWaitingThread( |
605 | wtWaitType, |
606 | i, |
607 | (TRUE == bAlertable), |
608 | bPrioritize != FALSE); |
609 | if (NO_ERROR != palErr) |
610 | { |
611 | ERROR("RegisterWaitingThread() failed for %d-th object " |
612 | "[handle=%p error=%u]\n" , i, lpHandles[i], palErr); |
613 | pThread->SetLastError(palErr); |
614 | goto WFMOExIntReleaseControllers; |
615 | } |
616 | } |
617 | } |
618 | |
619 | WFMOExIntReleaseControllers: |
620 | // Release all controllers before going to sleep |
621 | for (i = 0; i < (int)nCount; i++) |
622 | { |
623 | ppISyncWaitCtrlrs[i]->ReleaseController(); |
624 | ppISyncWaitCtrlrs[i] = NULL; |
625 | } |
626 | if (NO_ERROR != palErr) |
627 | goto WFMOExIntCleanup; |
628 | |
629 | if (fNeedToBlock) |
630 | { |
631 | ThreadWakeupReason twrWakeupReason; |
632 | |
633 | // |
634 | // Going to sleep |
635 | // |
636 | palErr = g_pSynchronizationManager->BlockThread(pThread, |
637 | dwMilliseconds, |
638 | (TRUE == bAlertable), |
639 | false, |
640 | &twrWakeupReason, |
641 | (DWORD *)&iSignaledObjIndex); |
642 | // |
643 | // Awakened |
644 | // |
645 | if (NO_ERROR != palErr) |
646 | { |
647 | ERROR("IPalSynchronizationManager::BlockThread failed for thread " |
648 | "pThread=%p [error=%u]\n" , pThread, palErr); |
649 | pThread->SetLastError(palErr); |
650 | goto WFMOExIntCleanup; |
651 | } |
652 | switch (twrWakeupReason) |
653 | { |
654 | case WaitSucceeded: |
655 | dwRet = WAIT_OBJECT_0; // offset added later |
656 | break; |
657 | case MutexAbondoned: |
658 | dwRet = WAIT_ABANDONED_0; // offset added later |
659 | break; |
660 | case WaitTimeout: |
661 | dwRet = WAIT_TIMEOUT; |
662 | break; |
663 | case Alerted: |
664 | _ASSERT_MSG(bAlertable, |
665 | "Awakened for APC from a non-alertable wait\n" ); |
666 | |
667 | dwRet = WAIT_IO_COMPLETION; |
668 | palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); |
669 | |
670 | _ASSERT_MSG(NO_ERROR == palErr, |
671 | "Awakened for APC, but no APC is pending\n" ); |
672 | break; |
673 | case WaitFailed: |
674 | default: |
675 | ERROR("Thread %p awakened with some failure\n" , pThread); |
676 | dwRet = WAIT_FAILED; |
677 | break; |
678 | } |
679 | } |
680 | |
681 | if (!fWAll && ((WAIT_OBJECT_0 == dwRet) || (WAIT_ABANDONED_0 == dwRet))) |
682 | { |
683 | _ASSERT_MSG(0 <= iSignaledObjIndex, |
684 | "Failed to identify signaled/abandoned object\n" ); |
685 | _ASSERT_MSG(iSignaledObjIndex >= 0 && nCount > static_cast<DWORD>(iSignaledObjIndex), |
686 | "SignaledObjIndex object out of range " |
687 | "[index=%d obj_count=%u\n" , |
688 | iSignaledObjCount, nCount); |
689 | |
690 | if (iSignaledObjIndex < 0) |
691 | { |
692 | pThread->SetLastError(ERROR_INTERNAL_ERROR); |
693 | dwRet = WAIT_FAILED; |
694 | goto WFMOExIntCleanup; |
695 | } |
696 | dwRet += iSignaledObjIndex; |
697 | } |
698 | |
699 | WFMOExIntCleanup: |
700 | for (i = 0; i < (int)nCount; i++) |
701 | { |
702 | ppIPalObjs[i]->ReleaseReference(pThread); |
703 | ppIPalObjs[i] = NULL; |
704 | } |
705 | |
706 | WFMOExIntExit: |
707 | if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE) |
708 | { |
709 | InternalDeleteArray(ppIPalObjs); |
710 | InternalDeleteArray(ppISyncWaitCtrlrs); |
711 | } |
712 | |
713 | return dwRet; |
714 | } |
715 | |
716 | DWORD CorUnix::InternalSignalObjectAndWait( |
717 | CPalThread *thread, |
718 | HANDLE hObjectToSignal, |
719 | HANDLE hObjectToWaitOn, |
720 | DWORD dwMilliseconds, |
721 | BOOL bAlertable) |
722 | { |
723 | DWORD result = WAIT_FAILED; |
724 | PAL_ERROR palError = NO_ERROR; |
725 | IPalObject *objectToSignal = nullptr; |
726 | IPalObject *objectToWaitOn = nullptr; |
727 | |
728 | // Validate and add a reference to the object to signal |
729 | palError = |
730 | g_pObjectManager->ReferenceObjectByHandle( |
731 | thread, |
732 | hObjectToSignal, |
733 | &sg_aotSignalableObject, |
734 | 0, // should be MUTEX_MODIFY_STATE or equivalent for a signalable object, currently ignored (no Win32 security) |
735 | &objectToSignal); |
736 | if (palError != NO_ERROR) |
737 | { |
738 | ERROR("Unable to obtain object for handle %p (error %u)!\n" , hObjectToSignal, palError); |
739 | goto InternalSignalObjectAndWait_Error; |
740 | } |
741 | |
742 | // Validate and add a reference to the object to wait on. Error checking is done before signaling. |
743 | palError = |
744 | g_pObjectManager->ReferenceObjectByHandle( |
745 | thread, |
746 | hObjectToWaitOn, |
747 | &sg_aotWaitObject, |
748 | SYNCHRONIZE, |
749 | &objectToWaitOn); |
750 | if (palError != NO_ERROR) |
751 | { |
752 | ERROR("Unable to obtain object for handle %p (error %u)!\n" , hObjectToWaitOn, palError); |
753 | goto InternalSignalObjectAndWait_Error; |
754 | } |
755 | |
756 | // Signal |
757 | switch (objectToSignal->GetObjectType()->GetId()) |
758 | { |
759 | case otiAutoResetEvent: |
760 | case otiManualResetEvent: |
761 | palError = InternalSetEvent(thread, hObjectToSignal, true /* fSetEvent */); |
762 | break; |
763 | |
764 | case otiMutex: |
765 | case otiNamedMutex: |
766 | palError = InternalReleaseMutex(thread, hObjectToSignal); |
767 | break; |
768 | |
769 | case otiSemaphore: |
770 | palError = InternalReleaseSemaphore(thread, hObjectToSignal, 1 /* lReleaseCount */, nullptr /* lpPreviousCount */); |
771 | break; |
772 | |
773 | default: |
774 | palError = ERROR_INVALID_HANDLE; |
775 | break; |
776 | } |
777 | if (palError != NO_ERROR) |
778 | { |
779 | ERROR("Unable to signal object for handle %p (error %u)!\n" , hObjectToSignal, palError); |
780 | goto InternalSignalObjectAndWait_Error; |
781 | } |
782 | objectToSignal->ReleaseReference(thread); |
783 | objectToSignal = nullptr; |
784 | |
785 | // Wait |
786 | result = |
787 | InternalWaitForMultipleObjectsEx( |
788 | thread, |
789 | 1 /* nCount */, |
790 | &hObjectToWaitOn, |
791 | false /* bWaitAll */, |
792 | dwMilliseconds, |
793 | bAlertable); |
794 | if (result == WAIT_FAILED) |
795 | { |
796 | ERROR("Unable to wait on object for handle %p (error %u)!\n" , hObjectToWaitOn, palError); |
797 | goto InternalSignalObjectAndWait_Error; |
798 | } |
799 | objectToWaitOn->ReleaseReference(thread); |
800 | objectToWaitOn = nullptr; |
801 | |
802 | goto InternalSignalObjectAndWait_Exit; |
803 | |
804 | InternalSignalObjectAndWait_Error: |
805 | if (objectToSignal != nullptr) |
806 | { |
807 | objectToSignal->ReleaseReference(thread); |
808 | } |
809 | if (objectToWaitOn != nullptr) |
810 | { |
811 | objectToWaitOn->ReleaseReference(thread); |
812 | } |
813 | |
814 | if (palError != NO_ERROR) |
815 | { |
816 | _ASSERTE(result == WAIT_FAILED); |
817 | thread->SetLastError(palError); |
818 | } |
819 | |
820 | InternalSignalObjectAndWait_Exit: |
821 | LOGEXIT("InternalSignalObjectAndWait returns %u\n" , result); |
822 | return result; |
823 | } |
824 | |
825 | DWORD CorUnix::InternalSleepEx ( |
826 | CPalThread * pThread, |
827 | DWORD dwMilliseconds, |
828 | BOOL bAlertable) |
829 | { |
830 | PAL_ERROR palErr = NO_ERROR; |
831 | DWORD dwRet = WAIT_FAILED; |
832 | int iSignaledObjIndex; |
833 | |
834 | TRACE("Sleeping %u ms [bAlertable=%d]" , dwMilliseconds, (int)bAlertable); |
835 | |
836 | if (bAlertable) |
837 | { |
838 | // In this case do not use AreAPCsPending. In fact, since we are |
839 | // not holding the synch lock(s) an APC posting may race with |
840 | // AreAPCsPending. |
841 | palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); |
842 | if (NO_ERROR == palErr) |
843 | { |
844 | return WAIT_IO_COMPLETION; |
845 | } |
846 | } |
847 | |
848 | if (dwMilliseconds > 0) |
849 | { |
850 | ThreadWakeupReason twrWakeupReason; |
851 | palErr = g_pSynchronizationManager->BlockThread(pThread, |
852 | dwMilliseconds, |
853 | (TRUE == bAlertable), |
854 | true, |
855 | &twrWakeupReason, |
856 | (DWORD *)&iSignaledObjIndex); |
857 | if (NO_ERROR != palErr) |
858 | { |
859 | ERROR("IPalSynchronizationManager::BlockThread failed for thread " |
860 | "pThread=%p [error=%u]\n" , pThread, palErr); |
861 | return dwRet; |
862 | } |
863 | |
864 | switch (twrWakeupReason) |
865 | { |
866 | case WaitSucceeded: |
867 | case WaitTimeout: |
868 | dwRet = 0; |
869 | break; |
870 | case Alerted: |
871 | _ASSERT_MSG(bAlertable, "Awakened for APC from a non-alertable wait\n" ); |
872 | |
873 | dwRet = WAIT_IO_COMPLETION; |
874 | palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); |
875 | _ASSERT_MSG(NO_ERROR == palErr, "Awakened for APC, but no APC is pending\n" ); |
876 | |
877 | break; |
878 | case MutexAbondoned: |
879 | ASSERT("Thread %p awakened with reason=MutexAbondoned from a SleepEx\n" , pThread); |
880 | break; |
881 | case WaitFailed: |
882 | default: |
883 | ERROR("Thread %p awakened with some failure\n" , pThread); |
884 | break; |
885 | } |
886 | } |
887 | else |
888 | { |
889 | sched_yield(); |
890 | dwRet = 0; |
891 | } |
892 | |
893 | TRACE("Done sleeping %u ms [bAlertable=%d]" , dwMilliseconds, (int)bAlertable); |
894 | return dwRet; |
895 | } |
896 | |
897 | |