/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Loop.Unix.cpp Date: 2021-10-1 Author: Reece ***/ #include #include "Loop.Unix.hpp" #include "ILoopSourceEx.hpp" #include namespace Aurora::IO::Loop { AuList> WaitMultipleOrObjects(const AuList> &objects, bool bZeroTick, AuUInt32 dwTimeoutReq, bool bAllowOthers, bool &bTooMany) { #if defined(AURORA_HAS_NO_POSIX_POLL) // do the WinNT falllback into loop queue. // // loop queues should have long term persistency in a user-space ABI (kqueue) or in the kernel (epoll) // they may take longer to setup, may seem wasteful to create and throw away, but it's probably the best we can do under BSD kernels. // why? kevents exist for posix aio objects. they dont use fds. // // BSDs with posix AIO based async file transactions, >2.2 linux kernels, macos with corefoundation interop with kqueues, and other posix kernels with alternative aio will probably need // need to use the win32 64 <= handles path. bTooMany = true; return false; #else AuList> loopSourceExs; AuList> loopSourceExs2; AuList> triggered; AuList handleArray; AuList> handleIndicies; try { loopSourceExs.reserve(objects.size()); loopSourceExs2.reserve(objects.size()); handleArray.reserve(objects.size()); triggered.reserve(triggered.size()); handleIndicies.reserve(triggered.size()); for (const auto &source : objects) { if (!source) { continue; } if (auto extended = AuDynamicCast(source)) { loopSourceExs2.push_back(extended); if (extended->Singular()) { auto handle = extended->GetHandle(); auto handleWrite = extended->GetWriteHandle(); auto i = loopSourceExs.size(); loopSourceExs.push_back(extended); if (handle != -1) { pollfd poll; poll.fd = handle; poll.events = POLLIN; poll.revents = 0; handleArray.push_back(poll); handleIndicies.push_back(AuMakePair(i, false)); } if (handleWrite != -1) { pollfd poll; poll.fd = handleWrite; poll.events = POLLOUT; poll.revents = 0; handleArray.push_back(poll); handleIndicies.push_back(AuMakePair(i, true)); } } else { auto handles = extended->GetHandles(); auto handlesWrite = extended->GetWriteHandles(); auto i = loopSourceExs.size(); loopSourceExs.push_back(extended); for (const auto &handle : handles) { pollfd poll; if (handle == -1) { continue; } poll.fd = handle; poll.events = POLLIN; poll.revents = 0; handleArray.push_back(poll); handleIndicies.push_back(AuMakePair(i, false)); } for (const auto &handle : handlesWrite) { pollfd poll; if (handle == -1) { continue; } poll.fd = handle; poll.events = POLLOUT; poll.revents = 0; handleArray.push_back(poll); handleIndicies.push_back(AuMakePair(i, true)); } } } } } catch (...) { return {}; } for (const auto &source : loopSourceExs2) { source->OnPresleep(); } AuUInt32 uTimeout {}; if (bZeroTick) { uTimeout = 0; } else if (!dwTimeoutReq) { uTimeout = -1; } else { uTimeout = dwTimeoutReq; } int ret; do { ret = poll(handleArray.data(), handleArray.size(), uTimeout); uTimeout = 0; if (ret > 0) { for (AU_ITERATE_N(i, handleArray.size())) { if (!handleArray[i].revents) { continue; } auto uIndex = AuGet<0>(handleIndicies[i]); auto bRead = AuGet<1>(handleIndicies[i]); auto pLoopSource = AuExchange(loopSourceExs[uIndex], nullptr); if (!pLoopSource) { continue; } if (!pLoopSource->OnTrigger(handleArray[i].fd)) { loopSourceExs[uIndex] = pLoopSource; continue; } triggered.push_back(pLoopSource); if (!bAllowOthers) { break; } } } } while (ret == -1 && errno == EINTR); for (const auto &source : loopSourceExs2) { source->OnFinishSleep(); } return triggered; #endif } }