/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSTimer.NT.cpp Date: 2021-10-1 Author: Reece ***/ #include #include "LSTimer.hpp" #include namespace Aurora::IO::Loop { static AuUInt32 gHighResTimers {}; LSTimer::LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, HANDLE handle, bool bHighRes) : LSHandle(AuReinterpretCast(handle)), reschedStepNsOrZero_(AuMSToNS(reschedStepMsOrZero)), bSingleshot(bSingleshot), maxIterationsOrZero_(maxIterationsOrZero), bHighRes_(bHighRes) { this->targetTime_ = AuTime::ConvertTimestamp(AuTime::CurrentClockMS()); if (this->bHighRes_) { if (AuAtomicAdd(&gHighResTimers, 1u) == 1) { Win32DropSchedulerResolutionForced(); } } } LSTimer::~LSTimer() { auto handle = AuReinterpretCast(this->handle); AuWin32CloseHandle(handle); this->handle = kInvalidHandle; if (this->bHighRes_) { if (AuAtomicSub(&gHighResTimers, 1u) == 0) { Win32DropSchedulerResolutionForcedUndo(); } } } void LSTimer::UpdateTimeWall(AuUInt64 absTimeMs) { UpdateTimeInternalNTWall(AuTime::ConvertTimestamp(absTimeMs)); } void LSTimer::UpdateTimeWallNs(AuUInt64 absTimeNs) { UpdateTimeInternalNTWall(AuTime::ConvertTimestampNs(absTimeNs)); } void LSTimer::UpdateTimeSteady(AuUInt64 absTimeMs) { UpdateTimeSteadyNs(AuMSToNS(absTimeMs)); } void LSTimer::UpdateTimeSteadyNs(AuUInt64 absTimeNs) { UpdateTimeWallNs(absTimeNs - AuTime::SteadyClockNS() + AuTime::CurrentClockNS()); } void LSTimer::UpdateTimeInternalNTWall(AuUInt64 absTimeNs) { LARGE_INTEGER i; this->targetTime_ = absTimeNs; i.QuadPart = absTimeNs; SysAssert(::SetWaitableTimer(AuReinterpretCast(this->handle), &i, 0, nullptr, nullptr, false)); } void LSTimer::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) { this->reschedStepNsOrZero_ = AuMSToNS(reschedStepMsOrZero); this->maxIterationsOrZero_ = maxIterationsOrZero; this->count_ = 0; UpdateTimeInternalNTWall(this->targetTime_); } void LSTimer::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) { this->reschedStepNsOrZero_ = reschedStepNsOrZero; this->maxIterationsOrZero_ = maxIterationsOrZero; this->count_ = 0; UpdateTimeInternalNTWall(this->targetTime_); } bool LSTimer::OnTrigger(AuUInt handle) { if (!this->reschedStepNsOrZero_) { return true; } auto uCurrentTime = AuTime::ConvertTimestampNs(AuTime::CurrentClockNS()); auto uNextTick = this->targetTime_ + (AuUInt64(this->reschedStepNsOrZero_) / 100ULL); if (this->targetTime_ > uCurrentTime || uCurrentTime > uNextTick) { this->targetTime_ = uCurrentTime; uNextTick = this->targetTime_ + (AuUInt64(this->reschedStepNsOrZero_) / 100ULL); } if (!this->maxIterationsOrZero_) { this->UpdateTimeInternalNTWall(uNextTick); return true; } if (AuAtomicAdd(&this->count_, 1) <= this->maxIterationsOrZero_) { this->UpdateTimeInternalNTWall(uNextTick); return true; } else { Stop(); return false; } } void LSTimer::Stop() { bool bSuccess = ::CancelWaitableTimer(AuReinterpretCast(handle)); SysAssertDbg(bSuccess); LARGE_INTEGER i; i.QuadPart = AuNumericLimits::max(); SysAssert(::SetWaitableTimer(AuReinterpretCast(this->handle), &i, 0, nullptr, nullptr, false)); } bool LSTimer::IsSignaled() { return LSHandle::IsSignaled(); } bool LSTimer::WaitOn(AuUInt32 timeout) { return LSHandle::WaitOn(timeout); } ELoopSource LSTimer::GetType() { return ELoopSource::eSourceTimer; } AuSPtr NewLSTimerOS(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady) { auto handle = CreateWaitableTimerW(nullptr, !bSingleshot, nullptr); if (!handle) { SysPushErrorIO(); return {}; } auto object = AuMakeShared(reschedStepMsOrZero, maxIterationsOrZero, bSingleshot, handle, false); if (!object) { SysPushErrorMem(); AuWin32CloseHandle(handle); return {}; } if (absStartTimeMs) { if (bIsStartTimeSteady) { object->UpdateTimeSteady(absStartTimeMs); } else { object->UpdateTimeWall(absStartTimeMs); } } return object; } AuSPtr NewLSTimerOSHiRes(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, bool bIsStartTimeSteady) { if (!AuSWInfo::IsWindows10MilestoneRS4OrGreater()) { return {}; } if (!pCreateWaitableTimerExW) { return {}; } auto handle = pCreateWaitableTimerExW(nullptr, nullptr, (bSingleshot ? 0 : 1) | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); if (!handle) { SysPushErrorIO(); return {}; } auto object = AuMakeShared(reschedStepMsOrZero, maxIterationsOrZero, bSingleshot, handle, true); if (!object) { SysPushErrorMem(); AuWin32CloseHandle(handle); return {}; } if (absStartTimeMs) { if (bIsStartTimeSteady) { object->UpdateTimeSteady(absStartTimeMs); } else { object->UpdateTimeWall(absStartTimeMs); } } return object; } }