diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b9c3ca7a..30db8c18 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES src/thread/Thread.cpp src/thread/ThreadImage.cpp src/thread/Scheduler.cpp + src/thread/Timer.cpp src/sys/Syscall.cpp src/sys/exit.cpp src/sys/clock_gettime.cpp diff --git a/kernel/src/sys/alarm.cpp b/kernel/src/sys/alarm.cpp index a0fe0e4e..83f86b66 100644 --- a/kernel/src/sys/alarm.cpp +++ b/kernel/src/sys/alarm.cpp @@ -1,6 +1,7 @@ #include "Pledge.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include "thread/Timer.h" Result sys_alarm(Registers*, SyscallArgs args) { @@ -10,9 +11,16 @@ Result sys_alarm(Registers*, SyscallArgs args) TRY(check_pledge(current, Promise::p_stdio)); - u64 time_left = current->alarm_ticks_left; + u64 ticks_left = current->timer ? current->timer->ticks_left() : 0; - current->alarm_ticks_left = seconds * 1000; + if (current->timer) Scheduler::remove_from_timer_queue(current->timer); + else + current->timer = TRY(make()); - return time_left * 1000; + current->timer->total_ticks = seconds * 1000; + current->timer->thread = current; + + Scheduler::add_to_timer_queue(current->timer); + + return ticks_left * 1000; } diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index af866997..5d166cd0 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -5,6 +5,7 @@ #include "binfmt/ELF.h" #include "memory/MemoryManager.h" #include "thread/ThreadImage.h" +#include "thread/Timer.h" #include #include #include @@ -216,6 +217,12 @@ namespace Scheduler MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value(); } + if (thread->timer) + { + Scheduler::remove_from_timer_queue(thread->timer); + delete thread->timer; + } + delete thread; CPU::enable_interrupts(); @@ -293,6 +300,8 @@ namespace Scheduler { CPU::disable_interrupts(); + tick_queue(); + if (is_in_kernel(regs)) g_current->kernel_ticks_self++; else g_current->user_ticks_self++; @@ -305,8 +314,6 @@ namespace Scheduler { if (thread->sleep_ticks_left == 0 || --thread->sleep_ticks_left == 0) thread->wake_up(); } - - if (thread->alarm_ticks_left && --thread->alarm_ticks_left == 0) thread->send_signal(SIGALRM); } if (!g_current->ticks_left) switch_task(regs); diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index a46ea73d..eefb33fc 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -19,6 +19,11 @@ #error "Unknown architecture." #endif +namespace Scheduler +{ + class Timer; +} + enum class ThreadState { None, @@ -60,7 +65,6 @@ struct Thread : public LinkedListNode u64 ticks_left; u64 sleep_ticks_left; - u64 alarm_ticks_left { 0 }; int promises { -1 }; int execpromises { -1 }; @@ -97,6 +101,8 @@ struct Thread : public LinkedListNode mode_t umask { 0 }; + Scheduler::Timer* timer { nullptr }; + StaticString<128> cmdline; String current_directory_path = {}; diff --git a/kernel/src/thread/Timer.cpp b/kernel/src/thread/Timer.cpp new file mode 100644 index 00000000..dc8c94d1 --- /dev/null +++ b/kernel/src/thread/Timer.cpp @@ -0,0 +1,72 @@ +#include "thread/Timer.h" +#include "Log.h" + +static LinkedList g_timer_queue; + +namespace Scheduler +{ + void add_to_timer_queue(Timer* timer) + { + timer->delta_ticks = timer->total_ticks; + + for (auto* t : g_timer_queue) + { + if (timer->delta_ticks <= t->delta_ticks) + { + t->delta_ticks -= timer->delta_ticks; + g_timer_queue.add_before(t, timer); + return; + } + timer->delta_ticks -= t->delta_ticks; + } + + g_timer_queue.append(timer); + } + + void remove_from_timer_queue(Timer* timer) + { + auto maybe_next = g_timer_queue.next(timer); + if (maybe_next.has_value()) + { + auto next = maybe_next.value(); + next->delta_ticks += timer->delta_ticks; + } + g_timer_queue.remove(timer); + } + + void tick_queue() + { + auto maybe_first = g_timer_queue.first(); + if (!maybe_first.has_value()) return; + + auto first = *maybe_first; + first->delta_ticks--; + + LinkedList timers_to_be_restarted; + + g_timer_queue.delayed_for_each([&](Timer* t) { + if (t->delta_ticks == 0) + { + g_timer_queue.remove(t); + t->thread->send_signal(t->signo); + if (t->restart) timers_to_be_restarted.append(t); + return true; + } + + return false; + }); + + timers_to_be_restarted.consume([](Timer* t) { add_to_timer_queue(t); }); + } + + u64 Timer::ticks_left() + { + u64 total = 0; + for (auto* t : g_timer_queue) + { + total += t->delta_ticks; + if (t == this) break; + } + return total; + } +} diff --git a/kernel/src/thread/Timer.h b/kernel/src/thread/Timer.h new file mode 100644 index 00000000..51419741 --- /dev/null +++ b/kernel/src/thread/Timer.h @@ -0,0 +1,23 @@ +#pragma once +#include "thread/Thread.h" +#include + +namespace Scheduler +{ + class Timer : public LinkedListNode + { + public: + u64 delta_ticks; + u64 total_ticks; + Thread* thread; + int signo { SIGALRM }; + bool restart { false }; + + u64 ticks_left(); + }; + + void add_to_timer_queue(Timer* timer); + void remove_from_timer_queue(Timer* timer); + + void tick_queue(); +}