Luna/libos/src/Timer.cpp
apio d4d748e153
terminal: Remove m_cursor_timer->restart() from tick_cursor()
Since the timer was created as a repeating timer, it is already restarted after the function.
2024-01-13 16:26:37 +01:00

116 lines
3.1 KiB
C++

/**
* @file Timer.cpp
* @author apio (cloudapio.eu)
* @brief Millisecond-precision timer.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#include <errno.h>
#include <os/EventLoop.h>
#include <os/Timer.h>
namespace os
{
Result<OwnedPtr<Timer>> Timer::create_singleshot(unsigned int milliseconds, Action&& action)
{
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
if (status < 0) return err(errno);
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
timer->m_repeating = false;
timer->m_action = move(action);
EventLoop::the().m_timer_list.append(timer.ptr());
return timer;
}
Result<OwnedPtr<Timer>> Timer::create_repeating(unsigned int milliseconds, Action&& action)
{
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
if (status < 0) return err(errno);
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
timer->m_repeating = true;
timer->m_interval = time;
timer->m_action = move(action);
EventLoop::the().m_timer_list.append(timer.ptr());
return timer;
}
void Timer::restart()
{
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = m_interval };
timer_settime(m_timerid, 0, &itimer, nullptr);
readd_if_necessary();
}
void Timer::reset(unsigned int milliseconds)
{
m_interval = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
restart();
}
void Timer::stop()
{
EventLoop::the().m_timer_list.remove(this);
m_stopped = true;
struct itimerspec itimer;
memset(&itimer, 0, sizeof(itimer));
timer_settime(m_timerid, 0, &itimer, nullptr);
}
void Timer::check_if_should_invoke_action()
{
struct itimerspec remaining;
if (timer_gettime(m_timerid, &remaining) < 0) return;
if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec && !m_stopped)
{
// Timer expired!
m_action();
if (!m_repeating)
{
EventLoop::the().m_timer_list.remove(this);
m_stopped = true;
}
else
restart();
}
}
void Timer::readd_if_necessary()
{
if (m_stopped)
{
m_stopped = false;
EventLoop::the().m_timer_list.append(this);
}
}
Timer::~Timer()
{
if (m_timerid >= 0) timer_delete(m_timerid);
}
}