/*** 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::Loop { struct LSTimer : ITimer, LSHandle { LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, HANDLE handle); ~LSTimer(); virtual void UpdateTime(AuUInt64 absTimeMs) override; virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) override; virtual void Stop() override; virtual bool OnTrigger(AuUInt handle) override; virtual bool IsSignaled() override; virtual bool WaitOn(AuUInt32 timeout) override; virtual ELoopSource GetType() override; void UpdateTimeInternal(AuUInt64 absTimeMs); private: AuUInt32 reschedStepMsOrZero_ {}, maxIterationsOrZero_ {}; AuUInt64 targetTime_ {}; AuUInt32 count_ {}; }; LSTimer::LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, HANDLE handle) : LSHandle(AuReinterpretCast(handle)), reschedStepMsOrZero_(reschedStepMsOrZero), maxIterationsOrZero_(maxIterationsOrZero) { this->targetTime_ = AuTime::ConvertTimestamp(AuTime::CurrentClockMS()); } LSTimer::~LSTimer() { auto handle = AuReinterpretCast(this->handle); AuWin32CloseHandle(handle); this->handle = kInvalidHandle; } void LSTimer::UpdateTime(AuUInt64 absTimeMs) { this->targetTime_ = AuTime::ConvertTimestamp(absTimeMs); UpdateTimeInternal(this->targetTime_); } void LSTimer::UpdateTimeInternal(AuUInt64 absTimeMs) { this->targetTime_ = absTimeMs; LARGE_INTEGER i; i.QuadPart = absTimeMs; SysAssert(::SetWaitableTimer(AuReinterpretCast(this->handle), &i, 0, nullptr, nullptr, false)); } void LSTimer::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) { this->reschedStepMsOrZero_ = reschedStepMsOrZero; this->maxIterationsOrZero_ = maxIterationsOrZero; this->count_ = 0; this->UpdateTime(this->targetTime_); } bool LSTimer::OnTrigger(AuUInt handle) { SysAssert(this->targetTime_ <= AuTime::ConvertTimestamp(AuTime::CurrentClockMS())); if (!this->reschedStepMsOrZero_) { return true; } if (!this->maxIterationsOrZero_) { this->UpdateTimeInternal(this->targetTime_ + (AuUInt64(this->reschedStepMsOrZero_) * 10'000ULL)); return true; } bool ok = AuAtomicAdd(&this->count_, 1) < this->maxIterationsOrZero_; if (ok) { this->UpdateTimeInternal(this->targetTime_ + (AuUInt64(this->reschedStepMsOrZero_) * 10'000ULL)); 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; } AUKN_SYM AuSPtr NewLSTimer(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) { // https://docs.microsoft.com/en-us/windows/win32/api/threadpoolapiset/nf-threadpoolapiset-setthreadpoolwait // TODO: Is that any better with an event? auto handle = CreateWaitableTimerW(nullptr, false, nullptr); if (!handle) { SysPushErrorIO(); return {}; } auto object = AuMakeShared(reschedStepMsOrZero, maxIterationsOrZero, handle); if (!object) { SysPushErrorMem(); AuWin32CloseHandle(handle); return {}; } object->UpdateTime(absStartTimeMs); return object; } }