[+] 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()).
|
||||
Use auasync timers for steady-clock timing and tick timing information. */
|
||||
// Update: not anymore
|
||||
|
||||
virtual void UpdateTime(AuUInt64 absTimeMs) = 0;
|
||||
virtual void UpdateTimeNs(AuUInt64 absTimeNs) = 0;
|
||||
virtual void UpdateTimeWall(AuUInt64 absTimeMs) = 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 UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0;
|
||||
virtual void Stop() = 0;
|
||||
@ -78,7 +81,8 @@ namespace Aurora::IO::Loop
|
||||
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> NewLSMutexSlow(); // interop-ready (usable with DbgLoopSourceToReadFd)
|
||||
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false);
|
||||
|
@ -260,8 +260,25 @@ namespace Aurora
|
||||
|
||||
struct IOConfig
|
||||
{
|
||||
AuUInt32 uProtocolStackDefaultBufferSize { 64 * 1024 };
|
||||
bool bIsVeryLargeIOApplication { false };
|
||||
AuUInt32 uProtocolStackDefaultBufferSize { 64 * 1024 };
|
||||
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
|
||||
|
@ -182,7 +182,15 @@ namespace Aurora::Async
|
||||
{
|
||||
try
|
||||
{
|
||||
AuStaticCast<AuAsync::ThreadPool>(entry.pool)->Run(entry.target, entry.runnable, false);
|
||||
if (entry.pool)
|
||||
{
|
||||
AuStaticCast<AuAsync::ThreadPool>(entry.pool)->Run(entry.target, entry.runnable, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// IO timer hack!
|
||||
entry.runnable->RunAsync();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -280,7 +288,10 @@ namespace Aurora::Async
|
||||
{
|
||||
AU_LOCK_GUARD(gSchedLock);
|
||||
AU_DEBUG_MEMCRUNCH;
|
||||
AuAtomicAdd(&pool->uAtomicCounter, 1u);
|
||||
if (pool)
|
||||
{
|
||||
AuAtomicAdd(&pool->uAtomicCounter, 1u);
|
||||
}
|
||||
SchedNextTime(ns);
|
||||
return AuTryInsert(gOrderedEntries,
|
||||
AuConstReference(ns),
|
||||
|
@ -14,6 +14,7 @@ namespace Aurora::Async
|
||||
void InitSched();
|
||||
void DeinitSched();
|
||||
void StartSched();
|
||||
void StartSched2();
|
||||
|
||||
bool Schedule(AuUInt64 ns, IThreadPoolInternal *pool, WorkerId_t target, AuSPtr<IAsyncRunnable> runnable);
|
||||
void TerminateSceduledTasks(IThreadPoolInternal *pool, WorkerId_t target);
|
||||
|
@ -76,7 +76,7 @@ namespace Aurora::IO
|
||||
AuUInt64 IOWaitableIOTimer::SetTargetTimeAbs(AuUInt64 ns)
|
||||
{
|
||||
auto old = AuExchange(this->targetTimeAbsNs, ns);
|
||||
this->source->UpdateTimeNs(ns);
|
||||
this->source->UpdateTimeSteadyNs(ns);
|
||||
return old;
|
||||
}
|
||||
|
||||
@ -89,7 +89,14 @@ namespace Aurora::IO
|
||||
return {};
|
||||
}
|
||||
|
||||
timer->source = AuLoop::NewLSTimer(0);
|
||||
if (gRuntimeConfig.ioConfig.bUseHighResTimerUnderIOWaitableTimer)
|
||||
{
|
||||
timer->source = AuLoop::NewLSTimerHighResolution(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer->source = AuLoop::NewLSTimer(0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this->targetTime_ = absTimeNs;
|
||||
this->targetTime_ = absTimeNs - AuTime::CurrentClockNS() + AuTime::SteadyClockNS();
|
||||
UpdateTimeInternalSteady(this->targetTime_);
|
||||
}
|
||||
|
||||
void LSTimer::UpdateTimeInternalSteady(AuUInt64 absTimeNs)
|
||||
{
|
||||
this->targetTime_ = absTimeNs;
|
||||
itimerspec spec {};
|
||||
AuTime::auabsns2ts(&spec.it_value, this->targetTime_);
|
||||
AuTime::ns2ts(&spec.it_value, this->targetTime_);
|
||||
AuTime::ns2ts(&spec.it_interval, this->reschedStepNsOrZero_);
|
||||
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)
|
||||
{
|
||||
this->reschedStepNsOrZero_ = AuMSToNS<AuUInt64>(reschedStepMsOrZero);
|
||||
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
||||
this->count_ = 0;
|
||||
this->UpdateTimeInternal(this->targetTime_);
|
||||
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||
}
|
||||
|
||||
void LSTimer::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero)
|
||||
@ -66,7 +79,7 @@ namespace Aurora::IO::Loop
|
||||
this->reschedStepNsOrZero_ = reschedStepNsOrZero;
|
||||
this->maxIterationsOrZero_ = maxIterationsOrZero;
|
||||
this->count_ = 0;
|
||||
this->UpdateTimeInternal(this->targetTime_);
|
||||
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||
}
|
||||
|
||||
bool LSTimer::OnTrigger(AuUInt handle)
|
||||
@ -121,15 +134,15 @@ namespace Aurora::IO::Loop
|
||||
if (ok && !this->reschedStepNsOrZero_ && !bSingleshot)
|
||||
{
|
||||
// Ensure non-step timers remain signaled despite the read above us caneling them
|
||||
this->UpdateTimeInternal(this->targetTime_);
|
||||
this->UpdateTimeInternalSteady(this->targetTime_);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
SysPushErrorIO();
|
||||
@ -146,7 +159,14 @@ namespace Aurora::IO::Loop
|
||||
|
||||
if (qwAbsStartTimeMs)
|
||||
{
|
||||
pTimer->UpdateTime(qwAbsStartTimeMs);
|
||||
if (bIsStartTimeSteady)
|
||||
{
|
||||
pTimer->UpdateTimeSteady(qwAbsStartTimeMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
pTimer->UpdateTimeWall(qwAbsStartTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
return pTimer;
|
||||
|
@ -17,8 +17,10 @@ namespace Aurora::IO::Loop
|
||||
|
||||
PROXY_LOOP_LOOPSOURCE_EXT_WAIT_APIS_;
|
||||
|
||||
virtual void UpdateTime(AuUInt64 absTimeMs) override;
|
||||
virtual void UpdateTimeNs(AuUInt64 absTimeNs) override;
|
||||
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;
|
||||
@ -31,6 +33,7 @@ namespace Aurora::IO::Loop
|
||||
virtual ELoopSource GetType() override;
|
||||
|
||||
void UpdateTimeInternal(AuUInt64 absTimeMs);
|
||||
void UpdateTimeInternalSteady(AuUInt64 absTimeMs);
|
||||
|
||||
private:
|
||||
AuUInt32 maxIterationsOrZero_ {};
|
||||
|
@ -27,16 +27,26 @@ namespace Aurora::IO::Loop
|
||||
this->handle = kInvalidHandle;
|
||||
}
|
||||
|
||||
void LSTimer::UpdateTime(AuUInt64 absTimeMs)
|
||||
void LSTimer::UpdateTimeWall(AuUInt64 absTimeMs)
|
||||
{
|
||||
UpdateTimeInternalNTWall(AuTime::ConvertTimestamp(absTimeMs));
|
||||
}
|
||||
|
||||
void LSTimer::UpdateTimeNs(AuUInt64 absTimeNs)
|
||||
void LSTimer::UpdateTimeWallNs(AuUInt64 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)
|
||||
{
|
||||
LARGE_INTEGER i;
|
||||
@ -120,7 +130,7 @@ namespace Aurora::IO::Loop
|
||||
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
|
||||
// TODO: Is that any better with an event?
|
||||
@ -142,7 +152,14 @@ namespace Aurora::IO::Loop
|
||||
|
||||
if (absStartTimeMs)
|
||||
{
|
||||
object->UpdateTime(absStartTimeMs);
|
||||
if (bIsStartTimeSteady)
|
||||
{
|
||||
object->UpdateTimeSteady(absStartTimeMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
object->UpdateTimeWall(absStartTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
|
@ -17,9 +17,10 @@ namespace Aurora::IO::Loop
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
|
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