J Reece Wilson
02826d2365
[+] AuLoop::kWaitMultipleFlagBreakAfterAPC [+] Alternative Wait AND implementations for NT, POSIX, and generic [+] IOConfig::... [*] LoopQueue improvements [+] ILoopQueue::ConfigureDoIOApcCallbacks [+] ILoopQueue::ConfigureDoIOApcCallbacks
371 lines
11 KiB
C++
371 lines
11 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: Loop.NT.cpp
|
|
Date: 2021-10-1
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#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<Loop::ILoopSource> &pLoopSource);
|
|
|
|
AuList<AuSPtr<ILoopSource>> WaitMultipleOrObjects(const AuList<AuSPtr<ILoopSource>> &objects, bool bAlert, bool bBreakAPCs, bool bZeroTick, AuUInt32 dwTimeoutReq, bool bAllowOthers, bool &bTooMany)
|
|
{
|
|
bool bIsWinLoop;
|
|
AuSPtr<ILoopSourceEx> loopSourceExs[MAXIMUM_WAIT_OBJECTS_];
|
|
HANDLE handleArray[MAXIMUM_WAIT_OBJECTS_];
|
|
AuUInt32 uCounterHandle, uCounterFD;
|
|
AuSPtr<ILoopSource> pMsgSource;
|
|
AuList<AuSPtr<ILoopSource>> 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<ILoopSourceEx>(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<HANDLE>(extended->GetHandle());
|
|
}
|
|
else
|
|
{
|
|
for (const auto &handle : extended->GetHandles())
|
|
{
|
|
auto uNewIndex = uCounterFD++;
|
|
if (uNewIndex == MAXIMUM_WAIT_OBJECTS_)
|
|
{
|
|
bTooMany = true;
|
|
return {};
|
|
}
|
|
handleArray[uNewIndex] = reinterpret_cast<HANDLE>(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<AuUInt>(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>(handle), 0) == WAIT_OBJECT_0))
|
|
{
|
|
lastHandle = handle;
|
|
wasTriggered = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (const auto &handle : pSource->GetHandles())
|
|
{
|
|
if ((firstTriggered == handle) ||
|
|
(bAllowOthers && WaitForSingleObject(reinterpret_cast<HANDLE>(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<AuSPtr<ILoopSource>> &objects, bool bAlert, bool bBreakAPCs, bool bZeroTick, AuUInt64 qwTimeoutAbs, bool &bTooMany, bool &bTimeout)
|
|
{
|
|
bool bIsWinLoop;
|
|
bool bRet;
|
|
AuSPtr<ILoopSourceEx> loopSourceExs[MAXIMUM_WAIT_OBJECTS_];
|
|
HANDLE handleArray[MAXIMUM_WAIT_OBJECTS_];
|
|
AuUInt32 uCounterHandle, uCounterFD;
|
|
AuSPtr<ILoopSource> 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<ILoopSourceEx>(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<HANDLE>(extended->GetHandle());
|
|
}
|
|
else
|
|
{
|
|
for (const auto &handle : extended->GetHandles())
|
|
{
|
|
auto uNewIndex = uCounterFD++;
|
|
if (uNewIndex == MAXIMUM_WAIT_OBJECTS_)
|
|
{
|
|
bTooMany = true;
|
|
return {};
|
|
}
|
|
handleArray[uNewIndex] = reinterpret_cast<HANDLE>(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<AuUInt32>(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<AuSPtr<ILoopSource>> &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;
|
|
}
|
|
} |