/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSTimer.Linux.cpp Date: 2021-10-1 Author: Reece ***/ #include #include "LSTimer.hpp" #include "LSFromFdNonblocking.hpp" #include #include namespace Aurora::IO::Loop { LSTimer::LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, int handle) : LSHandle(handle), bSingleshot(bSingleshot), reschedStepNsOrZero_(AuMSToNS(reschedStepMsOrZero)), maxIterationsOrZero_(maxIterationsOrZero) { this->targetTime_ = AuTime::CurrentClockNS(); } LSTimer::~LSTimer() { if ((this->handle != 0) && (this->handle != -1)) { ::close(AuExchange(this->handle, -1)); } } void LSTimer::UpdateTime(AuUInt64 absTimeMs) { this->targetTime_ = AuMSToNS(absTimeMs); UpdateTimeInternal(this->targetTime_); } void LSTimer::UpdateTimeNs(AuUInt64 absTimeNs) { this->targetTime_ = absTimeNs; UpdateTimeInternal(this->targetTime_); } void LSTimer::UpdateTimeInternal(AuUInt64 absTimeNs) { this->targetTime_ = absTimeNs; itimerspec spec {}; AuTime::auabsns2ts(&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::UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero) { this->reschedStepNsOrZero_ = AuMSToNS(reschedStepMsOrZero); this->maxIterationsOrZero_ = maxIterationsOrZero; this->count_ = 0; this->UpdateTimeInternal(this->targetTime_); } void LSTimer::UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero, AuUInt32 maxIterationsOrZero) { this->reschedStepNsOrZero_ = reschedStepNsOrZero; this->maxIterationsOrZero_ = maxIterationsOrZero; this->count_ = 0; this->UpdateTimeInternal(this->targetTime_); } bool LSTimer::OnTrigger(AuUInt handle) { return IsSignaledNonblocking(); } void LSTimer::Stop() { itimerspec spec {}; SysAssert(::timerfd_settime(this->handle, 0, &spec, nullptr) == 0); } bool LSTimer::IsSignaled() { return IsSignaledFromNonblockingImpl(this, this, &LSTimer::IsSignaledNonblocking); } bool LSTimer::WaitOn(AuUInt32 timeout) { return LSHandle::WaitOn(timeout); } ELoopSource LSTimer::GetType() { return ELoopSource::eSourceTimer; } bool LSTimer::IsSignaledNonblocking() { AuUInt64 oldSemaphoreValue {}; auto ok = ::read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8; if (ok && this->maxIterationsOrZero_) { ok = AuAtomicAdd(&this->count_, 1) <= this->maxIterationsOrZero_; if (ok) { // fall through } else { Stop(); return false; } } if (ok && !this->reschedStepNsOrZero_ && !bSingleshot) { // Ensure non-step timers remain signaled despite the read above us caneling them this->UpdateTimeInternal(this->targetTime_); } return ok; } AUKN_SYM AuSPtr NewLSTimer(AuUInt64 absStartTimeMs, AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot) { int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); if (fd == -1) { SysPushErrorIO(); return {}; } auto object = AuMakeShared(reschedStepMsOrZero, maxIterationsOrZero, bSingleshot, fd); if (!object) { SysPushErrorMem(); ::close(fd); return {}; } if (absStartTimeMs) { object->UpdateTime(absStartTimeMs); } return object; } }