149 lines
4.1 KiB
C++
149 lines
4.1 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: LSTimer.Linux.cpp
|
|
Date: 2021-10-1
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "LSTimer.hpp"
|
|
#include "LSFromFdNonblocking.hpp"
|
|
#include <Source/Time/Time.hpp>
|
|
#include <sys/timerfd.h>
|
|
|
|
namespace Aurora::IO::Loop
|
|
{
|
|
LSTimer::LSTimer(AuUInt32 reschedStepMsOrZero, AuUInt32 maxIterationsOrZero, bool bSingleshot, int handle) :
|
|
LSHandle(handle),
|
|
bSingleshot(bSingleshot),
|
|
reschedStepNsOrZero_(AuMSToNS<AuUInt64>(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<AuUInt64>(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<AuUInt64>(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<AuUInt32>(&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<ITimer> NewLSTimer(AuUInt64 qwAbsStartTimeMs, AuUInt32 dwReschedStepMsOrZero, AuUInt32 dwMaxIterationsOrZero, bool bSingleshot)
|
|
{
|
|
int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
if (fd == -1)
|
|
{
|
|
SysPushErrorIO();
|
|
return {};
|
|
}
|
|
|
|
auto pTimer = AuMakeShared<LSTimer>(dwReschedStepMsOrZero, dwMaxIterationsOrZero, bSingleshot, fd);
|
|
if (!pTimer)
|
|
{
|
|
SysPushErrorMem();
|
|
::close(fd);
|
|
return {};
|
|
}
|
|
|
|
if (qwAbsStartTimeMs)
|
|
{
|
|
pTimer->UpdateTime(qwAbsStartTimeMs);
|
|
}
|
|
|
|
return pTimer;
|
|
}
|
|
} |