in CoreCLRProfiler/native/coreclr_headers/src/pal/src/synchmgr/wait.cpp [353:715]
DWORD CorUnix::InternalWaitForMultipleObjectsEx(
CPalThread * pThread,
DWORD nCount,
CONST HANDLE *lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds,
BOOL bAlertable,
BOOL bPrioritize)
{
DWORD dwRet = WAIT_FAILED;
PAL_ERROR palErr = NO_ERROR;
int i, iSignaledObjCount, iSignaledObjIndex = -1;
bool fWAll = (bool)bWaitAll, fNeedToBlock = false;
bool fAbandoned = false;
WaitType wtWaitType;
IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
ISynchWaitController * pISyncStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
IPalObject ** ppIPalObjs = pIPalObjStackArray;
ISynchWaitController ** ppISyncWaitCtrlrs = pISyncStackArray;
if ((nCount == 0) || (nCount > MAXIMUM_WAIT_OBJECTS))
{
ppIPalObjs = NULL; // make delete at the end safe
ppISyncWaitCtrlrs = NULL; // make delete at the end safe
ERROR("Invalid object count=%d [range: 1 to %d]\n",
nCount, MAXIMUM_WAIT_OBJECTS)
pThread->SetLastError(ERROR_INVALID_PARAMETER);
goto WFMOExIntExit;
}
else if (nCount == 1)
{
fWAll = false; // makes no difference when nCount is 1
wtWaitType = SingleObject;
}
else
{
wtWaitType = fWAll ? MultipleObjectsWaitAll : MultipleObjectsWaitOne;
if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
{
ppIPalObjs = InternalNewArray<IPalObject*>(nCount);
ppISyncWaitCtrlrs = InternalNewArray<ISynchWaitController*>(nCount);
if ((NULL == ppIPalObjs) || (NULL == ppISyncWaitCtrlrs))
{
ERROR("Out of memory allocating internal structures\n");
pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto WFMOExIntExit;
}
}
}
palErr = g_pObjectManager->ReferenceMultipleObjectsByHandleArray(pThread,
(VOID **)lpHandles,
nCount,
&sg_aotWaitObject,
ppIPalObjs);
if (NO_ERROR != palErr)
{
ERROR("Unable to obtain object for some or all of the handles [error=%u]\n",
palErr);
if (palErr == ERROR_INVALID_HANDLE)
pThread->SetLastError(ERROR_INVALID_HANDLE);
else
pThread->SetLastError(ERROR_INTERNAL_ERROR);
goto WFMOExIntExit;
}
if (nCount > 1)
{
// Check for any cross-process sync objects. "Wait for any" and "wait for all" operations are not supported on
// cross-process sync objects in the PAL.
for (DWORD i = 0; i < nCount; ++i)
{
if (ppIPalObjs[i]->GetObjectType()->GetId() == otiNamedMutex)
{
ERROR("Attempt to wait for any or all handles including a cross-process sync object", ERROR_NOT_SUPPORTED);
pThread->SetLastError(ERROR_NOT_SUPPORTED);
goto WFMOExIntCleanup;
}
}
}
else if (ppIPalObjs[0]->GetObjectType()->GetId() == otiNamedMutex)
{
SharedMemoryProcessDataHeader *processDataHeader =
SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(ppIPalObjs[0]);
_ASSERTE(processDataHeader != nullptr);
try
{
MutexTryAcquireLockResult tryAcquireLockResult =
static_cast<NamedMutexProcessData *>(processDataHeader->GetData())->TryAcquireLock(dwMilliseconds);
switch (tryAcquireLockResult)
{
case MutexTryAcquireLockResult::AcquiredLock:
dwRet = WAIT_OBJECT_0;
break;
case MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned:
dwRet = WAIT_ABANDONED_0;
break;
case MutexTryAcquireLockResult::TimedOut:
dwRet = WAIT_TIMEOUT;
break;
default:
_ASSERTE(false);
break;
}
}
catch (SharedMemoryException ex)
{
pThread->SetLastError(ex.GetErrorCode());
}
goto WFMOExIntCleanup;
}
if (fWAll)
{
// For a wait-all operation, check for duplicate wait objects in the array. This just uses a brute-force O(n^2)
// algorithm, but since MAXIMUM_WAIT_OBJECTS is small, the worst case is not so bad, and the average case would involve
// significantly fewer items.
for (DWORD i = 0; i < nCount - 1; ++i)
{
IPalObject *const objectToCheck = ppIPalObjs[i];
for (DWORD j = i + 1; j < nCount; ++j)
{
if (ppIPalObjs[j] == objectToCheck)
{
ERROR("Duplicate handle provided for a wait-all operation [error=%u]\n", ERROR_INVALID_PARAMETER);
pThread->SetLastError(ERROR_INVALID_PARAMETER);
goto WFMOExIntCleanup;
}
}
}
}
palErr = g_pSynchronizationManager->GetSynchWaitControllersForObjects(
pThread, ppIPalObjs, nCount, ppISyncWaitCtrlrs);
if (NO_ERROR != palErr)
{
ERROR("Unable to obtain ISynchWaitController interface for some or all "
"of the objects [error=%u]\n", palErr);
pThread->SetLastError(ERROR_INTERNAL_ERROR);
goto WFMOExIntCleanup;
}
if (bAlertable)
{
// First check for pending APC. We need to do that while holding the global
// synch lock implicitely grabbed by GetSynchWaitControllersForObjects
if (g_pSynchronizationManager->AreAPCsPending(pThread))
{
// If there is any pending APC we need to release the
// implicit global synch lock before calling into it
for (i = 0; (i < (int)nCount) && (NULL != ppISyncWaitCtrlrs[i]); i++)
{
ppISyncWaitCtrlrs[i]->ReleaseController();
ppISyncWaitCtrlrs[i] = NULL;
}
palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
if (NO_ERROR == palErr)
{
dwRet = WAIT_IO_COMPLETION;
}
else
{
ASSERT("Awakened for APC, but no APC is pending\n");
pThread->SetLastError(ERROR_INTERNAL_ERROR);
dwRet = WAIT_FAILED;
}
goto WFMOExIntCleanup;
}
}
iSignaledObjCount = 0;
iSignaledObjIndex = -1;
for (i=0;i<(int)nCount;i++)
{
bool fValue, fWaitObjectAbandoned = false;
palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fWaitObjectAbandoned);
if (NO_ERROR != palErr)
{
ERROR("ISynchWaitController::CanThreadWaitWithoutBlocking() failed for "
"%d-th object [handle=%p error=%u]\n", i, lpHandles[i], palErr);
pThread->SetLastError(ERROR_INTERNAL_ERROR);
goto WFMOExIntReleaseControllers;
}
if (fWaitObjectAbandoned)
{
fAbandoned = true;
}
if (fValue)
{
iSignaledObjCount++;
iSignaledObjIndex = i;
if (!fWAll)
break;
}
}
fNeedToBlock = (iSignaledObjCount == 0) || (fWAll && (iSignaledObjCount < (int)nCount));
if (!fNeedToBlock)
{
// At least one object signaled, or bWaitAll==TRUE and all object signaled.
// No need to wait, let's unsignal the object(s) and return without blocking
int iStartIdx, iEndIdx;
if (fWAll)
{
iStartIdx = 0;
iEndIdx = nCount;
}
else
{
iStartIdx = iSignaledObjIndex;
iEndIdx = iStartIdx + 1;
}
// Unsignal objects
if( iStartIdx < 0 )
{
ERROR("Buffer underflow due to iStartIdx < 0");
pThread->SetLastError(ERROR_INTERNAL_ERROR);
dwRet = WAIT_FAILED;
goto WFMOExIntCleanup;
}
for (i = iStartIdx; i < iEndIdx; i++)
{
palErr = ppISyncWaitCtrlrs[i]->ReleaseWaitingThreadWithoutBlocking();
if (NO_ERROR != palErr)
{
ERROR("ReleaseWaitingThreadWithoutBlocking() failed for %d-th "
"object [handle=%p error=%u]\n",
i, lpHandles[i], palErr);
pThread->SetLastError(palErr);
goto WFMOExIntReleaseControllers;
}
}
dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0);
}
else if (0 == dwMilliseconds)
{
// Not enough objects signaled, but timeout is zero: no actual wait
dwRet = WAIT_TIMEOUT;
fNeedToBlock = false;
}
else
{
// Register the thread for waiting on all objects
for (i=0;i<(int)nCount;i++)
{
palErr = ppISyncWaitCtrlrs[i]->RegisterWaitingThread(
wtWaitType,
i,
(TRUE == bAlertable),
bPrioritize != FALSE);
if (NO_ERROR != palErr)
{
ERROR("RegisterWaitingThread() failed for %d-th object "
"[handle=%p error=%u]\n", i, lpHandles[i], palErr);
pThread->SetLastError(palErr);
goto WFMOExIntReleaseControllers;
}
}
}
WFMOExIntReleaseControllers:
// Release all controllers before going to sleep
for (i = 0; i < (int)nCount; i++)
{
ppISyncWaitCtrlrs[i]->ReleaseController();
ppISyncWaitCtrlrs[i] = NULL;
}
if (NO_ERROR != palErr)
goto WFMOExIntCleanup;
if (fNeedToBlock)
{
ThreadWakeupReason twrWakeupReason;
//
// Going to sleep
//
palErr = g_pSynchronizationManager->BlockThread(pThread,
dwMilliseconds,
(TRUE == bAlertable),
false,
&twrWakeupReason,
(DWORD *)&iSignaledObjIndex);
//
// Awakened
//
if (NO_ERROR != palErr)
{
ERROR("IPalSynchronizationManager::BlockThread failed for thread "
"pThread=%p [error=%u]\n", pThread, palErr);
pThread->SetLastError(palErr);
goto WFMOExIntCleanup;
}
switch (twrWakeupReason)
{
case WaitSucceeded:
dwRet = WAIT_OBJECT_0; // offset added later
break;
case MutexAbondoned:
dwRet = WAIT_ABANDONED_0; // offset added later
break;
case WaitTimeout:
dwRet = WAIT_TIMEOUT;
break;
case Alerted:
_ASSERT_MSG(bAlertable,
"Awakened for APC from a non-alertable wait\n");
dwRet = WAIT_IO_COMPLETION;
palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
_ASSERT_MSG(NO_ERROR == palErr,
"Awakened for APC, but no APC is pending\n");
break;
case WaitFailed:
default:
ERROR("Thread %p awakened with some failure\n", pThread);
dwRet = WAIT_FAILED;
break;
}
}
if (!fWAll && ((WAIT_OBJECT_0 == dwRet) || (WAIT_ABANDONED_0 == dwRet)))
{
_ASSERT_MSG(0 <= iSignaledObjIndex,
"Failed to identify signaled/abandoned object\n");
_ASSERT_MSG(iSignaledObjIndex >= 0 && nCount > static_cast<DWORD>(iSignaledObjIndex),
"SignaledObjIndex object out of range "
"[index=%d obj_count=%u\n",
iSignaledObjCount, nCount);
if (iSignaledObjIndex < 0)
{
pThread->SetLastError(ERROR_INTERNAL_ERROR);
dwRet = WAIT_FAILED;
goto WFMOExIntCleanup;
}
dwRet += iSignaledObjIndex;
}
WFMOExIntCleanup:
for (i = 0; i < (int)nCount; i++)
{
ppIPalObjs[i]->ReleaseReference(pThread);
ppIPalObjs[i] = NULL;
}
WFMOExIntExit:
if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
{
InternalDeleteArray(ppIPalObjs);
InternalDeleteArray(ppISyncWaitCtrlrs);
}
return dwRet;
}