summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Pankoff <deoxxa@fknsrs.biz>2019-12-27 11:58:28 +1100
committerAndreas Kling <awesomekling@gmail.com>2019-12-27 02:15:45 +0100
commit115b3153757f5a73eef555d22b20fa2e6a5ac371 (patch)
tree47998b256ffe404e02dfea2398347c5599bede72
parent13cf7e76b929fd99b05be0b3d0170d398537fa3c (diff)
downloadserenity-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/Makefile1
-rw-r--r--Kernel/Scheduler.cpp3
-rw-r--r--Kernel/TimerQueue.cpp72
-rw-r--r--Kernel/TimerQueue.h48
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;
+};