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 #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 0000000000..783d1a6cdf --- /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 0000000000..34dfafabf9 --- /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; +};