[+] ILoopSource::IsSignaledExt(...)

[+] ILoopSource::WaitOnExt(...)
[+] ILoopSource::WaitOnAbsExt(...)
This commit is contained in:
Reece Wilson 2024-09-07 22:45:34 +01:00
parent 77546b5098
commit 4d4f5e2501
39 changed files with 513 additions and 158 deletions

View File

@ -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;
};
}

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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.)
}

View File

@ -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)

View File

@ -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_;
};
}

View File

@ -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;

View File

@ -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<typename T>
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;
}
}

View File

@ -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<AuUInt64>(timeout) + AuTime::SteadyClockNS()); \
} \
}
namespace Aurora::IO::Loop
{
#if defined(AURORA_IS_MODERNNT_DERIVED)

View File

@ -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<AuUInt64>(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))
{

View File

@ -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;

View File

@ -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<AuUInt64>(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))
{

View File

@ -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 {};
};

View File

@ -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<AuUInt64>(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))
{

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -15,6 +15,8 @@ namespace Aurora::IO::Loop
LSMutex(HANDLE handle);
~LSMutex();
PROXY_LOOP_LOOPSOURCE_EXT_APIS_;
bool Unlock() override;
bool IsSignaled() override;

View File

@ -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)

View File

@ -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;

View File

@ -15,6 +15,8 @@ namespace Aurora::IO::Loop
LSSemaphore();
LSSemaphore(HANDLE handle);
~LSSemaphore();
PROXY_LOOP_LOOPSOURCE_EXT_APIS_;
bool TryInit(AuUInt32 initialCount = 0);

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -10,6 +10,16 @@
#include "ILoopSourceEx.hpp"
#include <poll.h>
// TODO:
#if defined(AURORA_IS_LINUX_DERIVED)
namespace Aurora::IO::UNIX
{
bool LinuxOverlappedYield(bool bUserspaceOnly);
}
#endif
namespace Aurora::IO::Loop
{
AuList<AuSPtr<ILoopSource>> WaitMultipleOrObjects(const AuList<AuSPtr<ILoopSource>> &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
}

View File

@ -10,7 +10,7 @@
namespace Aurora::IO::Loop
{
bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &handles, AuUInt &one)
bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &handles, AuUInt &one, bool bNoAlert)
{
if (handles.empty())
{
@ -43,7 +43,7 @@ namespace Aurora::IO::Loop
}
}
ret = WaitForSingleObjectEx(reinterpret_cast<HANDLE>(handles.at(0)), uRemMS, true);
ret = WaitForSingleObjectEx(reinterpret_cast<HANDLE>(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>(handle), timeout, true);
ret = WaitForSingleObjectEx(reinterpret_cast<HANDLE>(handle), timeout, bNoAlert ? FALSE : TRUE);
}
while (ret == WAIT_IO_COMPLETION);

View File

@ -11,8 +11,8 @@ namespace Aurora::IO::Loop
{
struct WaitSingleGeneric : virtual WaitSingleBase
{
virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &handles, AuUInt &one) override;
virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle) override;
virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &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;

View File

@ -15,13 +15,17 @@
namespace Aurora::IO::Loop
{
bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &handles, const AuList<AuUInt> &handlesWrite, AuUInt &one, AuUInt &two)
bool WaitSingleGeneric::WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &handles, const AuList<AuUInt> &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)
{

View File

@ -11,8 +11,8 @@ namespace Aurora::IO::Loop
{
struct WaitSingleGeneric : virtual WaitSingleBase
{
virtual bool WaitForAtleastOne(AuUInt32 timeout, const AuList<AuUInt> &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<AuUInt> &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;

View File

@ -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<AuUInt>::max()};
AuUInt two {AuNumericLimits<AuUInt>::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<AuUInt>::max()};
AuUInt two {AuNumericLimits<AuUInt>::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<AuUInt64>(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<AuUInt>::max()};
AuUInt two {AuNumericLimits<AuUInt>::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<AuInt64>(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<AuInt64>(uEndTime - uStartTime);
}
bAgain = true;
}
while (timeout && !val);
while (uEndTime && !val);
this->OnFinishSleep();
return val;

View File

@ -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<AuUInt> &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<AuUInt> &handles _OPT_WRITE_ARRAY, AuUInt &one _OPT_WRITE_REF, bool bNoAlert) = 0;
virtual bool WaitForOne(AuUInt32 timeout, AuUInt handle _OPT_WRITE, bool bNoAlert) = 0;
};
}

View File

@ -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)
{

View File

@ -9,6 +9,7 @@
#pragma once
#include <linux/aio_abi.h>
#include <poll.h>
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);