/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include namespace Kernel { TYPEDEF_DISTINCT_ORDERED_ID(u64, TimerId); class Timer : public RefCounted { friend class TimerQueue; public: void setup(clockid_t clock_id, Time expires, Function&& callback) { VERIFY(!is_queued()); m_clock_id = clock_id; m_expires = expires; m_callback = move(callback); } ~Timer() { VERIFY(!is_queued()); } Time remaining() const; private: TimerId m_id; clockid_t m_clock_id; Time m_expires; Time m_remaining {}; Function m_callback; Atomic m_cancelled { false }; Atomic m_callback_finished { false }; bool operator<(const Timer& rhs) const { return m_expires < rhs.m_expires; } bool operator>(const Timer& rhs) const { return m_expires > rhs.m_expires; } bool operator==(const Timer& rhs) const { return m_id == rhs.m_id; } void clear_cancelled() { return m_cancelled.store(false, AK::memory_order_release); } bool set_cancelled() { return m_cancelled.exchange(true, AK::memory_order_acq_rel); } bool is_callback_finished() const { return m_callback_finished.load(AK::memory_order_acquire); } void clear_callback_finished() { m_callback_finished.store(false, AK::memory_order_release); } void set_callback_finished() { m_callback_finished.store(true, AK::memory_order_release); } Time now(bool) const; bool is_queued() const { return m_list_node.is_in_list(); } public: IntrusiveListNode m_list_node; using List = IntrusiveList, &Timer::m_list_node>; }; class TimerQueue { friend class Timer; public: TimerQueue(); static TimerQueue& the(); TimerId add_timer(NonnullRefPtr&&); bool add_timer_without_id(NonnullRefPtr, clockid_t, const Time&, Function&&); TimerId add_timer(clockid_t, const Time& timeout, Function&& callback); bool cancel_timer(TimerId id); bool cancel_timer(Timer&); bool cancel_timer(NonnullRefPtr&& timer) { return cancel_timer(*move(timer)); } void fire(); private: struct Queue { Timer::List list; Time next_timer_due {}; }; void remove_timer_locked(Queue&, Timer&); void update_next_timer_due(Queue&); void add_timer_locked(NonnullRefPtr); Queue& queue_for_timer(Timer& timer) { switch (timer.m_clock_id) { case CLOCK_MONOTONIC: case CLOCK_MONOTONIC_COARSE: case CLOCK_MONOTONIC_RAW: return m_timer_queue_monotonic; case CLOCK_REALTIME: case CLOCK_REALTIME_COARSE: return m_timer_queue_realtime; default: VERIFY_NOT_REACHED(); } } u64 m_timer_id_count { 0 }; u64 m_ticks_per_second { 0 }; Queue m_timer_queue_monotonic; Queue m_timer_queue_realtime; Timer::List m_timers_executing; }; }