[+] AuIO::Loop::NewLSTimerHighResolution
[*] ILSTimer::UpdateTime -> ILSTimer::UpdateTimeWall [*] ILSTimer::UpdateTimeNs -> ILSTimer::UpdateTimeWallNs [+] ILSTimer::UpdateTimeSteady [+] ILSTimer::UpdateTimeSteadyNs
This commit is contained in:
parent
4d4f5e2501
commit
35761edaec
@ -65,9 +65,12 @@ namespace Aurora::IO::Loop
|
|||||||
{
|
{
|
||||||
/* Warning: IO timers use wall time (CurrentClock[M/NS), not SteadyClock[M/N]S()).
|
/* Warning: IO timers use wall time (CurrentClock[M/NS), not SteadyClock[M/N]S()).
|
||||||
Use auasync timers for steady-clock timing and tick timing information. */
|
Use auasync timers for steady-clock timing and tick timing information. */
|
||||||
|
// Update: not anymore
|
||||||
|
|
||||||
virtual void UpdateTime(AuUInt64 absTimeMs) = 0;
|
virtual void UpdateTimeWall(AuUInt64 absTimeMs) = 0;
|
||||||
virtual void UpdateTimeNs(AuUInt64 absTimeNs) = 0;
|
virtual void UpdateTimeWallNs(AuUInt64 absTimeNs) = 0;
|
||||||
|
virtual void UpdateTimeSteady(AuUInt64 absTimeMs) = 0;
|
||||||
|
virtual void UpdateTimeSteadyNs(AuUInt64 absTimeNs) = 0;
|
||||||
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0;
|
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0;
|
||||||
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0;
|
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0;
|
||||||
virtual void Stop() = 0;
|
virtual void Stop() = 0;
|
||||||
@ -78,7 +81,8 @@ namespace Aurora::IO::Loop
|
|||||||
virtual void *GetLastSignalInfo() = 0;
|
virtual void *GetLastSignalInfo() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
AUKN_SYM AuSPtr<ITimer> NewLSTimer(AuUInt64 absStartTimeMs /*CurrentClockMS()*/, AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0, bool bSingleshot = false /*cannot be changed*/); // warn: no IPC counterpart
|
AUKN_SYM AuSPtr<ITimer> NewLSTimer(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero = 0, AuUInt32 dwMaxIterationsOrZero = 0, bool bSingleshot = false /*cannot be changed*/, bool bIsStartTimeSteady = false); // warn: no IPC counterpart
|
||||||
|
AUKN_SYM AuSPtr<ITimer> NewLSTimerHighResolution(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero = 0, AuUInt32 dwMaxIterationsOrZero = 0, bool bSingleshot = false /*cannot be changed*/, bool bIsStartTimeSteady = false); // warn: no IPC counterpart
|
||||||
AUKN_SYM AuSPtr<ILSMutex> NewLSMutex();
|
AUKN_SYM AuSPtr<ILSMutex> NewLSMutex();
|
||||||
AUKN_SYM AuSPtr<ILSMutex> NewLSMutexSlow(); // interop-ready (usable with DbgLoopSourceToReadFd)
|
AUKN_SYM AuSPtr<ILSMutex> NewLSMutexSlow(); // interop-ready (usable with DbgLoopSourceToReadFd)
|
||||||
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false);
|
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false);
|
||||||
|
@ -262,6 +262,23 @@ namespace Aurora
|
|||||||
{
|
{
|
||||||
AuUInt32 uProtocolStackDefaultBufferSize { 64 * 1024 };
|
AuUInt32 uProtocolStackDefaultBufferSize { 64 * 1024 };
|
||||||
bool bIsVeryLargeIOApplication { false };
|
bool bIsVeryLargeIOApplication { false };
|
||||||
|
|
||||||
|
// On Win32, NewLSTimer has very bad resolution.
|
||||||
|
// On linux, timerfd is good enough.
|
||||||
|
// On other POSIX platforms, its best to keep timers emulated in process.
|
||||||
|
// By default, you have to opt into a higher res in-process, if required by the platform.
|
||||||
|
// This does not bypass IO yielding or timeouts; we simply use our own semaphore and scheduler instead of the kernels.
|
||||||
|
// It only takes 8k ns to 60k ns depending on the platform to wake a thread, and we can hit AuAsync scheduler without too much error, we can just do this instead of relying on historically shitty IO primitives.
|
||||||
|
// Assiging this to true, on any platform, will bypass the kernels timer ticker.
|
||||||
|
bool bForceAltOSTimerPrimitives { false };
|
||||||
|
|
||||||
|
// High resolution timers will be emulated in process, if this is assigned false, and we're on a platform with good IO timers
|
||||||
|
// For niche POSIX targets and Windows, this value will not be respected.
|
||||||
|
// For Linux, this value can be used to experience/test/benchmark Win32-like in-process scheduling.
|
||||||
|
bool bTrustOSTimerPrimitiveIfKnownGood { true };
|
||||||
|
|
||||||
|
//
|
||||||
|
bool bUseHighResTimerUnderIOWaitableTimer { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Win32Config
|
struct Win32Config
|
||||||
|
@ -181,9 +181,17 @@ namespace Aurora::Async
|
|||||||
for (auto &entry : pending)
|
for (auto &entry : pending)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if (entry.pool)
|
||||||
{
|
{
|
||||||
AuStaticCast<AuAsync::ThreadPool>(entry.pool)->Run(entry.target, entry.runnable, false);
|
AuStaticCast<AuAsync::ThreadPool>(entry.pool)->Run(entry.target, entry.runnable, false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IO timer hack!
|
||||||
|
entry.runnable->RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
SysPushErrorCatch();
|
SysPushErrorCatch();
|
||||||
@ -280,7 +288,10 @@ namespace Aurora::Async
|
|||||||
{
|
{
|
||||||
AU_LOCK_GUARD(gSchedLock);
|
AU_LOCK_GUARD(gSchedLock);
|
||||||
AU_DEBUG_MEMCRUNCH;
|
AU_DEBUG_MEMCRUNCH;
|
||||||
|
if (pool)
|
||||||
|
{
|
||||||
AuAtomicAdd(&pool->uAtomicCounter, 1u);
|
AuAtomicAdd(&pool->uAtomicCounter, 1u);
|
||||||
|
}
|
||||||
SchedNextTime(ns);
|
SchedNextTime(ns);
|
||||||
return AuTryInsert(gOrderedEntries,
|
return AuTryInsert(gOrderedEntries,
|
||||||
AuConstReference(ns),
|
AuConstReference(ns),
|
||||||
|
@ -14,6 +14,7 @@ namespace Aurora::Async
|
|||||||
void InitSched();
|
void InitSched();
|
||||||
void DeinitSched();
|
void DeinitSched();
|
||||||
void StartSched();
|
void StartSched();
|
||||||
|
void StartSched2();
|
||||||
|
|
||||||
bool Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
bool Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
||||||
void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target);
|
void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target);
|
||||||
|
@ -76,7 +76,7 @@ namespace Aurora::IO
|
|||||||
AuUInt64 IOWaitableIOTimer::SetTargetTimeAbs(AuUInt64 ns)
|
AuUInt64 IOWaitableIOTimer::SetTargetTimeAbs(AuUInt64 ns)
|
||||||
{
|
{
|
||||||
auto old = AuExchange(this->targetTimeAbsNs, ns);
|
auto old = AuExchange(this->targetTimeAbsNs, ns);
|
||||||
this->source->UpdateTimeNs(ns);
|
this->source->UpdateTimeSteadyNs(ns);
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,14 @@ namespace Aurora::IO
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gRuntimeConfig.ioConfig.bUseHighResTimerUnderIOWaitableTimer)
|
||||||
|
{
|
||||||
|
timer->source = AuLoop::NewLSTimerHighResolution(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
timer->source = AuLoop::NewLSTimer(0);
|
timer->source = AuLoop::NewLSTimer(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!timer->IsValid())
|
if (!timer->IsValid())
|
||||||
{
|
{
|
||||||
|
@ -31,34 +31,47 @@ namespace Aurora::IO::Loop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTime(AuUInt64 absTimeMs)
|
|
||||||
{
|
|
||||||
this->targetTime_ = AuMSToNS<AuUInt64>(absTimeMs);
|
|
||||||
UpdateTimeInternal(this->targetTime_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSTimer::UpdateTimeNs(AuUInt64 absTimeNs)
|
|
||||||
{
|
|
||||||
this->targetTime_ = absTimeNs;
|
|
||||||
UpdateTimeInternal(this->targetTime_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSTimer::UpdateTimeInternal(AuUInt64 absTimeNs)
|
void LSTimer::UpdateTimeInternal(AuUInt64 absTimeNs)
|
||||||
{
|
{
|
||||||
this->targetTime_ = absTimeNs;
|
this->targetTime_ = absTimeNs - AuTime::CurrentClockNS() + AuTime::SteadyClockNS();
|
||||||
|
UpdateTimeInternalSteady(this->targetTime_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeInternalSteady(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
this->targetTime_ = absTimeNs;
|
||||||
itimerspec spec {};
|
itimerspec spec {};
|
||||||
AuTime::auabsns2ts(&spec.it_value, this->targetTime_);
|
AuTime::ns2ts(&spec.it_value, this->targetTime_);
|
||||||
AuTime::ns2ts(&spec.it_interval, this->reschedStepNsOrZero_);
|
AuTime::ns2ts(&spec.it_interval, this->reschedStepNsOrZero_);
|
||||||
SysAssert(::timerfd_settime(this->handle, TFD_TIMER_ABSTIME, &spec, nullptr) == 0);
|
SysAssert(::timerfd_settime(this->handle, TFD_TIMER_ABSTIME, &spec, nullptr) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeWall(AuUInt64 absTimeMs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeInternal(AuMSToNS<AuUInt64>(absTimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeWallNs(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeInternal(absTimeNs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeSteady(AuUInt64 absTimeMs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeInternalSteady(AuMSToNS<AuUInt64>(absTimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeSteadyNs(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeInternalSteady(absTimeNs);
|
||||||
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero)
|
void LSTimer::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero)
|
||||||
{
|
{
|
||||||
this->reschedStepNsOrZero_ = AuMSToNS<AuUInt64>(reschedStepMsOrZero);
|
this->reschedStepNsOrZero_ = AuMSToNS<AuUInt64>(reschedStepMsOrZero);
|
||||||
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
||||||
this->count_ = 0;
|
this->count_ = 0;
|
||||||
this->UpdateTimeInternal(this->targetTime_);
|
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero)
|
void LSTimer::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero)
|
||||||
@ -66,7 +79,7 @@ namespace Aurora::IO::Loop
|
|||||||
this->reschedStepNsOrZero_ = reschedStepNsOrZero;
|
this->reschedStepNsOrZero_ = reschedStepNsOrZero;
|
||||||
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
||||||
this->count_ = 0;
|
this->count_ = 0;
|
||||||
this->UpdateTimeInternal(this->targetTime_);
|
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSTimer::OnTrigger(AuUInt handle)
|
bool LSTimer::OnTrigger(AuUInt handle)
|
||||||
@ -121,15 +134,15 @@ namespace Aurora::IO::Loop
|
|||||||
if (ok && !this->reschedStepNsOrZero_ && !bSingleshot)
|
if (ok && !this->reschedStepNsOrZero_ && !bSingleshot)
|
||||||
{
|
{
|
||||||
// Ensure non-step timers remain signaled despite the read above us caneling them
|
// Ensure non-step timers remain signaled despite the read above us caneling them
|
||||||
this->UpdateTimeInternal(this->targetTime_);
|
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM AuSPtr<ITimer> NewLSTimer(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot)
|
AuSPtr<ITimer> NewLSTimerOS(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady)
|
||||||
{
|
{
|
||||||
int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
|
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
{
|
{
|
||||||
SysPushErrorIO();
|
SysPushErrorIO();
|
||||||
@ -146,7 +159,14 @@ namespace Aurora::IO::Loop
|
|||||||
|
|
||||||
if (qwAbsStartTimeMs)
|
if (qwAbsStartTimeMs)
|
||||||
{
|
{
|
||||||
pTimer->UpdateTime(qwAbsStartTimeMs);
|
if (bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
pTimer->UpdateTimeSteady(qwAbsStartTimeMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pTimer->UpdateTimeWall(qwAbsStartTimeMs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pTimer;
|
return pTimer;
|
||||||
|
@ -17,8 +17,10 @@ namespace Aurora::IO::Loop
|
|||||||
|
|
||||||
PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_;
|
PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_;
|
||||||
|
|
||||||
virtual void UpdateTime(AuUInt64 absTimeMs) override;
|
virtual void UpdateTimeWall(AuUInt64 absTimeMs) override;
|
||||||
virtual void UpdateTimeNs(AuUInt64 absTimeNs) override;
|
virtual void UpdateTimeWallNs(AuUInt64 absTimeNs) override;
|
||||||
|
virtual void UpdateTimeSteady(AuUInt64 absTimeMs) override;
|
||||||
|
virtual void UpdateTimeSteadyNs(AuUInt64 absTimeNs) override;
|
||||||
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override;
|
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) override;
|
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
virtual void Stop() override;
|
virtual void Stop() override;
|
||||||
@ -31,6 +33,7 @@ namespace Aurora::IO::Loop
|
|||||||
virtual ELoopSource GetType() override;
|
virtual ELoopSource GetType() override;
|
||||||
|
|
||||||
void UpdateTimeInternal(AuUInt64 absTimeMs);
|
void UpdateTimeInternal(AuUInt64 absTimeMs);
|
||||||
|
void UpdateTimeInternalSteady(AuUInt64 absTimeMs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AuUInt32 maxIterationsOrZero_ {};
|
AuUInt32 maxIterationsOrZero_ {};
|
||||||
|
@ -27,16 +27,26 @@ namespace Aurora::IO::Loop
|
|||||||
this->handle = kInvalidHandle;
|
this->handle = kInvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTime(AuUInt64 absTimeMs)
|
void LSTimer::UpdateTimeWall(AuUInt64 absTimeMs)
|
||||||
{
|
{
|
||||||
UpdateTimeInternalNTWall(AuTime::ConvertTimestamp(absTimeMs));
|
UpdateTimeInternalNTWall(AuTime::ConvertTimestamp(absTimeMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTimeNs(AuUInt64 absTimeNs)
|
void LSTimer::UpdateTimeWallNs(AuUInt64 absTimeNs)
|
||||||
{
|
{
|
||||||
UpdateTimeInternalNTWall(AuTime::ConvertTimestampNs(absTimeNs));
|
UpdateTimeInternalNTWall(AuTime::ConvertTimestampNs(absTimeNs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeSteady(AuUInt64 absTimeMs)
|
||||||
|
{
|
||||||
|
UpdateTimeSteadyNs(AuMSToNS<AuUInt64>(absTimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimer::UpdateTimeSteadyNs(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
UpdateTimeWallNs(absTimeNs - AuTime::SteadyClockNS() + AuTime::CurrentClockNS());
|
||||||
|
}
|
||||||
|
|
||||||
void LSTimer::UpdateTimeInternalNTWall(AuUInt64 absTimeNs)
|
void LSTimer::UpdateTimeInternalNTWall(AuUInt64 absTimeNs)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER i;
|
LARGE_INTEGER i;
|
||||||
@ -120,7 +130,7 @@ namespace Aurora::IO::Loop
|
|||||||
return ELoopSource::eSourceTimer;
|
return ELoopSource::eSourceTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM AuSPtr<ITimer> NewLSTimer(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot)
|
AuSPtr<ITimer> NewLSTimerOS(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady)
|
||||||
{
|
{
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/threadpoolapiset/nf-threadpoolapiset-setthreadpoolwait
|
// https://docs.microsoft.com/en-us/windows/win32/api/threadpoolapiset/nf-threadpoolapiset-setthreadpoolwait
|
||||||
// TODO: Is that any better with an event?
|
// TODO: Is that any better with an event?
|
||||||
@ -142,7 +152,14 @@ namespace Aurora::IO::Loop
|
|||||||
|
|
||||||
if (absStartTimeMs)
|
if (absStartTimeMs)
|
||||||
{
|
{
|
||||||
object->UpdateTime(absStartTimeMs);
|
if (bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
object->UpdateTimeSteady(absStartTimeMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object->UpdateTimeWall(absStartTimeMs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
|
@ -17,9 +17,10 @@ namespace Aurora::IO::Loop
|
|||||||
|
|
||||||
PROXY_LOOP_LOOPSOURCE_EXT_APIS_;
|
PROXY_LOOP_LOOPSOURCE_EXT_APIS_;
|
||||||
|
|
||||||
virtual void UpdateTime(AuUInt64 absTimeMs) override;
|
virtual void UpdateTimeWall(AuUInt64 absTimeMs) override;
|
||||||
virtual void UpdateTimeNs(AuUInt64 absTimeNs) override;
|
virtual void UpdateTimeWallNs(AuUInt64 absTimeNs) override;
|
||||||
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override;
|
virtual void UpdateTimeSteady(AuUInt64 absTimeMs) override;
|
||||||
|
virtual void UpdateTimeSteadyNs(AuUInt64 absTimeNs) override; virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) override;
|
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
virtual void Stop() override;
|
virtual void Stop() override;
|
||||||
|
|
||||||
|
52
Source/IO/Loop/LSTimer.cpp
Executable file
52
Source/IO/Loop/LSTimer.cpp
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: LSTimer.cpp
|
||||||
|
Date: 2021-10-1 - 2024-09-08
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#include <Source/RuntimeInternal.hpp>
|
||||||
|
|
||||||
|
namespace Aurora::IO::Loop
|
||||||
|
{
|
||||||
|
AuSPtr<ITimer> NewLSTimerOS(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady);
|
||||||
|
AuSPtr<ITimer> NewLSTimerIP(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady);
|
||||||
|
|
||||||
|
static bool MustUseAltTimer()
|
||||||
|
{
|
||||||
|
return gRuntimeConfig.ioConfig.bForceAltOSTimerPrimitives;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsHPInProcess()
|
||||||
|
{
|
||||||
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return !gRuntimeConfig.ioConfig.bTrustOSTimerPrimitiveIfKnownGood;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<ITimer> NewLSTimerHighResolution(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
if (IsHPInProcess())
|
||||||
|
{
|
||||||
|
return NewLSTimerIP(qwAbsStartTimeMs, dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot, bIsStartTimeSteady);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NewLSTimerOS(qwAbsStartTimeMs, dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot, bIsStartTimeSteady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM AuSPtr<ITimer> NewLSTimer(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
if (MustUseAltTimer())
|
||||||
|
{
|
||||||
|
return NewLSTimerIP(qwAbsStartTimeMs, dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot, bIsStartTimeSteady);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NewLSTimerOS(qwAbsStartTimeMs, dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot, bIsStartTimeSteady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
290
Source/IO/Loop/LSTimerNoKernelScheduler.cpp
Executable file
290
Source/IO/Loop/LSTimerNoKernelScheduler.cpp
Executable file
@ -0,0 +1,290 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: LSTimerNoKernelScheduler.cpp
|
||||||
|
Date: 2024-09-08
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#include <Source/RuntimeInternal.hpp>
|
||||||
|
#include <Source/Async/IAsyncRunnable.hpp>
|
||||||
|
#include <Source/Async/AuSchedular.hpp>
|
||||||
|
#include "LSTimerNoKernelScheduler.hpp"
|
||||||
|
#include <Source/Threading/Primitives/SMTYield.hpp>
|
||||||
|
|
||||||
|
namespace Aurora::IO::Loop
|
||||||
|
{
|
||||||
|
void LSTimerIPDispatcher::RunAsync()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex);
|
||||||
|
if (this->pParent)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->pParent->cs);
|
||||||
|
this->pParent->Set();
|
||||||
|
this->pParent = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LSTimerIP::LSTimerIP(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot) :
|
||||||
|
bSingleshot(bSingleshot),
|
||||||
|
reschedStepNsOrZero_(AuMSToNS<AuUInt64>(reschedStepMsOrZero)),
|
||||||
|
maxIterationsOrZero_(maxIterationsOrZero)
|
||||||
|
{
|
||||||
|
this->targetTime_ = AuTime::CurrentClockNS();
|
||||||
|
}
|
||||||
|
|
||||||
|
LSTimerIP::~LSTimerIP()
|
||||||
|
{
|
||||||
|
if (this->pDispatcher)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->pDispatcher->mutex);
|
||||||
|
this->pDispatcher->pParent = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTimeWall(AuUInt64 absTimeMs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeWallNs(AuMSToNS<AuUInt64>(absTimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTimeWallNs(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeSteadyNs(absTimeNs - AuTime::CurrentClockNS() + AuTime::SteadyClockNS());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTimeSteady(AuUInt64 absTimeMs)
|
||||||
|
{
|
||||||
|
this->UpdateTimeSteadyNs(AuMSToNS<AuUInt64>(absTimeMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero)
|
||||||
|
{
|
||||||
|
this->UpdateTickRateIfAnyNs(AuMSToNS<AuUInt64>(reschedStepMsOrZero), maxIterationsOrZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->cs);
|
||||||
|
|
||||||
|
this->Stop();
|
||||||
|
|
||||||
|
this->reschedStepNsOrZero_ = reschedStepNsOrZero;
|
||||||
|
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
||||||
|
this->count_ = 0;
|
||||||
|
|
||||||
|
if (this->targetTime_)
|
||||||
|
{
|
||||||
|
this->UpdateTimeSteadyNs(this->targetTime_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::UpdateTimeSteadyNs(AuUInt64 absTimeNs)
|
||||||
|
{
|
||||||
|
AU_DEBUG_MEMCRUNCH;
|
||||||
|
AU_LOCK_GUARD(this->cs);
|
||||||
|
|
||||||
|
AuSPtr<LSTimerIPDispatcher> pNext;
|
||||||
|
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->dispatcherMutex);
|
||||||
|
|
||||||
|
if (this->pDispatcher)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->pDispatcher->mutex);
|
||||||
|
|
||||||
|
if (this->pDispatcher->pParent)
|
||||||
|
{
|
||||||
|
// abort
|
||||||
|
this->pDispatcher->pParent = nullptr;
|
||||||
|
pNext = this->pDispatcher = AuMakeShared<LSTimerIPDispatcher>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pNext = this->pDispatcher;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pNext = this->pDispatcher = AuMakeShared<LSTimerIPDispatcher>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SysAssert(pNext, "impossible OOM during timer op");
|
||||||
|
|
||||||
|
pNext->pParent = this;
|
||||||
|
AuAsync::Schedule(absTimeNs, nullptr, {}, pNext);
|
||||||
|
this->targetTime_ = absTimeNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LSTimerIP::Stop()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->cs);
|
||||||
|
|
||||||
|
AU_LOCK_GUARD(this->dispatcherMutex);
|
||||||
|
if (this->pDispatcher)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->pDispatcher->mutex);
|
||||||
|
this->pDispatcher->pParent = nullptr;
|
||||||
|
this->pDispatcher = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::IsSignaledNonblocking()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->cs);
|
||||||
|
|
||||||
|
auto uCurrentTime = AuTime::SteadyClockNS();
|
||||||
|
auto uNextTickTarget = AuAtomicLoad(&this->targetTime_);
|
||||||
|
|
||||||
|
if (uCurrentTime < uNextTickTarget)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto uNextTick = uNextTickTarget + this->reschedStepNsOrZero_;
|
||||||
|
|
||||||
|
if (uCurrentTime > uNextTick)
|
||||||
|
{
|
||||||
|
this->targetTime_ = uCurrentTime;
|
||||||
|
uNextTick = uCurrentTime + this->reschedStepNsOrZero_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->maxIterationsOrZero_)
|
||||||
|
{
|
||||||
|
this->UpdateTimeSteadyNs(uNextTick);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuAtomicAdd<AuUInt32>(&this->count_, 1) <= this->maxIterationsOrZero_)
|
||||||
|
{
|
||||||
|
this->UpdateTimeSteadyNs(uNextTick);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::OnTrigger(AuUInt handle)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->cs);
|
||||||
|
|
||||||
|
if (IsSignaledNoSpinIfUserland())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LSEvent::Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::TryTakeWaitNS(AuUInt64 uTimeoutAbs)
|
||||||
|
{
|
||||||
|
auto uNextTime = AuAtomicLoad(&this->targetTime_);
|
||||||
|
|
||||||
|
if (!uNextTime)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->IsSignaledNonblocking())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto uNow = AuTime::CurrentClockNS();
|
||||||
|
if (uNow >= uNextTime)
|
||||||
|
{
|
||||||
|
return this->IsSignaledNonblocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32 note: there is quite a bit of hackery behind SleepNs
|
||||||
|
// Win 7 and lower may spin for a bit
|
||||||
|
// Various CPUs may enter a user-space montior condition
|
||||||
|
// Dumb CPUs will SMT yield
|
||||||
|
// Alderlake et al can tpause
|
||||||
|
if (uTimeoutAbs)
|
||||||
|
{
|
||||||
|
AuThreading::SleepNs(AuMin(uNextTime, uTimeoutAbs) - uNow);
|
||||||
|
return this->IsSignaledNonblocking();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AuThreading::SleepNs(uNextTime - uNow);
|
||||||
|
return this->IsSignaledNonblocking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ELoopSource LSTimerIP::GetType()
|
||||||
|
{
|
||||||
|
return ELoopSource::eSourceTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::TryInit()
|
||||||
|
{
|
||||||
|
return LSEvent::TryInit(false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::IsSignaled()
|
||||||
|
{
|
||||||
|
return this->TryTakeSpin();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::IsSignaledNoSpinIfUserland()
|
||||||
|
{
|
||||||
|
return this->IsSignaledNonblocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::TryTakeNoSpin()
|
||||||
|
{
|
||||||
|
return this->IsSignaledNonblocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::TryTakeSpin()
|
||||||
|
{
|
||||||
|
return Threading::Primitives::DoTryIf([&]
|
||||||
|
{
|
||||||
|
return this->TryTakeNoSpin();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LSTimerIP::TryTake()
|
||||||
|
{
|
||||||
|
return this->TryTakeSpin();
|
||||||
|
}
|
||||||
|
|
||||||
|
AuSPtr<ITimer> NewLSTimerIP(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
AuAsync::StartSched2();
|
||||||
|
|
||||||
|
auto pTimer = AuMakeShared<LSTimerIP>(dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot);
|
||||||
|
if (!pTimer)
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pTimer->TryInit())
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qwAbsStartTimeMs)
|
||||||
|
{
|
||||||
|
if (bIsStartTimeSteady)
|
||||||
|
{
|
||||||
|
pTimer->UpdateTimeSteady(qwAbsStartTimeMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pTimer->UpdateTimeWall(qwAbsStartTimeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pTimer;
|
||||||
|
}
|
||||||
|
}
|
68
Source/IO/Loop/LSTimerNoKernelScheduler.hpp
Executable file
68
Source/IO/Loop/LSTimerNoKernelScheduler.hpp
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: LSTimerNoKernelScheduler.hpp
|
||||||
|
Date: 2024-09-08
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LSHandle.hpp"
|
||||||
|
#include "LSEvent.hpp"
|
||||||
|
|
||||||
|
namespace Aurora::IO::Loop
|
||||||
|
{
|
||||||
|
struct LSTimerIP;
|
||||||
|
|
||||||
|
struct LSTimerIPDispatcher : AuAsync::IAsyncRunnable
|
||||||
|
{
|
||||||
|
AuMutex mutex;
|
||||||
|
LSTimerIP *pParent {};
|
||||||
|
|
||||||
|
void RunAsync() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSTimerIP : LSEvent, ITimer
|
||||||
|
{
|
||||||
|
LSTimerIP(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot);
|
||||||
|
~LSTimerIP();
|
||||||
|
|
||||||
|
PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_;
|
||||||
|
|
||||||
|
virtual void UpdateTimeWall(AuUInt64 absTimeMs) override;
|
||||||
|
virtual void UpdateTimeWallNs(AuUInt64 absTimeNs) override;
|
||||||
|
virtual void UpdateTimeSteady(AuUInt64 absTimeMs) override;
|
||||||
|
virtual void UpdateTimeSteadyNs(AuUInt64 absTimeNs) override;
|
||||||
|
virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
|
virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) override;
|
||||||
|
virtual void Stop() override;
|
||||||
|
|
||||||
|
bool TryInit();
|
||||||
|
|
||||||
|
bool IsSignaled() override;
|
||||||
|
bool IsSignaledNoSpinIfUserland() override;
|
||||||
|
ELoopSource GetType() override;
|
||||||
|
|
||||||
|
virtual bool OnTrigger(AuUInt handle) override;
|
||||||
|
|
||||||
|
bool TryTakeNoSpin();
|
||||||
|
bool TryTakeSpin();
|
||||||
|
bool TryTake();
|
||||||
|
bool TryTakeWaitNS(AuUInt64 timeout);
|
||||||
|
|
||||||
|
void UpdateTimeInternal(AuUInt64 absTimeMs);
|
||||||
|
void UpdateTimeInternalSteady(AuUInt64 absTimeMs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct LSTimerIPDispatcher;
|
||||||
|
AuUInt32 maxIterationsOrZero_ {};
|
||||||
|
AuUInt64 reschedStepNsOrZero_ {}, targetTime_ {};
|
||||||
|
AuUInt32 count_ {};
|
||||||
|
bool bSingleshot {};
|
||||||
|
|
||||||
|
AuCriticalSection cs;
|
||||||
|
AuMutex dispatcherMutex;
|
||||||
|
AuSPtr<LSTimerIPDispatcher> pDispatcher;
|
||||||
|
bool IsSignaledNonblocking();
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user