/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Loop.NT.cpp Date: 2021-10-1 Author: Reece ***/ #include #include "Loop.NT.hpp" #include "ILoopSourceEx.hpp" #include "LSWin32.NT.hpp" #define MAXIMUM_WAIT_OBJECTS_ (MAXIMUM_WAIT_OBJECTS - 1) namespace Aurora::IO::Loop { extern AuRWRenterableLock gWaitForMultipleALLLock; void ResetLoopSourceFalseAlarm(const AuSPtr &pLoopSource); AuList> WaitMultipleOrObjects(const AuList> &objects, bool bAlert, bool bBreakAPCs, bool bZeroTick, AuUInt32 dwTimeoutReq, bool bAllowOthers, bool &bTooMany) { bool bIsWinLoop; AuSPtr loopSourceExs[MAXIMUM_WAIT_OBJECTS_]; HANDLE handleArray[MAXIMUM_WAIT_OBJECTS_]; AuUInt32 uCounterHandle, uCounterFD; AuSPtr pMsgSource; AuList> triggered; bIsWinLoop = false; uCounterHandle = 0; uCounterFD = 0; try { triggered.reserve(objects.size()); } catch (...) { return {}; } for (const auto &source : objects) { if (!source) { continue; } if (source->GetType() == ELoopSource::eSourceWin32) { bIsWinLoop = true; pMsgSource = source; continue; } if (auto extended = AuDynamicCast(source)) { { auto uNewIndex = uCounterHandle++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } loopSourceExs[uNewIndex] = extended; } if (extended->Singular()) { auto uNewIndex = uCounterFD++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } handleArray[uNewIndex] = reinterpret_cast(extended->GetHandle()); } else { for (const auto &handle : extended->GetHandles()) { auto uNewIndex = uCounterFD++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } handleArray[uNewIndex] = reinterpret_cast(extended->GetHandle()); } } } } for (AU_ITERATE_N(i, uCounterHandle)) { loopSourceExs[i]->OnPresleep(); } AuUInt32 uTimeout {}; if (!bZeroTick) { uTimeout = dwTimeoutReq ? dwTimeoutReq : INFINITE; } DWORD ret; if (bIsWinLoop && pMsgWaitForMultipleObjectsEx) { ret = pMsgWaitForMultipleObjectsEx(uCounterFD, handleArray, uTimeout, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_INPUTAVAILABLE | (bAlert ? MWMO_ALERTABLE : 0)); } else { ret = ::WaitForMultipleObjectsEx(uCounterFD, handleArray, false, uTimeout, bAlert); } bool error = ((ret == WAIT_TIMEOUT) || (ret == WAIT_IO_COMPLETION) || (ret == WAIT_FAILED)); bool isPump = WAIT_OBJECT_0 + uCounterFD == ret; AuUInt firstTriggered {}; if (!error) { if (!isPump) { firstTriggered = reinterpret_cast(handleArray[ret - WAIT_OBJECT_0]); } } if (isPump) { // msg loop queue for trigger AuTryInsert(triggered, pMsgSource); if (!bAllowOthers) { error = true; } } for (AU_ITERATE_N(i, uCounterHandle)) { auto &pSource = loopSourceExs[i]; if (!error) { AuUInt lastHandle {}; bool wasTriggered {}; if (pSource->Singular()) { auto handle = pSource->GetHandle(); if ((firstTriggered == handle) || (bAllowOthers && WaitForSingleObject(reinterpret_cast(handle), 0) == WAIT_OBJECT_0)) { lastHandle = handle; wasTriggered = true; } } else { for (const auto &handle : pSource->GetHandles()) { if ((firstTriggered == handle) || (bAllowOthers && WaitForSingleObject(reinterpret_cast(handle), 0) == WAIT_OBJECT_0)) { lastHandle = handle; wasTriggered = true; break; } } } // notify the loopsource of ex, only signal once we've filtered the kernel signal if (wasTriggered && pSource->OnTrigger(lastHandle)) { // pog AuTryInsert(triggered, pSource); } } pSource->OnFinishSleep(); } return AuMove(triggered); } bool WaitMultipleAndObjects_(const AuList> &objects, bool bAlert, bool bBreakAPCs, bool bZeroTick, AuUInt64 qwTimeoutAbs, bool &bTooMany, bool &bTimeout) { bool bIsWinLoop; bool bRet; AuSPtr loopSourceExs[MAXIMUM_WAIT_OBJECTS_]; HANDLE handleArray[MAXIMUM_WAIT_OBJECTS_]; AuUInt32 uCounterHandle, uCounterFD; AuSPtr pMsgSource; bIsWinLoop = false; uCounterHandle = 0; uCounterFD = 0; bRet = true; for (const auto &source : objects) { if (!source) { continue; } if (source->GetType() == ELoopSource::eSourceWin32) { bIsWinLoop = true; pMsgSource = source; continue; } if (auto extended = AuDynamicCast(source)) { { auto uNewIndex = uCounterHandle++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } loopSourceExs[uNewIndex] = extended; } if (extended->Singular()) { auto uNewIndex = uCounterFD++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } handleArray[uNewIndex] = reinterpret_cast(extended->GetHandle()); } else { for (const auto &handle : extended->GetHandles()) { auto uNewIndex = uCounterFD++; if (uNewIndex == MAXIMUM_WAIT_OBJECTS_) { bTooMany = true; return {}; } handleArray[uNewIndex] = reinterpret_cast(extended->GetHandle()); } } } } for (AU_ITERATE_N(i, uCounterHandle)) { loopSourceExs[i]->OnPresleep(); } DWORD ret; do { AuUInt32 uTimeout; if (bZeroTick) { uTimeout = 0; } else if (qwTimeoutAbs) { auto uNow = AuTime::SteadyClockNS(); if (uNow >= qwTimeoutAbs) { uTimeout = 0; } else { uTimeout = AuNSToMS(qwTimeoutAbs - uNow); } } else { uTimeout = -1; } if (bIsWinLoop && pMsgWaitForMultipleObjectsEx) { ret = pMsgWaitForMultipleObjectsEx(uCounterFD, handleArray, uTimeout, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_INPUTAVAILABLE | MWMO_WAITALL | (bAlert ? MWMO_ALERTABLE : 0)); } else { ret = ::WaitForMultipleObjectsEx(uCounterFD, handleArray, TRUE, uTimeout, bAlert); } } while (!bBreakAPCs && ret == WAIT_IO_COMPLETION); bool error = ((ret == WAIT_TIMEOUT) || (ret == WAIT_IO_COMPLETION) || (ret == WAIT_FAILED)); if (WAIT_OBJECT_0 + uCounterFD == ret) { bTimeout = false; bRet = false; } else if (error) { bTimeout = ret == WAIT_TIMEOUT; bRet = false; } { AU_LOCK_GLOBAL_GUARD(gWaitForMultipleALLLock->AsWritable()); for (AU_ITERATE_N(i, uCounterHandle)) { auto &pSource = loopSourceExs[i]; if (bRet) { AuUInt lastHandle {}; bool wasTriggered {}; if (pSource->Singular()) { auto handle = pSource->GetHandle(); lastHandle = handle; } else { for (const auto &handle : pSource->GetHandles()) { lastHandle = handle; break; } } if (!pSource->OnTrigger(lastHandle)) { for (AU_ITERATE_N(z, i)) { ResetLoopSourceFalseAlarm(loopSourceExs[z]); } bRet = false; break; } } pSource->OnFinishSleep(); } } return bRet; } bool WaitMultipleAndObjects(const AuList> &objects, bool bAlert, bool bBreakAPCs, bool bZeroTick, AuUInt64 qwTimeoutAbs, bool &bTooMany, bool &bTimeout) { bool bRet {}; do { bRet = WaitMultipleAndObjects_(objects, bAlert, bBreakAPCs, bZeroTick, qwTimeoutAbs, bTooMany, bTimeout); } while (!bRet && !bTimeout && !bTooMany); return bRet; } }