diff --git a/Include/Aurora/IO/Loop/ILoopSource.hpp b/Include/Aurora/IO/Loop/ILoopSource.hpp index 8f2e804f..786b5f93 100644 --- a/Include/Aurora/IO/Loop/ILoopSource.hpp +++ b/Include/Aurora/IO/Loop/ILoopSource.hpp @@ -9,24 +9,60 @@ namespace Aurora::IO::Loop { + /// Is the IO primitive is not IPC, entirely inprocess, this might avoid the kernel or a spinloop + static const AuUInt8 kFlagLSTryNoSpin = 1; + + /// Disallows IO Async transaction callbacks and APC callbacks from being called + static const AuUInt8 kFlagLSTryNoIOAlerts = 2; + struct ILoopSource { /** - * @brief Atomic is-signaled-and-latch + * @brief Atomic is-signaled-and-latch. + * Approx equiv to NT: WaitForSingleObjectEx(this, 0, TRUE); but will not break (WAIT_IO_COMPLETION) on APC or IO interruption. * @return - */ + */ virtual bool IsSignaled() = 0; + + /** + * @brief Atomic is-signaled-and-latch. + * Approx equiv to NT: WaitForSingleObjectEx(this, 0, !(uFlags & kFlagLSTryNoIOAlerts)); but will not break (WAIT_IO_COMPLETION) on APC or IO interruption + * @return + */ + virtual bool IsSignaledExt(AuUInt8 uFlags) = 0; /** * @brief Returns a generic description about the loop source handle * @return - */ + */ virtual ELoopSource GetType() = 0; /** - * @breif Blocks the current thread for the kernel primitives - * @warning Are you looking for LoopQueues? You can even reduce async threads down to kernel ILoopQueue's - */ - virtual bool WaitOn(AuUInt32 timeout = 0) = 0; + * @breif Blocks the current thread for the kernel primitive. + * @warning Are you looking for LoopQueues? You can even reduce async threads down to kernel ILoopQueues. + * @warning see: (stateful wait context) AuIO::Loop::ILoopQueue + * @warning see: (arbitrary stateless wait) AuIO::Loop::WaitMultipleLoopSources + * @warning see: (arbitrary stateless wait) AuIO::Loop::WaitMultipleLoopSourcesEx + * @warning see: (scheduling on registered thread) Async/IThreadPool::ToKernelWorkQueue + * @warning see: (scheduling on registered thread) Async/IWorkItem::SetSchedByLoopSourceOnce + * @warning see: (scheduling on registered thread) Async/IWorkItem::SetSchedByLoopSourceRepeating + */ + virtual bool WaitOn(AuUInt32 uTimeoutRelMS = 0) = 0; + + virtual bool WaitOnExt(AuUInt8 uFlags, AuUInt32 uTimeoutRelMS = 0) = 0; + + /** + * Follows steady time & 0 = indefinite convention + * @warning: The IO subsystem uses miliseconds internally. Expect under and overshoots. + * nanosec resolution is useful for dealing with hidden interruptions requiring a resleep. + */ + virtual bool WaitOnAbs(AuUInt64 uTimeoutAbs = 0) = 0; + + /** + * Follows steady time & 0 = indefinite convention + * @warning: The IO subsystem uses miliseconds internally. Expect under and overshoots. + * nanosec resolution is useful for dealing with hidden interruptions requiring a resleep. + */ + virtual bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs = 0) = 0; }; } \ No newline at end of file diff --git a/Include/Aurora/IO/Loop/Loop.hpp b/Include/Aurora/IO/Loop/Loop.hpp index 473ed75d..244e0a1e 100644 --- a/Include/Aurora/IO/Loop/Loop.hpp +++ b/Include/Aurora/IO/Loop/Loop.hpp @@ -19,6 +19,13 @@ namespace Aurora::IO::Loop { + + // TODO: Win32 enters an alertable state, does not break + // Linux does not enter an alertable state, but will soon-ish + // There is no flag for enter alertable state + // Cannot reach true parity with WaitForMultipleObjectsEx in this current state + // Also, there is no flag for awoken by APC + // optTimeoutMS = {} | indefinite // optTimeoutMS = 0 | poll // optTimeoutMS = 1 | 1ms diff --git a/Source/AuProcAddresses.Linux.cpp b/Source/AuProcAddresses.Linux.cpp index d79bfc62..f9933353 100644 --- a/Source/AuProcAddresses.Linux.cpp +++ b/Source/AuProcAddresses.Linux.cpp @@ -211,7 +211,8 @@ namespace Aurora int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event *events, - struct timespec *timeout) + struct timespec *timeout, + bool bStrictUserspaceOnly) { int i {}; @@ -256,6 +257,11 @@ namespace Aurora return i; } + if (bStrictUserspaceOnly) + { + return i; + } + do_syscall: int iKernelCount {}; if ((iKernelCount = syscallFuckYou(__NR_io_getevents, diff --git a/Source/AuProcAddresses.Linux.hpp b/Source/AuProcAddresses.Linux.hpp index ec1c7e10..5f45cd9d 100644 --- a/Source/AuProcAddresses.Linux.hpp +++ b/Source/AuProcAddresses.Linux.hpp @@ -56,7 +56,8 @@ namespace Aurora int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event *events, - struct timespec *timeout); + struct timespec *timeout, + bool bStrictUserspaceOnly); ssize_t sys_getrandom(void *pBuffer, size_t uLength); diff --git a/Source/IO/IPC/AuIPCMutexFutex.Linux.cpp b/Source/IO/IPC/AuIPCMutexFutex.Linux.cpp index e27c8665..1b656fdb 100644 --- a/Source/IO/IPC/AuIPCMutexFutex.Linux.cpp +++ b/Source/IO/IPC/AuIPCMutexFutex.Linux.cpp @@ -305,7 +305,7 @@ static void LinuxRemoveRobustFutexSlow(AuUInt32 *futex) } } -static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout) +static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout, AuUInt64 uTimeoutAbs) { bool bContended; struct timespec tspec; @@ -315,6 +315,10 @@ static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout) { AuTime::ms2tsabs(&tspec, timeout); } + else if (uTimeoutAbs) + { + AuTime::ns2ts(&tspec, uTimeoutAbs); + } do { @@ -322,7 +326,7 @@ static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout) bContended = old != kFutexValueUnlocked; if (bContended) { - int res = Aurora::futex_wait_shared(futex, old, timeout ? &tspec : nullptr); + int res = Aurora::futex_wait_shared(futex, old, (timeout || uTimeoutAbs) ? &tspec : nullptr); if (res < 0) { if (res == -ETIMEDOUT) @@ -541,7 +545,7 @@ namespace Aurora::IO::IPC return false; } - if (!::LinuxLockFutex(futex, timeout)) + if (!::LinuxLockFutex(futex, timeout, 0)) { return false; } @@ -581,6 +585,45 @@ namespace Aurora::IO::IPC return true; } + bool IPCMutexProxy::IsSignaledExt(AuUInt8 uFlags) + { + return this->IsSignaled(); + } + + bool IPCMutexProxy::WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) + { + return this->WaitOn(timeout); + } + + bool IPCMutexProxy::WaitOnAbs(AuUInt64 uTimeoutAbs) + { + auto futex = this->GetFutex(); + if (!futex) + { + return false; + } + + if (!::LinuxLockFutex(futex, 0, uTimeoutAbs)) + { + return false; + } + + if (!this->mutex_.IsSignaled()) + { + ::LinuxUnlockFutex(futex); + return false; + } + + this->leakSelf_ = AuSharedFromThis(); + + return true; + } + + bool IPCMutexProxy::WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) + { + return this->WaitOnAbs(uTimeoutAbs); + } + Loop::ELoopSource IPCMutexProxy::GetType() { return this->mutex_.GetType(); diff --git a/Source/IO/IPC/AuIPCMutexFutex.Linux.hpp b/Source/IO/IPC/AuIPCMutexFutex.Linux.hpp index ea45eb3b..a2e2d414 100644 --- a/Source/IO/IPC/AuIPCMutexFutex.Linux.hpp +++ b/Source/IO/IPC/AuIPCMutexFutex.Linux.hpp @@ -28,6 +28,11 @@ namespace Aurora::IO::IPC bool WaitOn(AuUInt32 timeout) override; Loop::ELoopSource GetType() override; + bool IsSignaledExt(AuUInt8 uFlags) override; + bool WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) override; + bool WaitOnAbs(AuUInt64 uTimeoutAbs) override; + bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) override; + AuString ExportToString() override; AuUInt32 *GetFutex(); diff --git a/Source/IO/IPC/AuIPCPrimitives.Linux.hpp b/Source/IO/IPC/AuIPCPrimitives.Linux.hpp index 9973c8bb..42a45c97 100644 --- a/Source/IO/IPC/AuIPCPrimitives.Linux.hpp +++ b/Source/IO/IPC/AuIPCPrimitives.Linux.hpp @@ -36,6 +36,7 @@ namespace Aurora::IO::IPC ~IPCEventProxy(); PROXY_INTERNAL_INTERFACE(event_) + PROXY_LOOPSOURCE_EXT_APIS(event_) IMPLEMENT_HANDLE bool Set() override; @@ -57,6 +58,7 @@ namespace Aurora::IO::IPC ~IPCSemaphoreProxy(); PROXY_INTERNAL_INTERFACE(semaphore_) + PROXY_LOOPSOURCE_EXT_APIS(semaphore_) IMPLEMENT_HANDLE bool AddOne() override; diff --git a/Source/IO/IPC/AuIPCPrimitives.NT.cpp b/Source/IO/IPC/AuIPCPrimitives.NT.cpp index f283dac1..1a79a193 100644 --- a/Source/IO/IPC/AuIPCPrimitives.NT.cpp +++ b/Source/IO/IPC/AuIPCPrimitives.NT.cpp @@ -33,6 +33,7 @@ namespace Aurora::IO::IPC ~IPCMutexProxy(); PROXY_INTERNAL_INTERFACE(mutex_) + PROXY_LOOPSOURCE_EXT_APIS(mutex_) IMPLEMENT_HANDLE bool Unlock() override; @@ -151,6 +152,7 @@ namespace Aurora::IO::IPC ~IPCEventProxy(); PROXY_INTERNAL_INTERFACE(event_) + PROXY_LOOPSOURCE_EXT_APIS(event_) IMPLEMENT_HANDLE bool Set() override; @@ -276,6 +278,7 @@ namespace Aurora::IO::IPC ~IPCSemaphoreProxy(); PROXY_INTERNAL_INTERFACE(semaphore_) + PROXY_LOOPSOURCE_EXT_APIS(semaphore_) IMPLEMENT_HANDLE bool AddOne() override; diff --git a/Source/IO/IPC/IPC.hpp b/Source/IO/IPC/IPC.hpp index 6c708415..15e29b75 100644 --- a/Source/IO/IPC/IPC.hpp +++ b/Source/IO/IPC/IPC.hpp @@ -51,7 +51,7 @@ namespace Aurora::IO::IPC #else #define PROXY_INTERNAL_INTERFACE_(Base)\ - inline virtual void OnPresleep() override \ + inline virtual void OnPresleep() override \ { \ Base OnPresleep(); \ }; \ @@ -59,15 +59,15 @@ namespace Aurora::IO::IPC { \ return Base OnTrigger(handle); \ } \ - inline virtual void OnFinishSleep() override \ + inline virtual void OnFinishSleep() override \ { \ Base OnFinishSleep(); \ } \ - inline virtual bool Singular() override \ + inline virtual bool Singular() override \ { \ return Base Singular(); \ } \ - inline virtual AuUInt GetHandle() override \ + inline virtual AuUInt GetHandle() override \ { \ return Base GetHandle(); \ } \ @@ -75,13 +75,36 @@ namespace Aurora::IO::IPC { \ return Base GetHandles(); \ } \ - inline bool HasValidHandle() \ + inline bool HasValidHandle() \ { \ return Base HasValidHandle(); \ } #endif +// TODO: 2024/09 (this doesnt belong here but im lazy and no primitive actually needs these overloads) +// (fill the entire interface and drop the _IM_SO_LAZY part) + +#define PROXY_LOOPSOURCE_EXT_APIS_(Base) \ + inline virtual bool IsSignaledExt(AuUInt8 uFlags) override \ + { \ + return Base IsSignaledExt(uFlags); \ + } \ + inline virtual bool WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) override \ + { \ + return Base WaitOnExt(uFlags, timeout); \ + } \ + inline virtual bool WaitOnAbs(AuUInt64 uTimeoutAbs) override \ + { \ + return Base WaitOnAbs(uTimeoutAbs); \ + } \ + inline virtual bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) override \ + { \ + return Base WaitOnAbsExt(uFlags, uTimeoutAbs); \ + } + #define PROXY_INTERNAL_INTERFACE(Base) PROXY_INTERNAL_INTERFACE_(Base.) + #define PROXY_LOOPSOURCE_EXT_APIS(Base) PROXY_LOOPSOURCE_EXT_APIS_(Base.) + } \ No newline at end of file diff --git a/Source/IO/Loop/LSEvent.Linux.cpp b/Source/IO/Loop/LSEvent.Linux.cpp index b660542c..bd8933fa 100644 --- a/Source/IO/Loop/LSEvent.Linux.cpp +++ b/Source/IO/Loop/LSEvent.Linux.cpp @@ -84,7 +84,12 @@ namespace Aurora::IO::Loop bool LSEvent::IsSignaled() { - return IsSignaledFromNonblockingImpl(this, this, &LSEvent::IsSignaledNonblocking); + return IsSignaledFromNonblockingImpl(this, this, &LSEvent::IsSignaledNonblocking, true); + } + + bool LSEvent::IsSignaledExt(AuUInt8 uFlags) + { + return IsSignaledFromNonblockingImpl(this, this, &LSEvent::IsSignaledNonblocking, !(uFlags & kFlagLSTryNoIOAlerts)); } bool LSEvent::WaitOn(AuUInt32 timeout) diff --git a/Source/IO/Loop/LSEvent.Linux.hpp b/Source/IO/Loop/LSEvent.Linux.hpp index 43602a9b..67d87c95 100644 --- a/Source/IO/Loop/LSEvent.Linux.hpp +++ b/Source/IO/Loop/LSEvent.Linux.hpp @@ -1,36 +1,39 @@ -/*** - Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. - - File: LSEvent.Linux.hpp - Date: 2022-4-4 - Author: Reece -***/ -#pragma once -#include "LSHandle.hpp" - -namespace Aurora::IO::Loop -{ - struct LSEvent : ILSEvent, virtual LSHandle - { - LSEvent(); - LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers); - LSEvent(int handle, bool triggered, bool atomicRelease); - ~LSEvent(); - - bool Set() override; - bool Reset() override; - - bool TryInit(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers); - - virtual bool OnTrigger(AuUInt handle) override; - - virtual bool IsSignaled() override; - virtual bool WaitOn(AuUInt32 timeout) override; - virtual ELoopSource GetType() override; - - private: - void Init(bool init); - bool IsSignaledNonblocking(); - bool atomicRelease_; - }; +/*** + Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LSEvent.Linux.hpp + Date: 2022-4-4 + Author: Reece +***/ +#pragma once +#include "LSHandle.hpp" + +namespace Aurora::IO::Loop +{ + struct LSEvent : ILSEvent, virtual LSHandle + { + LSEvent(); + LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers); + LSEvent(int handle, bool triggered, bool atomicRelease); + ~LSEvent(); + + PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_; + + bool Set() override; + bool Reset() override; + + bool TryInit(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers); + + virtual bool OnTrigger(AuUInt handle) override; + + virtual bool IsSignaled() override; + virtual bool IsSignaledExt(AuUInt8 uFlags) override; + virtual bool WaitOn(AuUInt32 timeout) override; + virtual ELoopSource GetType() override; + + private: + void Init(bool init); + bool IsSignaledNonblocking(); + bool atomicRelease_; + }; } \ No newline at end of file diff --git a/Source/IO/Loop/LSEvent.NT.hpp b/Source/IO/Loop/LSEvent.NT.hpp index 0b6868b6..513e8696 100644 --- a/Source/IO/Loop/LSEvent.NT.hpp +++ b/Source/IO/Loop/LSEvent.NT.hpp @@ -17,6 +17,8 @@ namespace Aurora::IO::Loop LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers); ~LSEvent(); + PROXY_LOOP_LOOPSOURCE_EXT_APIS_; + bool TryInit(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers); bool Set() override; diff --git a/Source/IO/Loop/LSFromFdNonblocking.hpp b/Source/IO/Loop/LSFromFdNonblocking.hpp index 5b17382d..3cdff0db 100644 --- a/Source/IO/Loop/LSFromFdNonblocking.hpp +++ b/Source/IO/Loop/LSFromFdNonblocking.hpp @@ -7,15 +7,37 @@ ***/ #pragma once +#if defined(AURORA_IS_LINUX_DERIVED) + +namespace Aurora::IO::UNIX +{ + bool LinuxOverlappedYield(bool bUserspaceOnly); +} + +#endif + namespace Aurora::IO::Loop { template - inline bool IsSignaledFromNonblockingImpl(ILoopSourceEx *source, T * that, bool(T::*IsSignaledNonblocking)()) + inline bool IsSignaledFromNonblockingImpl(ILoopSourceEx *source, T * that, bool(T::*IsSignaledNonblocking)(), bool bIOTick) { bool val {}; source->OnPresleep(); val = ((that)->*(IsSignaledNonblocking))(); source->OnFinishSleep(); + // Required for precise Windows on Linux OVERLAPPED semantics for emulators. + // IAsyncTransaction::Complete()/Wait() will always enter an alertable state and dispatch io completion callbacks. + // In theory, we don't need this. + // In practice, if NT can atomically set an event and update the apc queue, we could be out of parity. + if (bIOTick) + { + #if defined(AURORA_IS_LINUX_DERIVED) + // Do not syscall after read or select again under Linux + UNIX::LinuxOverlappedYield(true); + #else + IOYield(); + #endif + } return val; } } \ No newline at end of file diff --git a/Source/IO/Loop/LSHandle.hpp b/Source/IO/Loop/LSHandle.hpp index 577d7637..73b93a1f 100644 --- a/Source/IO/Loop/LSHandle.hpp +++ b/Source/IO/Loop/LSHandle.hpp @@ -9,6 +9,73 @@ #include "WaitSingle.hpp" +#define PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_ \ + inline virtual bool WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) override \ + { \ + return LSHandle::WaitOnExt(uFlags, timeout); \ + } \ + inline virtual bool WaitOnAbs(AuUInt64 uTimeoutAbs) override \ + { \ + return LSHandle::WaitOnAbs(uTimeoutAbs); \ + } \ + inline virtual bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) override \ + { \ + return LSHandle::WaitOnAbsExt(uFlags, uTimeoutAbs); \ + } + +#define PROXY_LOOP_LOOPSOURCE_EXT_APIS_ \ + inline virtual bool IsSignaledExt(AuUInt8 uFlags) override \ + { \ + return LSHandle::IsSignaledExt(uFlags); \ + } \ + PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_ + +// TODO: more work required +#define PROXY_LOCAL_LOOPSOURCE_EXT_APIS_ \ + inline virtual bool IsSignaledExt(AuUInt8 uFlags) override \ + { \ + bool bFlagUser = uFlags & kFlagLSTryNoSpin; \ + if (bFlagUser) \ + { \ + return this->TryTakeNoSpin(); \ + } \ + else \ + { \ + return this->TryTakeSpin(); \ + } \ + } \ + inline virtual bool WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) override \ + { \ + return this->WaitOn(timeout); \ + } \ + inline virtual bool WaitOnAbs(AuUInt64 uTimeoutAbs) override \ + { \ + return this->WaitOnAbs(uTimeoutAbs); \ + } \ + inline virtual bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) override \ + { \ + if (!uTimeoutAbs) \ + { \ + return this->TryTakeWaitNS(0); \ + } \ + else \ + { \ + return this->TryTakeWaitNS(uTimeoutAbs); \ + } \ + } \ + inline virtual bool WaitOn(AuUInt32 timeout) override \ + { \ + if (!timeout) \ + { \ + return this->TryTakeWaitNS(0); \ + } \ + else \ + { \ + return this->TryTakeWaitNS(AuMSToNS(timeout) + AuTime::SteadyClockNS()); \ + } \ + } + + namespace Aurora::IO::Loop { #if defined(AURORA_IS_MODERNNT_DERIVED) diff --git a/Source/IO/Loop/LSLocalEvent.cpp b/Source/IO/Loop/LSLocalEvent.cpp index 8ae2c9b6..547de225 100644 --- a/Source/IO/Loop/LSLocalEvent.cpp +++ b/Source/IO/Loop/LSLocalEvent.cpp @@ -84,11 +84,6 @@ namespace Aurora::IO::Loop return this->TryTake(); } - bool LSLocalEvent::WaitOn(AuUInt32 timeout) - { - return this->TryTakeWaitMS(timeout); - } - ELoopSource LSLocalEvent::GetType() { return ELoopSource::eSourceFastEvent; @@ -234,12 +229,8 @@ namespace Aurora::IO::Loop return this->TryTakeSpin(); } - bool LSLocalEvent::TryTakeWaitMS(AuUInt32 timeout) + bool LSLocalEvent::TryTakeWaitNS(AuUInt64 uEndTime) { - auto uEndTime = timeout ? - AuTime::SteadyClockNS() + AuMSToNS(timeout) : - 0; - if (this->TryTakeSpin()) { return true; @@ -247,7 +238,7 @@ namespace Aurora::IO::Loop while (!this->TryTakeNoSpin()) { - if (!timeout) + if (!uEndTime) { if (LSSemaphore::WaitOn(0)) { diff --git a/Source/IO/Loop/LSLocalEvent.hpp b/Source/IO/Loop/LSLocalEvent.hpp index 8deed47c..645fd05c 100644 --- a/Source/IO/Loop/LSLocalEvent.hpp +++ b/Source/IO/Loop/LSLocalEvent.hpp @@ -19,7 +19,6 @@ namespace Aurora::IO::Loop bool TryInit(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers); virtual bool IsSignaled() override; - bool WaitOn(AuUInt32 timeout) override; ELoopSource GetType() override; bool Set() override; @@ -30,8 +29,10 @@ namespace Aurora::IO::Loop bool TryTakeNoSpin(); bool TryTakeSpin(); bool TryTake(); - bool TryTakeWaitMS(AuUInt32 timeout); - + bool TryTakeWaitNS(AuUInt64 timeout); + + PROXY_LOCAL_LOOPSOURCE_EXT_APIS_; + bool IsSignaledNoSpinIfUserland() override; void OnPresleep() override; void OnFinishSleep() override; diff --git a/Source/IO/Loop/LSLocalMutex.cpp b/Source/IO/Loop/LSLocalMutex.cpp index f8c30694..ccbef7d7 100644 --- a/Source/IO/Loop/LSLocalMutex.cpp +++ b/Source/IO/Loop/LSLocalMutex.cpp @@ -91,11 +91,6 @@ namespace Aurora::IO::Loop return this->TryTakeNoSpin(); } - bool LSLocalMutex::WaitOn(AuUInt32 timeout) - { - return this->TryTakeWaitMS(timeout); - } - ELoopSource LSLocalMutex::GetType() { return ELoopSource::eSourceFastMutex; @@ -119,12 +114,8 @@ namespace Aurora::IO::Loop return this->TryTakeSpin(); } - bool LSLocalMutex::TryTakeWaitMS(AuUInt32 timeout) + bool LSLocalMutex::TryTakeWaitNS(AuUInt64 uEndTime) { - auto uEndTime = timeout ? - AuTime::SteadyClockNS() + AuMSToNS(timeout) : - 0; - if (this->TryTakeSpin()) { return true; @@ -132,7 +123,7 @@ namespace Aurora::IO::Loop while (!this->TryTakeNoSpin()) { - if (!timeout) + if (!uEndTime) { if (LSSemaphore::WaitOn(0)) { diff --git a/Source/IO/Loop/LSLocalMutex.hpp b/Source/IO/Loop/LSLocalMutex.hpp index 4c95babc..2ed57b38 100644 --- a/Source/IO/Loop/LSLocalMutex.hpp +++ b/Source/IO/Loop/LSLocalMutex.hpp @@ -20,7 +20,6 @@ namespace Aurora::IO::Loop bool IsSignaled() override; bool IsSignaledNoSpinIfUserland() override; - bool WaitOn(AuUInt32 timeout) override; ELoopSource GetType() override; bool Unlock() override; @@ -30,7 +29,9 @@ namespace Aurora::IO::Loop bool TryTakeNoSpin(); bool TryTakeSpin(); bool TryTake(); - bool TryTakeWaitMS(AuUInt32 timeout); + bool TryTakeWaitNS(AuUInt64 timeout); + + PROXY_LOCAL_LOOPSOURCE_EXT_APIS_; AuAUInt32 uAtomicWord {}; }; diff --git a/Source/IO/Loop/LSLocalSemaphore.cpp b/Source/IO/Loop/LSLocalSemaphore.cpp index 4c811edc..72981d7a 100644 --- a/Source/IO/Loop/LSLocalSemaphore.cpp +++ b/Source/IO/Loop/LSLocalSemaphore.cpp @@ -251,11 +251,6 @@ namespace Aurora::IO::Loop return this->TryTakeNoSpin(); } - bool LSLocalSemaphore::WaitOn(AuUInt32 timeout) - { - return this->TryTakeWaitMS(timeout); - } - ELoopSource LSLocalSemaphore::GetType() { return ELoopSource::eSourceFastSemaphore; @@ -289,12 +284,8 @@ namespace Aurora::IO::Loop return this->TryTakeSpin(); } - bool LSLocalSemaphore::TryTakeWaitMS(AuUInt32 timeout) + bool LSLocalSemaphore::TryTakeWaitNS(AuUInt64 uEndTime) { - auto uEndTime = timeout ? - AuTime::SteadyClockNS() + AuMSToNS(timeout) : - 0; - if (this->TryTakeSpin()) { return true; @@ -302,7 +293,7 @@ namespace Aurora::IO::Loop while (!this->TryTakeNoSpin()) { - if (!timeout) + if (!uEndTime) { if (LSSemaphore::WaitOn(0)) { diff --git a/Source/IO/Loop/LSLocalSemaphore.hpp b/Source/IO/Loop/LSLocalSemaphore.hpp index 84fbdab8..693f5725 100644 --- a/Source/IO/Loop/LSLocalSemaphore.hpp +++ b/Source/IO/Loop/LSLocalSemaphore.hpp @@ -19,7 +19,6 @@ namespace Aurora::IO::Loop bool TryInit(AuUInt32 initialCount, AuUInt32 uMaxCount = 0); bool IsSignaled() override; - bool WaitOn(AuUInt32 timeout) override; bool IsSignaledNoSpinIfUserland() override; ELoopSource GetType() override; @@ -31,7 +30,9 @@ namespace Aurora::IO::Loop bool TryTakeNoSpin(); bool TryTakeSpin(); bool TryTake(); - bool TryTakeWaitMS(AuUInt32 timeout); + bool TryTakeWaitNS(AuUInt64 timeout); + + PROXY_LOCAL_LOOPSOURCE_EXT_APIS_; void OnPresleep() override; void OnFinishSleep() override; diff --git a/Source/IO/Loop/LSMutex.Linux.cpp b/Source/IO/Loop/LSMutex.Linux.cpp index 25c77b17..5f607d8d 100644 --- a/Source/IO/Loop/LSMutex.Linux.cpp +++ b/Source/IO/Loop/LSMutex.Linux.cpp @@ -61,7 +61,12 @@ namespace Aurora::IO::Loop bool LSMutex::IsSignaled() { - return IsSignaledFromNonblockingImpl(this, this, &LSMutex::IsSignaledNonblocking); + return IsSignaledFromNonblockingImpl(this, this, &LSMutex::IsSignaledNonblocking, true); + } + + bool LSMutex::IsSignaledExt(AuUInt8 uFlags) + { + return IsSignaledFromNonblockingImpl(this, this, &LSMutex::IsSignaledNonblocking, !(uFlags & kFlagLSTryNoIOAlerts)); } bool LSMutex::WaitOn(AuUInt32 timeout) diff --git a/Source/IO/Loop/LSMutex.Linux.hpp b/Source/IO/Loop/LSMutex.Linux.hpp index ad1a9a79..99713a9b 100644 --- a/Source/IO/Loop/LSMutex.Linux.hpp +++ b/Source/IO/Loop/LSMutex.Linux.hpp @@ -16,11 +16,14 @@ namespace Aurora::IO::Loop LSMutex(int handle); ~LSMutex(); + PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_; + bool Unlock() override; virtual bool OnTrigger(AuUInt handle) override; bool IsSignaled() override; + bool IsSignaledExt(AuUInt8 uFlags) override; bool WaitOn(AuUInt32 timeout) override; virtual ELoopSource GetType() override; diff --git a/Source/IO/Loop/LSMutex.NT.hpp b/Source/IO/Loop/LSMutex.NT.hpp index 458597e2..86af5802 100644 --- a/Source/IO/Loop/LSMutex.NT.hpp +++ b/Source/IO/Loop/LSMutex.NT.hpp @@ -15,6 +15,8 @@ namespace Aurora::IO::Loop LSMutex(HANDLE handle); ~LSMutex(); + PROXY_LOOP_LOOPSOURCE_EXT_APIS_; + bool Unlock() override; bool IsSignaled() override; diff --git a/Source/IO/Loop/LSSemaphore.Linux.cpp b/Source/IO/Loop/LSSemaphore.Linux.cpp index a4f54b20..19bba309 100644 --- a/Source/IO/Loop/LSSemaphore.Linux.cpp +++ b/Source/IO/Loop/LSSemaphore.Linux.cpp @@ -85,7 +85,12 @@ namespace Aurora::IO::Loop bool LSSemaphore::IsSignaled() { - return IsSignaledFromNonblockingImpl(this, this, &LSSemaphore::IsSignaledNonblocking); + return IsSignaledFromNonblockingImpl(this, this, &LSSemaphore::IsSignaledNonblocking, true); + } + + bool LSSemaphore::IsSignaledExt(AuUInt8 uFlags) + { + return IsSignaledFromNonblockingImpl(this, this, &LSSemaphore::IsSignaledNonblocking, !(uFlags & kFlagLSTryNoIOAlerts)); } bool LSSemaphore::WaitOn(AuUInt32 timeout) diff --git a/Source/IO/Loop/LSSemaphore.Linux.hpp b/Source/IO/Loop/LSSemaphore.Linux.hpp index 864a6c6e..c45db24b 100644 --- a/Source/IO/Loop/LSSemaphore.Linux.hpp +++ b/Source/IO/Loop/LSSemaphore.Linux.hpp @@ -17,15 +17,17 @@ namespace Aurora::IO::Loop LSSemaphore(int handle, int tag); ~LSSemaphore(); + PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_; + bool TryInit(AuUInt32 initialCount = 0); bool AddOne() override; bool AddMany(AuUInt32 uCount) override; - virtual bool OnTrigger(AuUInt handle) override; bool IsSignaled() override; + bool IsSignaledExt(AuUInt8 uFlags) override; bool WaitOn(AuUInt32 timeout) override; virtual ELoopSource GetType() override; diff --git a/Source/IO/Loop/LSSemaphore.NT.hpp b/Source/IO/Loop/LSSemaphore.NT.hpp index 4cd15277..6a54502e 100644 --- a/Source/IO/Loop/LSSemaphore.NT.hpp +++ b/Source/IO/Loop/LSSemaphore.NT.hpp @@ -15,6 +15,8 @@ namespace Aurora::IO::Loop LSSemaphore(); LSSemaphore(HANDLE handle); ~LSSemaphore(); + + PROXY_LOOP_LOOPSOURCE_EXT_APIS_; bool TryInit(AuUInt32 initialCount = 0); diff --git a/Source/IO/Loop/LSSignalCatcher.Linux.cpp b/Source/IO/Loop/LSSignalCatcher.Linux.cpp index 3b14161b..a3880958 100644 --- a/Source/IO/Loop/LSSignalCatcher.Linux.cpp +++ b/Source/IO/Loop/LSSignalCatcher.Linux.cpp @@ -56,7 +56,7 @@ namespace Aurora::IO::Loop bool LSSignalCatcher::IsSignaled() { - return IsSignaledFromNonblockingImpl(this, this, &LSSignalCatcher::IsSignaledNonblocking); + return IsSignaledFromNonblockingImpl(this, this, &LSSignalCatcher::IsSignaledNonblocking, true); } bool LSSignalCatcher::WaitOn(AuUInt32 timeout) diff --git a/Source/IO/Loop/LSTimer.Linux.cpp b/Source/IO/Loop/LSTimer.Linux.cpp index 78ad913c..c1a3eb10 100644 --- a/Source/IO/Loop/LSTimer.Linux.cpp +++ b/Source/IO/Loop/LSTimer.Linux.cpp @@ -82,7 +82,12 @@ namespace Aurora::IO::Loop bool LSTimer::IsSignaled() { - return IsSignaledFromNonblockingImpl(this, this, &LSTimer::IsSignaledNonblocking); + return IsSignaledFromNonblockingImpl(this, this, &LSTimer::IsSignaledNonblocking, true); + } + + bool LSTimer::IsSignaledExt(AuUInt8 uFlags) + { + return IsSignaledFromNonblockingImpl(this, this, &LSTimer::IsSignaledNonblocking, !(uFlags & kFlagLSTryNoIOAlerts)); } bool LSTimer::WaitOn(AuUInt32 timeout) diff --git a/Source/IO/Loop/LSTimer.Linux.hpp b/Source/IO/Loop/LSTimer.Linux.hpp index 8f8a1b33..70fe5625 100644 --- a/Source/IO/Loop/LSTimer.Linux.hpp +++ b/Source/IO/Loop/LSTimer.Linux.hpp @@ -15,6 +15,8 @@ namespace Aurora::IO::Loop LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, int handle); ~LSTimer(); + PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_; + virtual void UpdateTime(AuUInt64 absTimeMs) override; virtual void UpdateTimeNs(AuUInt64 absTimeNs) override; virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override; @@ -24,6 +26,7 @@ namespace Aurora::IO::Loop virtual bool OnTrigger(AuUInt handle) override; virtual bool IsSignaled() override; + virtual bool IsSignaledExt(AuUInt8 uFlags) override; virtual bool WaitOn(AuUInt32 timeout) override; virtual ELoopSource GetType() override; diff --git a/Source/IO/Loop/LSTimer.NT.hpp b/Source/IO/Loop/LSTimer.NT.hpp index 531ec72a..dada1669 100644 --- a/Source/IO/Loop/LSTimer.NT.hpp +++ b/Source/IO/Loop/LSTimer.NT.hpp @@ -15,6 +15,8 @@ namespace Aurora::IO::Loop LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, HANDLE handle); ~LSTimer(); + PROXY_LOOP_LOOPSOURCE_EXT_APIS_; + virtual void UpdateTime(AuUInt64 absTimeMs) override; virtual void UpdateTimeNs(AuUInt64 absTimeNs) override; virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override; diff --git a/Source/IO/Loop/Loop.Unix.cpp b/Source/IO/Loop/Loop.Unix.cpp index ef6a6973..0dbb4e51 100644 --- a/Source/IO/Loop/Loop.Unix.cpp +++ b/Source/IO/Loop/Loop.Unix.cpp @@ -10,6 +10,16 @@ #include "ILoopSourceEx.hpp" #include +// TODO: +#if defined(AURORA_IS_LINUX_DERIVED) + +namespace Aurora::IO::UNIX +{ + bool LinuxOverlappedYield(bool bUserspaceOnly); +} + +#endif + namespace Aurora::IO::Loop { AuList> WaitMultipleOrObjects(const AuList> &objects, bool bZeroTick, AuUInt32 dwTimeoutReq, bool bAllowOthers, bool &bTooMany) @@ -194,6 +204,16 @@ namespace Aurora::IO::Loop source->OnFinishSleep(); } + // TODO: ugly workaround (see: LSFromHdNonblocking rationale) for an ugly TODO issue implicating all targets (see public Loop.hpp) + { + #if defined(AURORA_IS_LINUX_DERIVED) + // Do not syscall after read or select again under Linux + UNIX::LinuxOverlappedYield(true); + #else + IOYield(); + #endif + } + return triggered; #endif } diff --git a/Source/IO/Loop/WaitSingle.NT.cpp b/Source/IO/Loop/WaitSingle.NT.cpp index 759864d9..37ee77f4 100644 --- a/Source/IO/Loop/WaitSingle.NT.cpp +++ b/Source/IO/Loop/WaitSingle.NT.cpp @@ -10,7 +10,7 @@ namespace Aurora::IO::Loop { - bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, AuUInt &one) + bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, AuUInt &one, bool bNoAlert) { if (handles.empty()) { @@ -43,7 +43,7 @@ namespace Aurora::IO::Loop } } - ret = WaitForSingleObjectEx(reinterpret_cast(handles.at(0)), uRemMS, true); + ret = WaitForSingleObjectEx(reinterpret_cast(handles.at(0)), uRemMS, bNoAlert ? FALSE : TRUE); if (timeout && ret != WAIT_IO_COMPLETION && @@ -94,7 +94,7 @@ namespace Aurora::IO::Loop } } - ret = WaitForMultipleObjectsEx(ntHandles.size(), ntHandles.data(), false, uRemMS, true); + ret = WaitForMultipleObjectsEx(ntHandles.size(), ntHandles.data(), false, uRemMS, bNoAlert ? FALSE : TRUE); if (ret < WAIT_OBJECT_0) { continue; @@ -121,13 +121,13 @@ namespace Aurora::IO::Loop } } - bool WaitSingleGeneric::WaitForOne(AuUInt32 timeout, AuUInt handle) + bool WaitSingleGeneric::WaitForOne(AuUInt32 timeout, AuUInt handle, bool bNoAlert) { DWORD ret; do { - ret = WaitForSingleObjectEx(reinterpret_cast(handle), timeout, true); + ret = WaitForSingleObjectEx(reinterpret_cast(handle), timeout, bNoAlert ? FALSE : TRUE); } while (ret == WAIT_IO_COMPLETION); diff --git a/Source/IO/Loop/WaitSingle.NT.hpp b/Source/IO/Loop/WaitSingle.NT.hpp index 88947096..d321c65b 100644 --- a/Source/IO/Loop/WaitSingle.NT.hpp +++ b/Source/IO/Loop/WaitSingle.NT.hpp @@ -11,8 +11,8 @@ namespace Aurora::IO::Loop { struct WaitSingleGeneric : virtual WaitSingleBase { - virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, AuUInt &one) override; - virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle) override; + virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, AuUInt &one, bool bNoAlert) override; + virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle, bool bNoAlert) override; virtual void OnPresleep() override; virtual void OnFinishSleep() override; virtual ELoopSource GetType() override; diff --git a/Source/IO/Loop/WaitSingle.Unix.cpp b/Source/IO/Loop/WaitSingle.Unix.cpp index 67a6beaf..5a2bd797 100644 --- a/Source/IO/Loop/WaitSingle.Unix.cpp +++ b/Source/IO/Loop/WaitSingle.Unix.cpp @@ -15,13 +15,17 @@ namespace Aurora::IO::Loop { - bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, const AuList &handlesWrite, AuUInt &one, AuUInt &two) + bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList &handles, const AuList &handlesWrite, AuUInt &one, AuUInt &two, bool bNoAlert) { fd_set readSet, writeSet; AuUInt maxHandle {}; struct timeval tv {}; - - // TODO: IO SUBMIT HOOK + int active; + + if (!bNoAlert) + { + // TODO: IO SUBMIT HOOK + } FD_ZERO(&readSet); FD_ZERO(&writeSet); @@ -45,11 +49,18 @@ namespace Aurora::IO::Loop maxHandle = AuMax(maxHandle, i + 1); } - auto active = select(maxHandle, - handles.size() ? &readSet : nullptr, - handlesWrite.size() ? &writeSet : nullptr, - nullptr, - timeout == AuUInt32(-1) ? nullptr : &tv); + active = select(maxHandle, + handles.size() ? &readSet : nullptr, + handlesWrite.size() ? &writeSet : nullptr, + nullptr, + timeout == AuUInt32(-1) ? nullptr : &tv); + +#if defined(AURORA_IS_LINUX_DERIVED) + if (!bNoAlert) + { + (void)IO::UNIX::LinuxOverlappedYield(); + } +#endif if (active == -1) { @@ -87,17 +98,25 @@ namespace Aurora::IO::Loop return true; } - bool WaitSingleGeneric::WaitForOne(AuUInt32 timeout, AuUInt read, AuUInt write) +// TODO: pass u64 end time, use pselect when available, fallback to this old logic +// LinuxOverlappedWaitForOne doesnt understand yields, we have a specific yield op like the thread waitables, but that doesnt help with a single fd +// pselect doesnt help much considering we need monotonic clock sleeps +// also when we get around to freebsd support, we need to append one to the select array to handle a kqueue of thread local APCs + transactions +// this is a mess and will continue to be a mess + bool WaitSingleGeneric::WaitForOne(AuUInt32 timeout, AuUInt read, AuUInt write, bool bNoAlert) { fd_set readSet, writeSet; struct timeval tv {}; int maxFd {}; + int active; +// TODO: see todo #if defined(AURORA_IS_LINUX_DERIVED) + if (!bNoAlert && + timeout) { bool bWriteTriggered, bReadTriggered; - - if (IO::UNIX::LinuxOverlappedWaitForOne(timeout, read, write, bReadTriggered, bWriteTriggered)) + if (IO::UNIX::LinuxOverlappedWaitForOne(timeout == AuUInt32(-1) ? 0 : timeout, read, write, bReadTriggered, bWriteTriggered)) { if (!bWriteTriggered && !bReadTriggered) { @@ -129,11 +148,20 @@ namespace Aurora::IO::Loop maxFd = AuMax(maxFd, int(write) + 1); } - auto active = select(maxFd, - read != -1 ? &readSet : nullptr, - write != -1 ? &writeSet : nullptr, - nullptr, - timeout == AuUInt32(-1) ? nullptr : &tv); + active = select(maxFd, + read != -1 ? &readSet : nullptr, + write != -1 ? &writeSet : nullptr, + nullptr, + timeout == AuUInt32(-1) ? nullptr : &tv); + +// TODO: see todo +#if defined(AURORA_IS_LINUX_DERIVED) + if (!bNoAlert && + !timeout) + { + (void)IO::UNIX::LinuxOverlappedYield(); + } +#endif if (active == 0) { diff --git a/Source/IO/Loop/WaitSingle.Unix.hpp b/Source/IO/Loop/WaitSingle.Unix.hpp index bd20360c..7879f163 100644 --- a/Source/IO/Loop/WaitSingle.Unix.hpp +++ b/Source/IO/Loop/WaitSingle.Unix.hpp @@ -11,8 +11,8 @@ namespace Aurora::IO::Loop { struct WaitSingleGeneric : virtual WaitSingleBase { - virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles _OPT_WRITE_ARRAY, AuUInt &read _OPT_WRITE_REF) override; - virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle _OPT_WRITE) override; + virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles _OPT_WRITE_ARRAY, AuUInt &read _OPT_WRITE_REF, bool bNoAlert) override; + virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle _OPT_WRITE, bool bNoAlert) override; virtual void OnPresleep() override; virtual void OnFinishSleep() override; virtual ELoopSource GetType() override; diff --git a/Source/IO/Loop/WaitSingleBase.cpp b/Source/IO/Loop/WaitSingleBase.cpp index 45b9a2f6..43e37b55 100644 --- a/Source/IO/Loop/WaitSingleBase.cpp +++ b/Source/IO/Loop/WaitSingleBase.cpp @@ -16,10 +16,24 @@ namespace Aurora::IO::Loop } bool WaitSingleBase::IsSignaled() + { + return this->IsSignaledExt(0); + } + + bool WaitSingleBase::IsSignaledExt(AuUInt8 uFlags) { bool val {}; AuUInt one {AuNumericLimits::max()}; AuUInt two {AuNumericLimits::max()}; + + bool bFlagUser = uFlags & kFlagLSTryNoSpin; + bool bFlagNoAlert = uFlags & kFlagLSTryNoIOAlerts; + + if (bFlagUser) + { + return this->IsSignaledNoSpinIfUserland(); + } + this->OnPresleep(); if (this->Singular()) @@ -27,10 +41,10 @@ namespace Aurora::IO::Loop #if defined(AURORA_IS_POSIX_DERIVED) auto handle = this->GetHandle(); auto handleRw = this->GetWriteHandle(); - val = this->WaitForOne(0, handle, handleRw); + val = this->WaitForOne(0, handle, handleRw, bFlagNoAlert); #else auto handle = this->GetHandle(); - val = this->WaitForOne(0, handle); + val = this->WaitForOne(0, handle, bFlagNoAlert); #endif if (val) { @@ -42,11 +56,11 @@ namespace Aurora::IO::Loop #if defined(AURORA_IS_POSIX_DERIVED) auto handles = this->GetHandles(); auto handlesRw = this->GetWriteHandles(); - val = this->WaitForAtleastOne(0, handles, handlesRw, one, two); + val = this->WaitForAtleastOne(0, handles, handlesRw, one, two, bFlagNoAlert); if (one == -1) one = two; #else auto handles = this->GetHandles(); - val = this->WaitForAtleastOne(0, handles, one); + val = this->WaitForAtleastOne(0, handles, one, bFlagNoAlert); #endif if (val) @@ -61,27 +75,93 @@ namespace Aurora::IO::Loop bool WaitSingleBase::WaitOn(AuUInt32 timeout) { - bool val {}; - AuUInt one {AuNumericLimits::max()}; - AuUInt two {AuNumericLimits::max()}; + return this->WaitOnExt(0, timeout); + } - this->OnPresleep(); + bool WaitSingleBase::WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) + { + bool bFlagUser = uFlags & kFlagLSTryNoSpin; + + if (bFlagUser) + { + if (this->IsSignaledNoSpinIfUserland()) + { + return true; + } + + uFlags &= ~kFlagLSTryNoSpin; + } auto uEndTime = timeout ? AuTime::SteadyClockNS() + AuMSToNS(timeout) : 0; + return this->WaitOnAbsExt(uFlags, uEndTime); + } + + bool WaitSingleBase::WaitOnAbs(AuUInt64 uTimeoutAbs) + { + return this->WaitOnAbsExt(0, uTimeoutAbs); + } + + bool WaitSingleBase::WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uEndTime) + { + bool val {}; + AuUInt32 uMSTimeout {}; + AuUInt64 uNSTimeout {}; + bool bAgain {}; + AuUInt one {AuNumericLimits::max()}; + AuUInt two {AuNumericLimits::max()}; + + bool bFlagUser = uFlags & kFlagLSTryNoSpin; + bool bFlagNoAlert = uFlags & kFlagLSTryNoIOAlerts; + + if (bFlagUser) + { + if (this->IsSignaledNoSpinIfUserland()) + { + return true; + } + } + + this->OnPresleep(); + do { + if (uEndTime) + { + auto uStartTime = Time::SteadyClockNS(); + if (uStartTime >= uEndTime) + { + uMSTimeout = 0; + uNSTimeout = 0; + } + else + { + uNSTimeout = uEndTime - uStartTime; + uMSTimeout = AuNSToMS(uNSTimeout); + } + + if (bAgain && !uMSTimeout) + { + val = false; + break; + } + } + else + { + uMSTimeout = AuUInt32(-1); + } + if (this->Singular()) { #if defined(AURORA_IS_POSIX_DERIVED) auto handle = this->GetHandle(); auto handleRw = this->GetWriteHandle(); - val = this->WaitForOne(timeout ? timeout : AuUInt32(-1), handle, handleRw); + val = this->WaitForOne(uMSTimeout, handle, handleRw, bFlagNoAlert); #else auto handle = this->GetHandle(); - val = this->WaitForOne(timeout ? timeout : AuUInt32(-1), handle); + val = this->WaitForOne(uMSTimeout, handle, bFlagNoAlert); #endif if (val) { @@ -93,11 +173,11 @@ namespace Aurora::IO::Loop #if defined(AURORA_IS_POSIX_DERIVED) auto handles = this->GetHandles(); auto handlesRw = this->GetWriteHandles(); - val = this->WaitForAtleastOne(timeout ? timeout : AuUInt32(-1), handles, handlesRw, one, two); + val = this->WaitForAtleastOne(uMSTimeout, handles, handlesRw, one, two, bFlagNoAlert); if (one == -1) one = two; #else auto handles = this->GetHandles(); - val = this->WaitForAtleastOne(timeout ? timeout : AuUInt32(-1), handles, one); + val = this->WaitForAtleastOne(uMSTimeout, handles, one, bFlagNoAlert); #endif if (val) @@ -106,18 +186,9 @@ namespace Aurora::IO::Loop } } - if (timeout) - { - auto uStartTime = Time::SteadyClockNS(); - if (uStartTime >= uEndTime) - { - return false; - } - - timeout = AuNSToMS(uEndTime - uStartTime); - } + bAgain = true; } - while (timeout && !val); + while (uEndTime && !val); this->OnFinishSleep(); return val; diff --git a/Source/IO/Loop/WaitSingleBase.hpp b/Source/IO/Loop/WaitSingleBase.hpp index a62481a7..c522a5ff 100644 --- a/Source/IO/Loop/WaitSingleBase.hpp +++ b/Source/IO/Loop/WaitSingleBase.hpp @@ -29,11 +29,14 @@ namespace Aurora::IO::Loop struct WaitSingleBase : virtual ILoopSourceEx { + virtual bool IsSignaledExt(AuUInt8 uFlags) override; virtual bool IsSignaledNoSpinIfUserland() override; virtual bool IsSignaled() override; - virtual bool WaitOn(AuUInt32 timeout) override; - - virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles _OPT_WRITE_ARRAY, AuUInt &one _OPT_WRITE_REF) = 0; - virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle _OPT_WRITE) = 0; + virtual bool WaitOnExt(AuUInt8 uFlags, AuUInt32 timeout) override; + virtual bool WaitOn(AuUInt32 timeout) override; + virtual bool WaitOnAbs(AuUInt64 uTimeoutAbs) override; + virtual bool WaitOnAbsExt(AuUInt8 uFlags, AuUInt64 uTimeoutAbs) override; + virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList &handles _OPT_WRITE_ARRAY, AuUInt &one _OPT_WRITE_REF, bool bNoAlert) = 0; + virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle _OPT_WRITE, bool bNoAlert) = 0; }; } \ No newline at end of file diff --git a/Source/IO/UNIX/IOSubmit.Linux.cpp b/Source/IO/UNIX/IOSubmit.Linux.cpp index 10f1eb58..b45c2a48 100644 --- a/Source/IO/UNIX/IOSubmit.Linux.cpp +++ b/Source/IO/UNIX/IOSubmit.Linux.cpp @@ -580,7 +580,7 @@ namespace Aurora::IO::UNIX AuTime::ns2ts(&targetTime, uTimeNow - uTargetTime); } - temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr); + temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr, false); if (temp >= 0) { @@ -748,7 +748,7 @@ namespace Aurora::IO::UNIX { break; } - temp = io_getevents(io->context, 1, uCount, ioEvents, timeout ? &targetTime : nullptr); + temp = io_getevents(io->context, 1, uCount, ioEvents, timeout ? &targetTime : nullptr, false); if (temp >= 0) { @@ -971,7 +971,7 @@ namespace Aurora::IO::UNIX { break; } - temp = io_getevents(io->context, 1, uCount, ioEvents, timeout ? &targetTime : nullptr); + temp = io_getevents(io->context, 1, uCount, ioEvents, timeout ? &targetTime : nullptr, false); if (temp >= 0) { @@ -1106,7 +1106,7 @@ namespace Aurora::IO::UNIX return false; } - bool LinuxOverlappedYield() + bool LinuxOverlappedYield(bool bUserspaceOnly) { io_event ioEvents[512]; int temp; @@ -1131,7 +1131,7 @@ namespace Aurora::IO::UNIX return false; } - temp = io_getevents(io->context, 1, 512, ioEvents, &targetTime); + temp = io_getevents(io->context, 1, 512, ioEvents, &targetTime, bUserspaceOnly); if (temp >= 0) { diff --git a/Source/IO/UNIX/IOSubmit.Linux.hpp b/Source/IO/UNIX/IOSubmit.Linux.hpp index ca6766d1..6010e7ba 100644 --- a/Source/IO/UNIX/IOSubmit.Linux.hpp +++ b/Source/IO/UNIX/IOSubmit.Linux.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include namespace Aurora::IO::UNIX { @@ -64,8 +65,10 @@ namespace Aurora::IO::UNIX int LinuxOverlappedEpollShim(int epfd, struct epoll_event *events, int maxevents, int timeout); - - bool LinuxOverlappedYield(); + // TODO: + //int LinuxOverlappedPosixPoll(struct pollfd *pPollFDs, nfds_t uCount, int timeout); + + bool LinuxOverlappedYield(bool bUserspaceOnly = false);