浏览代码

Kernel: Separate runnable thread queues by priority

This patch introduces three separate thread queues, one for each thread
priority available to userspace (Low, Normal and High.)

Each queue operates in a round-robin fashion, but we now always prefer
to schedule the highest priority thread that currently wants to run.

There are tons of tweaks and improvements that we can and should make
to this mechanism, but I think this is a step in the right direction.

This makes WindowServer significantly more responsive while one of its
clients is burning CPU. :^)
Andreas Kling 5 年之前
父节点
当前提交
abdd5aa08a
共有 2 个文件被更改,包括 62 次插入37 次删除
  1. 49 27
      Kernel/Scheduler.cpp
  2. 13 10
      Kernel/Thread.h

+ 49 - 27
Kernel/Scheduler.cpp

@@ -6,6 +6,10 @@
 #include <Kernel/RTC.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Scheduler.h>
 
 
+//#define LOG_EVERY_CONTEXT_SWITCH
+//#define SCHEDULER_DEBUG
+//#define SCHEDULER_RUNNABLE_DEBUG
+
 SchedulerData* g_scheduler_data;
 SchedulerData* g_scheduler_data;
 
 
 void Scheduler::init_thread(Thread& thread)
 void Scheduler::init_thread(Thread& thread)
@@ -15,7 +19,8 @@ void Scheduler::init_thread(Thread& thread)
 
 
 void Scheduler::update_state_for_thread(Thread& thread)
 void Scheduler::update_state_for_thread(Thread& thread)
 {
 {
-    auto& list = g_scheduler_data->thread_list_for_state(thread.state());
+    ASSERT_INTERRUPTS_DISABLED();
+    auto& list = g_scheduler_data->thread_list_for_state_and_priority(thread.state(), thread.priority());
 
 
     if (list.contains(thread))
     if (list.contains(thread))
         return;
         return;
@@ -23,16 +28,27 @@ void Scheduler::update_state_for_thread(Thread& thread)
     list.append(thread);
     list.append(thread);
 }
 }
 
 
-//#define LOG_EVERY_CONTEXT_SWITCH
-//#define SCHEDULER_DEBUG
-//#define SCHEDULER_RUNNABLE_DEBUG
+template<typename Callback>
+static inline IterationDecision for_each_runnable_with_priority(ThreadPriority priority, Callback callback)
+{
+    ASSERT_INTERRUPTS_DISABLED();
+    auto& tl = g_scheduler_data->m_runnable_threads[(u8)priority - (u8)ThreadPriority::First];
+    for (auto it = tl.begin(); it != tl.end();) {
+        auto& thread = *it;
+        it = ++it;
+        if (callback(thread) == IterationDecision::Break)
+            return IterationDecision::Break;
+    }
+
+    return IterationDecision::Continue;
+}
 
 
 static u32 time_slice_for(ThreadPriority priority)
 static u32 time_slice_for(ThreadPriority priority)
 {
 {
     // One time slice unit == 1ms
     // One time slice unit == 1ms
     switch (priority) {
     switch (priority) {
     case ThreadPriority::High:
     case ThreadPriority::High:
-        return 50;
+        return 20;
     case ThreadPriority::Normal:
     case ThreadPriority::Normal:
         return 15;
         return 15;
     case ThreadPriority::Low:
     case ThreadPriority::Low:
@@ -352,36 +368,41 @@ bool Scheduler::pick_next()
         return IterationDecision::Continue;
         return IterationDecision::Continue;
     });
     });
 
 
-    dbgprintf("Runnables:\n");
-    Scheduler::for_each_runnable([](Thread& thread) -> IterationDecision {
-        auto& process = thread.process();
-        dbgprintf("[K%x] %-12s %s(%u:%u) @ %w:%x\n", &process, thread.state_string(), process.name().characters(), process.pid(), thread.tid(), thread.tss().cs, thread.tss().eip);
-        return IterationDecision::Continue;
-    });
+    for (u8 priority = (u8)ThreadPriority::Last; priority >= (u8)ThreadPriority::First; --priority) {
+        dbgprintf("Runnables (%s):\n", to_string((ThreadPriority)priority));
+        for_each_runnable_with_priority((ThreadPriority)priority, [](Thread& thread) -> IterationDecision {
+            auto& process = thread.process();
+            dbgprintf("[K%x] %-12s %s(%u:%u) @ %w:%x\n", &process, thread.state_string(), process.name().characters(), process.pid(), thread.tid(), thread.tss().cs, thread.tss().eip);
+            return IterationDecision::Continue;
+        });
+    }
 #endif
 #endif
 
 
-    auto& runnable_list = g_scheduler_data->m_runnable_threads;
-    if (runnable_list.is_empty())
-        return context_switch(*g_colonel);
+    for (u8 priority = (u8)ThreadPriority::Last; priority >= (u8)ThreadPriority::First; --priority) {
+        auto& runnable_list = g_scheduler_data->m_runnable_threads[priority - (u8)ThreadPriority::First];
+        if (runnable_list.is_empty())
+            continue;
 
 
-    auto* previous_head = runnable_list.first();
-    for (;;) {
-        // Move head to tail.
-        runnable_list.append(*runnable_list.first());
-        auto* thread = runnable_list.first();
+        auto* previous_head = runnable_list.first();
+        for (;;) {
+            // Move head to tail.
+            runnable_list.append(*runnable_list.first());
+            auto* thread = runnable_list.first();
 
 
-        if (!thread->process().is_being_inspected() && (thread->state() == Thread::Runnable || thread->state() == Thread::Running)) {
+            if (!thread->process().is_being_inspected() && (thread->state() == Thread::Runnable || thread->state() == Thread::Running)) {
 #ifdef SCHEDULER_DEBUG
 #ifdef SCHEDULER_DEBUG
-            dbgprintf("switch to %s(%u:%u) @ %w:%x\n", thread->process().name().characters(), thread->process().pid(), thread->tid(), thread->tss().cs, thread->tss().eip);
+                dbgprintf("switch to %s(%u:%u) @ %w:%x\n", thread->process().name().characters(), thread->process().pid(), thread->tid(), thread->tss().cs, thread->tss().eip);
 #endif
 #endif
-            return context_switch(*thread);
-        }
+                return context_switch(*thread);
+            }
 
 
-        if (thread == previous_head) {
-            // Back at process_head, nothing wants to run. Send in the colonel!
-            return context_switch(*g_colonel);
+            if (thread == previous_head)
+                break;
         }
         }
     }
     }
+
+    // Nothing wants to run. Send in the colonel!
+    return context_switch(*g_colonel);
 }
 }
 
 
 bool Scheduler::donate_to(Thread* beneficiary, const char* reason)
 bool Scheduler::donate_to(Thread* beneficiary, const char* reason)
@@ -450,9 +471,10 @@ bool Scheduler::context_switch(Thread& thread)
             current->set_state(Thread::Runnable);
             current->set_state(Thread::Runnable);
 
 
 #ifdef LOG_EVERY_CONTEXT_SWITCH
 #ifdef LOG_EVERY_CONTEXT_SWITCH
-        dbgprintf("Scheduler: %s(%u:%u) -> %s(%u:%u) %w:%x\n",
+        dbgprintf("Scheduler: %s(%u:%u) -> %s(%u:%u) [%s] %w:%x\n",
             current->process().name().characters(), current->process().pid(), current->tid(),
             current->process().name().characters(), current->process().pid(), current->tid(),
             thread.process().name().characters(), thread.process().pid(), thread.tid(),
             thread.process().name().characters(), thread.process().pid(), thread.tid(),
+            to_string(thread.priority()),
             thread.tss().cs, thread.tss().eip);
             thread.tss().cs, thread.tss().eip);
 #endif
 #endif
     }
     }

+ 13 - 10
Kernel/Thread.h

@@ -40,7 +40,7 @@ enum class ThreadPriority : u8 {
     Low,
     Low,
     Normal,
     Normal,
     High,
     High,
-    First = Idle,
+    First = Low,
     Last = High,
     Last = High,
 };
 };
 
 
@@ -501,13 +501,15 @@ const LogStream& operator<<(const LogStream&, const Thread&);
 struct SchedulerData {
 struct SchedulerData {
     typedef IntrusiveList<Thread, &Thread::m_runnable_list_node> ThreadList;
     typedef IntrusiveList<Thread, &Thread::m_runnable_list_node> ThreadList;
 
 
-    ThreadList m_runnable_threads;
+    static constexpr size_t num_thread_priorities = (size_t)ThreadPriority::Last - (size_t)ThreadPriority::First + 1;
+    ThreadList m_runnable_threads[num_thread_priorities];
+
     ThreadList m_nonrunnable_threads;
     ThreadList m_nonrunnable_threads;
 
 
-    ThreadList& thread_list_for_state(Thread::State state)
+    ThreadList& thread_list_for_state_and_priority(Thread::State state, ThreadPriority priority)
     {
     {
         if (Thread::is_runnable_state(state))
         if (Thread::is_runnable_state(state))
-            return m_runnable_threads;
+            return m_runnable_threads[(u8)priority - (u8)ThreadPriority::First];
         return m_nonrunnable_threads;
         return m_nonrunnable_threads;
     }
     }
 };
 };
@@ -516,12 +518,13 @@ template<typename Callback>
 inline IterationDecision Scheduler::for_each_runnable(Callback callback)
 inline IterationDecision Scheduler::for_each_runnable(Callback callback)
 {
 {
     ASSERT_INTERRUPTS_DISABLED();
     ASSERT_INTERRUPTS_DISABLED();
-    auto& tl = g_scheduler_data->m_runnable_threads;
-    for (auto it = tl.begin(); it != tl.end();) {
-        auto& thread = *it;
-        it = ++it;
-        if (callback(thread) == IterationDecision::Break)
-            return IterationDecision::Break;
+    for (auto& tl : g_scheduler_data->m_runnable_threads) {
+        for (auto it = tl.begin(); it != tl.end();) {
+            auto& thread = *it;
+            it = ++it;
+            if (callback(thread) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
     }
     }
 
 
     return IterationDecision::Continue;
     return IterationDecision::Continue;