From dc86ee0f7c566084c8e5412e2a1c5829db00416e Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 14 Jan 2024 21:51:31 +0100 Subject: [PATCH] Schedule interactive threads fairly under load. Preempted threads are now removed from the runnable list until every other thread has been preempted or the system goes idle. This ensures threads that roundtrip to other threads get a full chance to perform their work cooperatively without being starved by CPU intensive threads whenever they yield. --- kernel/scheduler.cpp | 60 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/kernel/scheduler.cpp b/kernel/scheduler.cpp index 06a4ec61..2542e94b 100644 --- a/kernel/scheduler.cpp +++ b/kernel/scheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2015, 2021-2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2015, 2021-2022, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * scheduler.cpp - * Decides the order to execute threads in and switching between them. + * Decides the order to execute threads in and switches between them. */ #include @@ -267,6 +267,7 @@ static void SwitchRegisters(struct interrupt_context* intctx, static Thread* idle_thread; static Thread* first_runnable_thread; +static Thread* first_preempted_thread; static Thread* true_current_thread; static Process* init_process; @@ -323,15 +324,23 @@ static Thread* PopNextThread(bool yielded) return result; } + // Switch to the next runnable thread that hasn't been preempted. if ( first_runnable_thread ) { result = first_runnable_thread; first_runnable_thread = first_runnable_thread->scheduler_list_next; } - else + // If all threads have been preempted, allow them all to run again. + else if ( first_preempted_thread ) { - result = idle_thread; + first_runnable_thread = first_preempted_thread; + first_preempted_thread = NULL; + result = first_runnable_thread; + first_runnable_thread = first_runnable_thread->scheduler_list_next; } + // Otherwise there is nothing to run. + else + result = idle_thread; return result; } @@ -359,6 +368,37 @@ static void RealSwitch(struct interrupt_context* intctx, bool yielded) void Switch(struct interrupt_context* intctx) { + Thread* thread = CurrentThread(); + if ( thread->state == ThreadState::RUNNABLE ) + { + // Remove the thread from the list of runnable threads. + if ( thread == first_runnable_thread ) + { + first_runnable_thread = thread->scheduler_list_next; + if ( thread == first_runnable_thread ) + first_runnable_thread = NULL; + } + else if ( thread == first_preempted_thread ) + { + first_preempted_thread = thread->scheduler_list_next; + if ( thread == first_preempted_thread ) + first_preempted_thread = NULL; + } + assert(thread->scheduler_list_prev); + assert(thread->scheduler_list_next); + thread->scheduler_list_prev->scheduler_list_next = thread->scheduler_list_next; + thread->scheduler_list_next->scheduler_list_prev = thread->scheduler_list_prev; + thread->scheduler_list_prev = NULL; + thread->scheduler_list_next = NULL; + + // Insert the thread into the list of preempted threads. + if ( first_preempted_thread == NULL ) + first_preempted_thread = thread; + thread->scheduler_list_prev = first_preempted_thread->scheduler_list_prev; + thread->scheduler_list_next = first_preempted_thread; + first_preempted_thread->scheduler_list_prev = thread; + thread->scheduler_list_prev->scheduler_list_next = thread; + } RealSwitch(intctx, false); } @@ -437,9 +477,17 @@ void SetThreadState(Thread* thread, ThreadState state, bool wake_only) state != ThreadState::RUNNABLE ) { if ( thread == first_runnable_thread ) + { first_runnable_thread = thread->scheduler_list_next; - if ( thread == first_runnable_thread ) - first_runnable_thread = NULL; + if ( thread == first_runnable_thread ) + first_runnable_thread = NULL; + } + else if ( thread == first_preempted_thread ) + { + first_preempted_thread = thread->scheduler_list_next; + if ( thread == first_preempted_thread ) + first_preempted_thread = NULL; + } assert(thread->scheduler_list_prev); assert(thread->scheduler_list_next); thread->scheduler_list_prev->scheduler_list_next = thread->scheduler_list_next;