added 1 changeset to branch 'refs/remotes/pdziepak-github/scheduler' old head: bab69bdb4707498efc97bb3bf3d58d58553b4dbc new head: 03e3a8295375a672734943bcaa489ad09b22ac35 overview: https://github.com/pdziepak/Haiku/compare/bab69bd...03e3a82 ---------------------------------------------------------------------------- 03e3a82: kernel: Allow threads to yield CPU properly [ Pawel Dziepak <pdziepak@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- Commit: 03e3a8295375a672734943bcaa489ad09b22ac35 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Tue Oct 8 04:41:20 2013 UTC ---------------------------------------------------------------------------- 2 files changed, 75 insertions(+), 26 deletions(-) src/system/kernel/scheduler/scheduler_simple.cpp | 92 ++++++++++++++++---- src/system/kernel/thread.cpp | 9 +- ---------------------------------------------------------------------------- diff --git a/src/system/kernel/scheduler/scheduler_simple.cpp b/src/system/kernel/scheduler/scheduler_simple.cpp index 950da57..db51e53 100644 --- a/src/system/kernel/scheduler/scheduler_simple.cpp +++ b/src/system/kernel/scheduler/scheduler_simple.cpp @@ -15,6 +15,7 @@ #include <OS.h> +#include <AutoDeleter.h> #include <cpu.h> #include <debug.h> #include <int.h> @@ -43,7 +44,10 @@ const bigtime_t kThreadQuantum = 3000; // The run queue. Holds the threads ready to run ordered by priority. -static RunQueue<Thread, THREAD_MAX_SET_PRIORITY>* sRunQueue; +typedef RunQueue<Thread, THREAD_MAX_SET_PRIORITY> SimpleRunQueue; +static SimpleRunQueue* sRunQueue; +static SimpleRunQueue* sExpiredQueue; +static int32 sYieldedThreadPriority = -1; struct scheduler_thread_data { @@ -80,16 +84,13 @@ scheduler_thread_data::Init() } -static int -dump_run_queue(int argc, char **argv) +static inline void +dump_queue(RunQueue<Thread, THREAD_MAX_SET_PRIORITY>::ConstIterator& iterator) { - RunQueue<Thread, THREAD_MAX_SET_PRIORITY>::ConstIterator iterator; - iterator = sRunQueue->GetConstIterator(); - if (!iterator.HasNext()) - kprintf("Run queue is empty!\n"); + kprintf("Queue is empty.\n"); else { - kprintf("thread id priority penalty name\n"); + kprintf("thread id priority penalty name\n"); while (iterator.HasNext()) { Thread *thread = iterator.Next(); scheduler_thread_data* schedulerThreadData @@ -100,11 +101,33 @@ dump_run_queue(int argc, char **argv) schedulerThreadData->priority_penalty, thread->name); } } +} + + +static int +dump_run_queue(int argc, char **argv) +{ + RunQueue<Thread, THREAD_MAX_SET_PRIORITY>::ConstIterator iterator; + kprintf("Current run queue:\n"); + iterator = sRunQueue->GetConstIterator(); + dump_queue(iterator); + + kprintf("\nExpired run queue:\n"); + iterator = sExpiredQueue->GetConstIterator(); + dump_queue(iterator); return 0; } +static inline void +simple_yield(Thread* thread) +{ + TRACE("thread %ld yielded\n", thread->id); + sYieldedThreadPriority = max_c(sYieldedThreadPriority, thread->priority); +} + + static inline int32 simple_get_effective_priority(Thread *thread) { @@ -120,9 +143,10 @@ simple_get_effective_priority(Thread *thread) if (schedulerThreadData->forced_yield_count != 0 && schedulerThreadData->forced_yield_count % kYieldFrequency == 0) { TRACE("forcing thread %ld to yield\n", thread->id); - effectivePriority = B_LOWEST_ACTIVE_PRIORITY; - } else - effectivePriority -= schedulerThreadData->priority_penalty; + simple_yield(thread); + } + + effectivePriority -= schedulerThreadData->priority_penalty; } ASSERT(schedulerThreadData->priority_penalty >= 0); @@ -188,7 +212,11 @@ simple_enqueue_in_run_queue(Thread *thread) scheduler_thread_data* schedulerThreadData = reinterpret_cast<scheduler_thread_data*>(thread->scheduler_data); - sRunQueue->PushBack(thread, threadPriority); + if (threadPriority <= sYieldedThreadPriority) + sExpiredQueue->PushBack(thread, threadPriority); + else + sRunQueue->PushBack(thread, threadPriority); + thread->next_priority = thread->priority; schedulerThreadData->cpu_bound = true; schedulerThreadData->time_left = 0; @@ -358,6 +386,10 @@ simple_reschedule(void) else simple_cancel_penalty(oldThread); + if (oldThread->was_yielded) + simple_yield(oldThread); + oldThread->was_yielded = false; + TRACE("enqueueing thread %ld into run queue priority = %ld\n", oldThread->id, simple_get_effective_priority(oldThread)); simple_enqueue_in_run_queue(oldThread); @@ -385,7 +417,26 @@ simple_reschedule(void) schedulerOldThreadData->lost_cpu = false; // select thread with the biggest priority - nextThread = sRunQueue->PeekMaximum(); + do { + nextThread = sRunQueue->PeekMaximum(); + + if (sYieldedThreadPriority >= 0 && nextThread != NULL + && thread_is_idle_thread(nextThread)) { + sRunQueue->Remove(nextThread); + simple_enqueue_in_run_queue(nextThread); + continue; + } + + break; + } while (true); + if (nextThread == NULL && sYieldedThreadPriority >= 0) { + RunQueue<Thread, THREAD_MAX_SET_PRIORITY>* temp = sRunQueue; + sRunQueue = sExpiredQueue; + sExpiredQueue = temp; + sYieldedThreadPriority = -1; + + nextThread = sRunQueue->PeekMaximum(); + } if (!nextThread) panic("reschedule(): run queue is empty!\n"); @@ -399,7 +450,6 @@ simple_reschedule(void) nextThread->state = B_THREAD_RUNNING; nextThread->next_state = B_THREAD_READY; - oldThread->was_yielded = false; // track kernel time (user time is tracked in thread_at_kernel_entry()) scheduler_update_thread_times(oldThread, nextThread); @@ -490,20 +540,26 @@ static scheduler_ops kSimpleOps = { status_t scheduler_simple_init() { - sRunQueue = new(std::nothrow) RunQueue<Thread, THREAD_MAX_SET_PRIORITY>; + sRunQueue = new(std::nothrow) SimpleRunQueue; if (sRunQueue == NULL) return B_NO_MEMORY; + ObjectDeleter<SimpleRunQueue> runQueueDeleter(sRunQueue); + + sExpiredQueue = new(std::nothrow) SimpleRunQueue; + if (sExpiredQueue == NULL) + return B_NO_MEMORY; + ObjectDeleter<SimpleRunQueue> expiredQueueDeleter(sExpiredQueue); status_t result = sRunQueue->GetInitStatus(); - if (result != B_OK) { - delete sRunQueue; + if (result != B_OK) return result; - } gScheduler = &kSimpleOps; add_debugger_command_etc("run_queue", &dump_run_queue, "List threads in run queue", "\nLists threads in run queue", 0); + runQueueDeleter.Detach(); + expiredQueueDeleter.Detach(); return B_OK; } diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 7e29df2..0c785a2 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -2447,11 +2447,6 @@ void thread_yield(bool force) { if (force) { - // snooze for roughly 3 thread quantums - snooze_etc(9000, B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT); -#if 0 - cpu_status state; - Thread *thread = thread_get_current_thread(); if (thread == NULL) return; @@ -2459,10 +2454,8 @@ thread_yield(bool force) InterruptsSpinLocker _(gSchedulerLock); // mark the thread as yielded, so it will not be scheduled next - //thread->was_yielded = true; - thread->next_priority = B_LOWEST_ACTIVE_PRIORITY; + thread->was_yielded = true; scheduler_reschedule(); -#endif } else { Thread *thread = thread_get_current_thread(); if (thread == NULL)