/** * @file Timer.cpp * @author apio (cloudapio.eu) * @brief Millisecond-precision timer. * * @copyright Copyright (c) 2024, the Luna authors. * */ #include #include #include namespace os { Result> 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> 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); } }