diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index cd65429ea5b..c5a1d51e1f3 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -367,6 +367,7 @@ Process::Process(RefPtr& first_thread, const String& name, uid_t uid, gi Process::~Process() { ASSERT(thread_count() == 0); // all threads should have been finalized + ASSERT(!m_alarm_timer); { ScopedSpinLock processses_lock(g_processes_lock); @@ -596,6 +597,8 @@ void Process::finalize(Thread& last_thread) } } + if (m_alarm_timer) + TimerQueue::the().cancel_timer(m_alarm_timer.release_nonnull()); m_fds.clear(); m_tty = nullptr; m_executable = nullptr; diff --git a/Kernel/Process.h b/Kernel/Process.h index 84a542a184f..94c8181cf67 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -611,7 +611,7 @@ private: Lock m_big_lock { "Process" }; mutable SpinLock m_lock; - u64 m_alarm_deadline { 0 }; + RefPtr m_alarm_timer; int m_icon_id { -1 }; diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 759732b8736..1b06079432b 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -137,15 +137,6 @@ bool Scheduler::pick_next() current_thread->set_state(Thread::Dying); } - Process::for_each([&](Process& process) { - if (process.m_alarm_deadline && TimeManagement::the().uptime_ms() > process.m_alarm_deadline) { - process.m_alarm_deadline = 0; - // FIXME: Should we observe this signal somehow? - (void)process.send_signal(SIGALRM, nullptr); - } - return IterationDecision::Continue; - }); - #ifdef SCHEDULER_RUNNABLE_DEBUG dbg() << "Scheduler[" << Processor::current().id() << "]: Non-runnables:"; Scheduler::for_each_nonrunnable([&](Thread& thread) -> IterationDecision { diff --git a/Kernel/Syscalls/alarm.cpp b/Kernel/Syscalls/alarm.cpp index b7e34352197..2636c41b4fb 100644 --- a/Kernel/Syscalls/alarm.cpp +++ b/Kernel/Syscalls/alarm.cpp @@ -33,15 +33,25 @@ unsigned Process::sys$alarm(unsigned seconds) { REQUIRE_PROMISE(stdio); unsigned previous_alarm_remaining = 0; - auto uptime = TimeManagement::the().uptime_ms(); - if (m_alarm_deadline && m_alarm_deadline > uptime) { - previous_alarm_remaining = m_alarm_deadline - uptime; + if (auto alarm_timer = move(m_alarm_timer)) { + if (TimerQueue::the().cancel_timer(*alarm_timer)) { + // The timer hasn't fired. Round up the remaining time (if any) + timespec remaining; + timespec_add(alarm_timer->remaining(), { 0, 1000000000 - 1 }, remaining); + previous_alarm_remaining = remaining.tv_sec; + } + // We had an existing alarm, must return a non-zero value here! + if (previous_alarm_remaining == 0) + previous_alarm_remaining = 1; } - if (!seconds) { - m_alarm_deadline = 0; - return previous_alarm_remaining; + + if (seconds > 0) { + auto deadline = TimeManagement::the().monotonic_time(); // TODO: should be using CLOCK_REALTIME + timespec_add(deadline, { seconds, 0 }, deadline); + m_alarm_timer = TimerQueue::the().add_timer_without_id(deadline, [this]() { + (void)send_signal(SIGALRM, nullptr); + }); } - m_alarm_deadline = uptime + seconds * 1000; return previous_alarm_remaining; } diff --git a/Kernel/TimerQueue.cpp b/Kernel/TimerQueue.cpp index 3a7bbb9feb2..6c1647b5506 100644 --- a/Kernel/TimerQueue.cpp +++ b/Kernel/TimerQueue.cpp @@ -38,6 +38,13 @@ namespace Kernel { static AK::Singleton s_the; static SpinLock g_timerqueue_lock; +timespec Timer::remaining() const +{ + if (m_remaining == 0) + return {}; + return TimerQueue::the().ticks_to_time(m_remaining); +} + TimerQueue& TimerQueue::the() { return *s_the; @@ -164,14 +171,7 @@ bool TimerQueue::cancel_timer(TimerId id) } ASSERT(found_timer); - bool was_next_timer = (m_timer_queue.head() == found_timer); - - m_timer_queue.remove(found_timer); - found_timer->set_queued(false); - - if (was_next_timer) - update_next_timer_due(); - + remove_timer_locked(*found_timer); lock.unlock(); found_timer->unref(); return true; @@ -199,13 +199,21 @@ bool TimerQueue::cancel_timer(Timer& timer) return false; } + remove_timer_locked(timer); + return true; +} + +void TimerQueue::remove_timer_locked(Timer& timer) +{ bool was_next_timer = (m_timer_queue.head() == &timer); m_timer_queue.remove(&timer); timer.set_queued(false); + auto now = TimeManagement::the().monotonic_ticks(); + if (timer.m_expires > now) + timer.m_remaining = timer.m_expires - now; if (was_next_timer) update_next_timer_due(); - return true; } void TimerQueue::fire() diff --git a/Kernel/TimerQueue.h b/Kernel/TimerQueue.h index a6c3ae33564..ffeda2fedc4 100644 --- a/Kernel/TimerQueue.h +++ b/Kernel/TimerQueue.h @@ -53,9 +53,12 @@ public: ASSERT(!is_queued()); } + timespec remaining() const; + private: TimerId m_id; u64 m_expires; + u64 m_remaining { 0 }; Function m_callback; Timer* m_next { nullptr }; Timer* m_prev { nullptr }; @@ -88,6 +91,7 @@ public: RefPtr add_timer_without_id(const timespec& timeout, Function&& callback); TimerId add_timer(timeval& timeout, Function&& callback); bool cancel_timer(TimerId id); + bool cancel_timer(Timer&); bool cancel_timer(NonnullRefPtr&& timer) { return cancel_timer(timer.leak_ref()); @@ -95,7 +99,7 @@ public: void fire(); private: - bool cancel_timer(Timer&); + void remove_timer_locked(Timer&); void update_next_timer_due(); void add_timer_locked(NonnullRefPtr);