/*** Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSTimerNoKernelScheduler.cpp Date: 2024-09-08 Author: Reece ***/ #include #include #include #include "LSTimerNoKernelScheduler.hpp" #include 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(reschedStepMsOrZero)), maxIterationsOrZero_(maxIterationsOrZero) { this->targetTime_ = AuTime::SteadyClockNS(); } LSTimerIP::~LSTimerIP() { } void LSTimerIP::UpdateTimeWall(AuUInt64 absTimeMs) { this->UpdateTimeWallNs(AuMSToNS(absTimeMs)); } void LSTimerIP::UpdateTimeWallNs(AuUInt64 absTimeNs) { this->UpdateTimeSteadyNs(absTimeNs - AuTime::CurrentClockNS() + AuTime::SteadyClockNS()); } void LSTimerIP::UpdateTimeSteady(AuUInt64 absTimeMs) { this->UpdateTimeSteadyNs(AuMSToNS(absTimeMs)); } void LSTimerIP::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) { this->UpdateTickRateIfAnyNs(AuMSToNS(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(); 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(&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 NewLSTimerIP(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady) { AuAsync::StartSched2(); auto pTimer = AuMakeShared(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; } }