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
9Module Name:
10
11 wait.cpp
12
13Abstract:
14
15 Implementation of waiting functions as described in
16 the WIN32 API
17
18Revision 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
33SET_DEFAULT_DEBUG_CHANNEL(SYNC);
34
35#define MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE (MAXIMUM_WAIT_OBJECTS / 4)
36
37using namespace CorUnix;
38
39static PalObjectTypeId sg_rgWaitObjectsIds[] =
40 {
41 otiAutoResetEvent,
42 otiManualResetEvent,
43 otiMutex,
44 otiNamedMutex,
45 otiSemaphore,
46 otiProcess,
47 otiThread
48 };
49static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds,
50 sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0]));
51
52static PalObjectTypeId sg_rgSignalableObjectIds[] =
53{
54 otiAutoResetEvent,
55 otiManualResetEvent,
56 otiMutex,
57 otiNamedMutex,
58 otiSemaphore
59};
60static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, _countof(sg_rgSignalableObjectIds));
61
62/*++
63Function:
64 WaitForSingleObject
65
66See MSDN doc.
67--*/
68DWORD
69PALAPI
70WaitForSingleObject(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/*++
91Function:
92 WaitForSingleObjectPrioritized
93
94Similar to WaitForSingleObject, except uses a LIFO release policy for waiting threads by prioritizing new waiters (registering
95them at the beginning of the wait queue rather than at the end).
96--*/
97DWORD
98PALAPI
99PAL_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/*++
120Function:
121 WaitForSingleObjectEx
122
123See MSDN doc.
124--*/
125DWORD
126PALAPI
127WaitForSingleObjectEx(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/*++
149Function:
150 WaitForMultipleObjects
151
152See MSDN doc.
153
154--*/
155DWORD
156PALAPI
157WaitForMultipleObjects(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/*++
180Function:
181 WaitForMultipleObjectsEx
182
183See MSDN doc for info about this function.
184--*/
185DWORD
186PALAPI
187WaitForMultipleObjectsEx(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/*++
211Function:
212 SignalObjectAndWait
213
214See MSDN doc for info about this function.
215--*/
216DWORD
217PALAPI
218SignalObjectAndWait(
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/*++
241Function:
242 Sleep
243
244See MSDN doc.
245--*/
246VOID
247PALAPI
248Sleep(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/*++
269Function:
270 SleepEx
271
272See MSDN doc.
273--*/
274DWORD
275PALAPI
276SleepEx(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/*++
295Function:
296 QueueUserAPC
297
298See MSDN doc.
299--*/
300DWORD
301PALAPI
302QueueUserAPC(
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
342QueueUserAPC_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
355DWORD 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 *processDataHeader =
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
619WFMOExIntReleaseControllers:
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
699WFMOExIntCleanup:
700 for (i = 0; i < (int)nCount; i++)
701 {
702 ppIPalObjs[i]->ReleaseReference(pThread);
703 ppIPalObjs[i] = NULL;
704 }
705
706WFMOExIntExit:
707 if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
708 {
709 InternalDeleteArray(ppIPalObjs);
710 InternalDeleteArray(ppISyncWaitCtrlrs);
711 }
712
713 return dwRet;
714}
715
716DWORD 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
804InternalSignalObjectAndWait_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
820InternalSignalObjectAndWait_Exit:
821 LOGEXIT("InternalSignalObjectAndWait returns %u\n", result);
822 return result;
823}
824
825DWORD 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