Przeglądaj źródła

Kernel: Use TimerQueue for SIGALRM

Tom 4 lat temu
rodzic
commit
4c1e27ec65

+ 3 - 0
Kernel/Process.cpp

@@ -367,6 +367,7 @@ Process::Process(RefPtr<Thread>& 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;

+ 1 - 1
Kernel/Process.h

@@ -611,7 +611,7 @@ private:
     Lock m_big_lock { "Process" };
     mutable SpinLock<u32> m_lock;
 
-    u64 m_alarm_deadline { 0 };
+    RefPtr<Timer> m_alarm_timer;
 
     int m_icon_id { -1 };
 

+ 0 - 9
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 {

+ 17 - 7
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;
 }
 

+ 17 - 9
Kernel/TimerQueue.cpp

@@ -38,6 +38,13 @@ namespace Kernel {
 static AK::Singleton<TimerQueue> s_the;
 static SpinLock<u8> 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()

+ 5 - 1
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<void()> m_callback;
     Timer* m_next { nullptr };
     Timer* m_prev { nullptr };
@@ -88,6 +91,7 @@ public:
     RefPtr<Timer> add_timer_without_id(const timespec& timeout, Function<void()>&& callback);
     TimerId add_timer(timeval& timeout, Function<void()>&& callback);
     bool cancel_timer(TimerId id);
+    bool cancel_timer(Timer&);
     bool cancel_timer(NonnullRefPtr<Timer>&& 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<Timer>);