summaryrefslogtreecommitdiff
path: root/src/pal/src/synchmgr/wait.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src/synchmgr/wait.cpp')
-rw-r--r--src/pal/src/synchmgr/wait.cpp711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/pal/src/synchmgr/wait.cpp b/src/pal/src/synchmgr/wait.cpp
new file mode 100644
index 0000000000..9c4fe3a9ca
--- /dev/null
+++ b/src/pal/src/synchmgr/wait.cpp
@@ -0,0 +1,711 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ wait.cpp
+
+Abstract:
+
+ Implementation of waiting functions as described in
+ the WIN32 API
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/synchobjects.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC);
+
+#define MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE (MAXIMUM_WAIT_OBJECTS / 4)
+
+using namespace CorUnix;
+
+static PalObjectTypeId sg_rgWaitObjectsIds[] =
+ {
+ otiAutoResetEvent,
+ otiManualResetEvent,
+ otiMutex,
+ otiNamedMutex,
+ otiSemaphore,
+ otiProcess,
+ otiThread
+ };
+static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds,
+ sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0]));
+
+/*++
+Function:
+ WaitForSingleObject
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+WaitForSingleObject(IN HANDLE hHandle,
+ IN DWORD dwMilliseconds)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForSingleObject);
+ ENTRY("WaitForSingleObject(hHandle=%p, dwMilliseconds=%u)\n",
+ hHandle, dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE,
+ dwMilliseconds, FALSE);
+
+ LOGEXIT("WaitForSingleObject returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForSingleObject);
+ return dwRet;
+}
+
+
+/*++
+Function:
+ WaitForSingleObjectEx
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+WaitForSingleObjectEx(IN HANDLE hHandle,
+ IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForSingleObjectEx);
+ ENTRY("WaitForSingleObjectEx(hHandle=%p, dwMilliseconds=%u, bAlertable=%s)\n",
+ hHandle, dwMilliseconds, bAlertable ? "TRUE" : "FALSE");
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE,
+ dwMilliseconds, bAlertable);
+
+ LOGEXIT("WaitForSingleObjectEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForSingleObjectEx);
+ return dwRet;
+}
+
+
+/*++
+Function:
+ WaitForMultipleObjects
+
+See MSDN doc.
+
+--*/
+DWORD
+PALAPI
+WaitForMultipleObjects(IN DWORD nCount,
+ IN CONST HANDLE *lpHandles,
+ IN BOOL bWaitAll,
+ IN DWORD dwMilliseconds)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForMultipleObjects);
+ ENTRY("WaitForMultipleObjects(nCount=%d, lpHandles=%p,"
+ " bWaitAll=%d, dwMilliseconds=%u)\n",
+ nCount, lpHandles, bWaitAll, dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles,
+ bWaitAll, dwMilliseconds, FALSE);
+
+ LOGEXIT("WaitForMultipleObjects returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForMultipleObjects);
+ return dwRet;
+}
+
+/*++
+Function:
+ WaitForMultipleObjectsEx
+
+See MSDN doc for info about this function.
+--*/
+DWORD
+PALAPI
+WaitForMultipleObjectsEx(IN DWORD nCount,
+ IN CONST HANDLE *lpHandles,
+ IN BOOL bWaitAll,
+ IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForMultipleObjectsEx);
+ ENTRY("WaitForMultipleObjectsEx(nCount=%d, lpHandles=%p,"
+ " bWaitAll=%d, dwMilliseconds=%u, bAlertable=d)\n",
+ nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, bWaitAll,
+ dwMilliseconds, bAlertable);
+
+ LOGEXIT("WaitForMultipleObjectsEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForMultipleObjectsEx);
+ return dwRet;
+}
+
+/*++
+Function:
+ Sleep
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+Sleep(IN DWORD dwMilliseconds)
+{
+ PERF_ENTRY(Sleep);
+ ENTRY("Sleep(dwMilliseconds=%u)\n", dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ DWORD internalSleepRet = InternalSleepEx(pThread, dwMilliseconds, FALSE);
+
+ if (internalSleepRet != 0)
+ {
+ ERROR("Sleep(dwMilliseconds=%u) failed [error=%u]\n", dwMilliseconds, internalSleepRet);
+ pThread->SetLastError(internalSleepRet);
+ }
+
+ LOGEXIT("Sleep returns VOID\n");
+ PERF_EXIT(Sleep);
+}
+
+
+/*++
+Function:
+ SleepEx
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+SleepEx(IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(SleepEx);
+ ENTRY("SleepEx(dwMilliseconds=%u, bAlertable=%d)\n", dwMilliseconds, bAlertable);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalSleepEx(pThread, dwMilliseconds, bAlertable);
+
+ LOGEXIT("SleepEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(SleepEx);
+
+ return dwRet;
+}
+
+/*++
+Function:
+ QueueUserAPC
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+QueueUserAPC(
+ PAPCFUNC pfnAPC,
+ HANDLE hThread,
+ ULONG_PTR dwData)
+{
+ CPalThread * pCurrentThread = NULL;
+ CPalThread * pTargetThread = NULL;
+ IPalObject * pTargetThreadObject = NULL;
+ PAL_ERROR palErr;
+ DWORD dwRet;
+
+ PERF_ENTRY(QueueUserAPC);
+ ENTRY("QueueUserAPC(pfnAPC=%p, hThread=%p, dwData=%#x)\n",
+ pfnAPC, hThread, dwData);
+
+ /* NOTE: Windows does not check the validity of pfnAPC, even if it is
+ NULL. It just does an access violation later on when the APC call
+ is attempted */
+
+ pCurrentThread = InternalGetCurrentThread();
+
+ palErr = InternalGetThreadDataFromHandle(
+ pCurrentThread,
+ hThread,
+ 0, // THREAD_SET_CONTEXT
+ &pTargetThread,
+ &pTargetThreadObject
+ );
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to obtain thread data for handle %p (error %x)!\n",
+ hThread, palErr);
+ goto QueueUserAPC_exit;
+ }
+
+
+ palErr = g_pSynchronizationManager->QueueUserAPC(pCurrentThread, pTargetThread,
+ pfnAPC, dwData);
+
+QueueUserAPC_exit:
+ if (pTargetThreadObject)
+ {
+ pTargetThreadObject->ReleaseReference(pCurrentThread);
+ }
+
+ dwRet = (NO_ERROR == palErr) ? 1 : 0;
+
+ LOGEXIT("QueueUserAPC returns DWORD %d\n", dwRet);
+ PERF_EXIT(QueueUserAPC);
+ return dwRet;
+}
+
+DWORD CorUnix::InternalWaitForMultipleObjectsEx(
+ CPalThread * pThread,
+ DWORD nCount,
+ CONST HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ 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,
+ SYNCHRONIZE,
+ 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;
+ palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fAbandoned);
+ 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 (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));
+ 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;
+}
+
+DWORD CorUnix::InternalSleepEx (
+ CPalThread * pThread,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ PAL_ERROR palErr = NO_ERROR;
+ DWORD dwRet = WAIT_FAILED;
+ int iSignaledObjIndex;
+
+ TRACE("Sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable);
+
+ if (bAlertable)
+ {
+ // In this case do not use AreAPCsPending. In fact, since we are
+ // not holding the synch lock(s) an APC posting may race with
+ // AreAPCsPending.
+ palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+ if (NO_ERROR == palErr)
+ {
+ return WAIT_IO_COMPLETION;
+ }
+ }
+
+ if (dwMilliseconds > 0)
+ {
+ ThreadWakeupReason twrWakeupReason;
+ palErr = g_pSynchronizationManager->BlockThread(pThread,
+ dwMilliseconds,
+ (TRUE == bAlertable),
+ true,
+ &twrWakeupReason,
+ (DWORD *)&iSignaledObjIndex);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("IPalSynchronizationManager::BlockThread failed for thread "
+ "pThread=%p [error=%u]\n", pThread, palErr);
+ return dwRet;
+ }
+
+ switch (twrWakeupReason)
+ {
+ case WaitSucceeded:
+ case WaitTimeout:
+ dwRet = 0;
+ 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 MutexAbondoned:
+ ASSERT("Thread %p awakened with reason=MutexAbondoned from a SleepEx\n", pThread);
+ break;
+ case WaitFailed:
+ default:
+ ERROR("Thread %p awakened with some failure\n", pThread);
+ break;
+ }
+ }
+ else
+ {
+ dwRet = 0;
+ }
+
+ TRACE("Done sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable);
+ return dwRet;
+}
+