mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 00:31:14 +00:00
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. :^)
This commit is contained in:
parent
23e16a3e2e
commit
abdd5aa08a
Notes:
sideshowbarker
2024-07-19 10:38:24 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/abdd5aa08ae
|
@ -6,6 +6,10 @@
|
|||
#include <Kernel/RTC.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
|
||||
//#define LOG_EVERY_CONTEXT_SWITCH
|
||||
//#define SCHEDULER_DEBUG
|
||||
//#define SCHEDULER_RUNNABLE_DEBUG
|
||||
|
||||
SchedulerData* g_scheduler_data;
|
||||
|
||||
void Scheduler::init_thread(Thread& thread)
|
||||
|
@ -15,7 +19,8 @@ void Scheduler::init_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))
|
||||
return;
|
||||
|
@ -23,16 +28,27 @@ void Scheduler::update_state_for_thread(Thread& 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)
|
||||
{
|
||||
// One time slice unit == 1ms
|
||||
switch (priority) {
|
||||
case ThreadPriority::High:
|
||||
return 50;
|
||||
return 20;
|
||||
case ThreadPriority::Normal:
|
||||
return 15;
|
||||
case ThreadPriority::Low:
|
||||
|
@ -352,36 +368,41 @@ bool Scheduler::pick_next()
|
|||
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
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
@ -450,9 +471,10 @@ bool Scheduler::context_switch(Thread& thread)
|
|||
current->set_state(Thread::Runnable);
|
||||
|
||||
#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(),
|
||||
thread.process().name().characters(), thread.process().pid(), thread.tid(),
|
||||
to_string(thread.priority()),
|
||||
thread.tss().cs, thread.tss().eip);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ enum class ThreadPriority : u8 {
|
|||
Low,
|
||||
Normal,
|
||||
High,
|
||||
First = Idle,
|
||||
First = Low,
|
||||
Last = High,
|
||||
};
|
||||
|
||||
|
@ -501,13 +501,15 @@ const LogStream& operator<<(const LogStream&, const Thread&);
|
|||
struct SchedulerData {
|
||||
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& 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))
|
||||
return m_runnable_threads;
|
||||
return m_runnable_threads[(u8)priority - (u8)ThreadPriority::First];
|
||||
return m_nonrunnable_threads;
|
||||
}
|
||||
};
|
||||
|
@ -516,12 +518,13 @@ template<typename Callback>
|
|||
inline IterationDecision Scheduler::for_each_runnable(Callback callback)
|
||||
{
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue