diff options
author | Conrad Pankoff <deoxxa@fknsrs.biz> | 2019-12-27 11:58:28 +1100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-12-27 02:15:45 +0100 |
commit | 115b3153757f5a73eef555d22b20fa2e6a5ac371 (patch) | |
tree | 47998b256ffe404e02dfea2398347c5599bede72 | |
parent | 13cf7e76b929fd99b05be0b3d0170d398537fa3c (diff) | |
download | serenity-115b3153757f5a73eef555d22b20fa2e6a5ac371.zip |
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.
-rw-r--r-- | Kernel/Makefile | 1 | ||||
-rw-r--r-- | Kernel/Scheduler.cpp | 3 | ||||
-rw-r--r-- | Kernel/TimerQueue.cpp | 72 | ||||
-rw-r--r-- | Kernel/TimerQueue.h | 48 |
4 files changed, 124 insertions, 0 deletions
diff --git a/Kernel/Makefile b/Kernel/Makefile index 6fd0dd6808..e031905737 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 80ae106f32..febb8a1f19 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -5,6 +5,7 @@ #include <Kernel/Profiling.h> #include <Kernel/RTC.h> #include <Kernel/Scheduler.h> +#include <Kernel/TimerQueue.h> //#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 0000000000..783d1a6cdf --- /dev/null +++ b/Kernel/TimerQueue.cpp @@ -0,0 +1,72 @@ +#include <AK/Function.h> +#include <AK/NonnullOwnPtr.h> +#include <AK/OwnPtr.h> +#include <Kernel/Scheduler.h> +#include <Kernel/TimerQueue.h> + +static TimerQueue* s_the; + +TimerQueue& TimerQueue::the() +{ + if (!s_the) + s_the = new TimerQueue; + return *s_the; +} + +u64 TimerQueue::add_timer(NonnullOwnPtr<Timer>&& 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<void()>&& callback) +{ + NonnullOwnPtr timer = make<Timer>(); + 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 0000000000..34dfafabf9 --- /dev/null +++ b/Kernel/TimerQueue.h @@ -0,0 +1,48 @@ +#pragma once + +#include <AK/Function.h> +#include <AK/NonnullOwnPtr.h> +#include <AK/OwnPtr.h> +#include <AK/SinglyLinkedList.h> +#include <Kernel/Arch/i386/PIT.h> + +struct Timer { + u64 id; + u64 expires; + Function<void()> 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<Timer>&&); + u64 add_timer(u64 duration, TimeUnit, Function<void()>&& 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<NonnullOwnPtr<Timer>> m_timer_queue; +}; |