From 115b3153757f5a73eef555d22b20fa2e6a5ac371 Mon Sep 17 00:00:00 2001 From: Conrad Pankoff Date: Fri, 27 Dec 2019 11:58:28 +1100 Subject: [PATCH] Kernel: Add kernel-level timer queue (heavily based on @juliusf's work) PR #591 defines the rationale for kernel-level timers. They're most immediately useful for TCP retransmission, but will most likely see use in many other areas as well. --- Kernel/Makefile | 1 + Kernel/Scheduler.cpp | 3 ++ Kernel/TimerQueue.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++ Kernel/TimerQueue.h | 48 +++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 Kernel/TimerQueue.cpp create mode 100644 Kernel/TimerQueue.h diff --git a/Kernel/Makefile b/Kernel/Makefile index 6fd0dd68084..e0319057370 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -80,6 +80,7 @@ OBJS = \ SharedBuffer.o \ StdLib.o \ Syscall.o \ + TimerQueue.o \ TTY/MasterPTY.o \ TTY/PTYMultiplexer.o \ TTY/SlavePTY.o \ diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 80ae106f32f..febb8a1f19a 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -5,6 +5,7 @@ #include #include #include +#include //#define LOG_EVERY_CONTEXT_SWITCH //#define SCHEDULER_DEBUG @@ -579,6 +580,8 @@ void Scheduler::timer_tick(RegisterDump& regs) } } + TimerQueue::the().fire(); + if (current->tick()) return; diff --git a/Kernel/TimerQueue.cpp b/Kernel/TimerQueue.cpp new file mode 100644 index 00000000000..783d1a6cdfc --- /dev/null +++ b/Kernel/TimerQueue.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +static TimerQueue* s_the; + +TimerQueue& TimerQueue::the() +{ + if (!s_the) + s_the = new TimerQueue; + return *s_the; +} + +u64 TimerQueue::add_timer(NonnullOwnPtr&& timer) +{ + ASSERT(timer->expires > g_uptime); + + timer->id = ++m_timer_id_count; + + auto following_timer = m_timer_queue.find([&timer](auto& other) { return other->expires > timer->expires; }); + if (following_timer.is_end()) + m_timer_queue.append(move(timer)); + else + m_timer_queue.insert_before(following_timer, move(timer)); + + update_next_timer_due(); + + return m_timer_id_count; +} + +u64 TimerQueue::add_timer(u64 duration, TimeUnit unit, Function&& callback) +{ + NonnullOwnPtr timer = make(); + timer->expires = g_uptime + duration * unit; + timer->callback = move(callback); + return add_timer(move(timer)); +} + +bool TimerQueue::cancel_timer(u64 id) +{ + auto it = m_timer_queue.find([id](auto& timer) { return timer->id == id; }); + if (it.is_end()) + return false; + m_timer_queue.remove(it); + update_next_timer_due(); + return true; +} + +void TimerQueue::fire() +{ + if (m_timer_queue.is_empty()) + return; + + ASSERT(m_next_timer_due == m_timer_queue.first()->expires); + + while (!m_timer_queue.is_empty() && g_uptime > m_timer_queue.first()->expires) { + auto timer = m_timer_queue.take_first(); + timer->callback(); + } + + update_next_timer_due(); +} + +void TimerQueue::update_next_timer_due() +{ + if (m_timer_queue.is_empty()) + m_next_timer_due = 0; + else + m_next_timer_due = m_timer_queue.first()->expires; +} diff --git a/Kernel/TimerQueue.h b/Kernel/TimerQueue.h new file mode 100644 index 00000000000..34dfafabf93 --- /dev/null +++ b/Kernel/TimerQueue.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct Timer { + u64 id; + u64 expires; + Function callback; + bool operator<(const Timer& rhs) const + { + return expires < rhs.expires; + } + bool operator>(const Timer& rhs) const + { + return expires > rhs.expires; + } + bool operator==(const Timer& rhs) const + { + return id == rhs.id; + } +}; + +enum TimeUnit { + MS = TICKS_PER_SECOND / 1000, + S = TICKS_PER_SECOND, + M = TICKS_PER_SECOND * 60 +}; + +class TimerQueue { +public: + static TimerQueue& the(); + + u64 add_timer(NonnullOwnPtr&&); + u64 add_timer(u64 duration, TimeUnit, Function&& callback); + bool cancel_timer(u64 id); + void fire(); + +private: + void update_next_timer_due(); + + u64 m_next_timer_due { 0 }; + u64 m_timer_id_count { 0 }; + SinglyLinkedList> m_timer_queue; +};