AuroraRuntime/Source/IO/Loop/LSTimerNoKernelScheduler.cpp

293 lines
7.3 KiB
C++
Executable File

/***
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);
// TODO: not as optimized
// TODO: we now just added a AuSharedFromThis requirement for no good reason
if (auto pThat = AuTryLockMemoryType(this->pParent))
{
pThat->Set();
}
}
LSTimerIP::LSTimerIP(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot) :
bSingleshot(bSingleshot),
reschedStepNsOrZero_(AuMSToNS<AuUInt64>(reschedStepMsOrZero)),
maxIterationsOrZero_(maxIterationsOrZero)
{
this->targetTime_ = AuTime::SteadyClockNS();
}
LSTimerIP::~LSTimerIP()
{
}
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);
if (this->pDispatcher)
{
AU_LOCK_GUARD(this->pDispatcher->mutex);
AuResetMember(this->pDispatcher->pParent);
}
// TODO: optimization was removed. readd it.
auto pNext = this->pDispatcher = AuMakeShared<LSTimerIPDispatcher>();
SysAssert(pNext, "impossible OOM during timer op");
pNext->pParent = this->SharedFromThis();;
AuAsync::Schedule(absTimeNs, nullptr, {}, pNext);
this->targetTime_ = absTimeNs;
}
void LSTimerIP::Stop()
{
AU_LOCK_GUARD(this->cs);
if (this->pDispatcher)
{
AU_LOCK_GUARD(this->pDispatcher->mutex);
AuResetMember(this->pDispatcher->pParent);
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)
{
return IsSignaledNoSpinIfUserland();
return true;
}
bool LSTimerIP::TryTakeWaitNS(AuUInt64 uTimeoutAbs)
{
auto uNextTime = AuAtomicLoad(&this->targetTime_);
if (!uNextTime)
{
return false;
}
if (this->IsSignaledNonblocking())
{
return true;
}
else
{
uNextTime = AuAtomicLoad(&this->targetTime_);
}
auto uNow = AuTime::SteadyClockNS();
if (uNow >= uNextTime)
{
return this->IsSignaledNonblocking();
}
// TODO: this added mess isn't even hit.
// 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)
{
if (uTimeoutAbs < uNextTime)
{
AuThreading::SleepNs(uTimeoutAbs - uNow);
return false;
}
AuThreading::SleepNs(uNextTime - uNow);
while (uNextTime < AuTime::SteadyClockNS())
{
if (this->IsSignaledNonblocking())
{
return true;
}
}
return this->IsSignaledNonblocking();
}
else
{
AuThreading::SleepNs(uNextTime - uNow);
if (this->IsSignaledNonblocking())
{
return true;
}
while (AuAtomicLoad(&this->targetTime_) < AuTime::SteadyClockNS())
{
if (this->IsSignaledNonblocking())
{
return true;
}
}
}
return false;
}
ELoopSource LSTimerIP::GetType()
{
return ELoopSource::eSourceTimer;
}
bool LSTimerIP::TryInit()
{
return LSEvent::TryInit(false, true, 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;
}
}