AuroraRuntime/Source/IO/Loop/Loop.NT.cpp

185 lines
5.5 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"
namespace Aurora::IO::Loop
{
AuList<AuSPtr<ILoopSource>> WaitMultipleOrObjects(const AuList<AuSPtr<ILoopSource>> &objects, 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(triggered.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 | MWMO_ALERTABLE);
}
else
{
ret = ::WaitForMultipleObjectsEx(uCounterFD, handleArray, false, uTimeout, true);
}
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);
}
}