From b164d8ee536dba526f9da2083433d529daf7b37b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 Mar 2019 17:01:17 -0400 Subject: Implement a new Core Scheduler --- src/core/hle/kernel/scheduler.h | 220 +++++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 103 deletions(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index b29bf7be8..50fa7376b 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -20,124 +20,141 @@ namespace Kernel { class Process; -class Scheduler final { +class GlobalScheduler final { public: - explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core); - ~Scheduler(); - - /// Returns whether there are any threads that are ready to run. - bool HaveReadyThreads() const; - - /// Reschedules to the next available thread (call after current thread is suspended) - void Reschedule(); - - /// Gets the current running thread - Thread* GetCurrentThread() const; - - /// Gets the timestamp for the last context switch in ticks. - u64 GetLastContextSwitchTicks() const; + static constexpr u32 NUM_CPU_CORES = 4; + GlobalScheduler() { + reselection_pending = false; + } + ~GlobalScheduler(); /// Adds a new thread to the scheduler void AddThread(SharedPtr thread); /// Removes a thread from the scheduler void RemoveThread(Thread* thread); - /// Schedules a thread that has become "ready" - void ScheduleThread(Thread* thread, u32 priority); + /// Returns a list of all threads managed by the scheduler + const std::vector>& GetThreadList() const { + return thread_list; + } - /// Unschedules a thread that was already scheduled - void UnscheduleThread(Thread* thread, u32 priority); + void Suggest(u32 priority, u32 core, Thread* thread) { + suggested_queue[core].add(thread, priority); + } - /// Sets the priority of a thread in the scheduler - void SetThreadPriority(Thread* thread, u32 priority); + void Unsuggest(u32 priority, u32 core, Thread* thread) { + suggested_queue[core].remove(thread, priority); + } - /// Gets the next suggested thread for load balancing - Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const; + void Schedule(u32 priority, u32 core, Thread* thread) { + ASSERT_MSG(thread->GetProcessorID() == core, + "Thread must be assigned to this core."); + scheduled_queue[core].add(thread, priority); + } - /** - * YieldWithoutLoadBalancing -- analogous to normal yield on a system - * Moves the thread to the end of the ready queue for its priority, and then reschedules the - * system to the new head of the queue. - * - * Example (Single Core -- but can be extrapolated to multi): - * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->) - * Currently Running: ThreadR - * - * ThreadR calls YieldWithoutLoadBalancing - * - * ThreadR is moved to the end of ready_queue[prio=0]: - * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->) - * Currently Running: Nothing - * - * System is rescheduled (ThreadA is popped off of queue): - * ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->) - * Currently Running: ThreadA - * - * If the queue is empty at time of call, no yielding occurs. This does not cross between cores - * or priorities at all. - */ - void YieldWithoutLoadBalancing(Thread* thread); + void SchedulePrepend(u32 priority, u32 core, Thread* thread) { + ASSERT_MSG(thread->GetProcessorID() == core, + "Thread must be assigned to this core."); + scheduled_queue[core].add(thread, priority, false); + } - /** - * YieldWithLoadBalancing -- yield but with better selection of the new running thread - * Moves the current thread to the end of the ready queue for its priority, then selects a - * 'suggested thread' (a thread on a different core that could run on this core) from the - * scheduler, changes its core, and reschedules the current core to that thread. - * - * Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were - * single core): - * ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant - * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] - * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 - * - * ThreadQ calls YieldWithLoadBalancing - * - * ThreadQ is moved to the end of ready_queue[core=0][prio=0]: - * ready_queue[core=0][prio=0]: ThreadA, ThreadB - * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] - * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 - * - * A list of suggested threads for each core is compiled - * Suggested Threads: {ThreadC on Core 1} - * If this were quad core (as the switch is), there could be between 0 and 3 threads in this - * list. If there are more than one, the thread is selected by highest prio. - * - * ThreadC is core changed to Core 0: - * ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ - * ready_queue[core=1][prio=0]: ThreadD - * Currently Running: None on Core 0 || ThreadP on Core 1 - * - * System is rescheduled (ThreadC is popped off of queue): - * ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ - * ready_queue[core=1][prio=0]: ThreadD - * Currently Running: ThreadC on Core 0 || ThreadP on Core 1 - * - * If no suggested threads can be found this will behave just as normal yield. If there are - * multiple candidates for the suggested thread on a core, the highest prio is taken. - */ - void YieldWithLoadBalancing(Thread* thread); + void Reschedule(u32 priority, u32 core, Thread* thread) { + scheduled_queue[core].remove(thread, priority); + scheduled_queue[core].add(thread, priority); + } - /// Currently unknown -- asserts as unimplemented on call - void YieldAndWaitForLoadBalancing(Thread* thread); + void Unschedule(u32 priority, u32 core, Thread* thread) { + scheduled_queue[core].remove(thread, priority); + } - /// Returns a list of all threads managed by the scheduler - const std::vector>& GetThreadList() const { - return thread_list; + void TransferToCore(u32 priority, s32 destination_core, Thread* thread) { + bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; + s32 source_core = thread->GetProcessorID(); + if (source_core == destination_core || !schedulable) + return; + thread->SetProcessorID(destination_core); + if (source_core >= 0) + Unschedule(priority, source_core, thread); + if (destination_core >= 0) { + Unsuggest(priority, destination_core, thread); + Schedule(priority, destination_core, thread); + } + if (source_core >= 0) + Suggest(priority, source_core, thread); + } + + void UnloadThread(s32 core); + + void SelectThreads(); + void SelectThread(u32 core); + + bool HaveReadyThreads(u32 core_id) { + return !scheduled_queue[core_id].empty(); + } + + void YieldThread(Thread* thread); + void YieldThreadAndBalanceLoad(Thread* thread); + void YieldThreadAndWaitForLoadBalancing(Thread* thread); + + u32 CpuCoresCount() const { + return NUM_CPU_CORES; + } + + void SetReselectionPending() { + reselection_pending.store(true, std::memory_order_release); + } + + bool IsReselectionPending() { + return reselection_pending.load(std::memory_order_acquire); } private: - /** - * Pops and returns the next thread from the thread queue - * @return A pointer to the next ready thread - */ - Thread* PopNextReadyThread(); + void AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner); + + static constexpr u32 min_regular_priority = 2; + std::array, NUM_CPU_CORES> scheduled_queue; + std::array, NUM_CPU_CORES> suggested_queue; + std::atomic reselection_pending; + + /// Lists all thread ids that aren't deleted/etc. + std::vector> thread_list; +}; + +class Scheduler final { +public: + explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 id); + ~Scheduler(); + + /// Returns whether there are any threads that are ready to run. + bool HaveReadyThreads() const; + + /// Reschedules to the next available thread (call after current thread is suspended) + void TryDoContextSwitch(); + + void UnloadThread(); + + void SelectThreads(); + + /// Gets the current running thread + Thread* GetCurrentThread() const; + + Thread* GetSelectedThread() const; + + /// Gets the timestamp for the last context switch in ticks. + u64 GetLastContextSwitchTicks() const; + + bool ContextSwitchPending() const { + return context_switch_pending; + } +private: + friend class GlobalScheduler; /** * Switches the CPU's active thread context to that of the specified thread * @param new_thread The thread to switch to */ - void SwitchContext(Thread* new_thread); + void SwitchContext(); /** * Called on every context switch to update the internal timestamp @@ -152,19 +169,16 @@ private: */ void UpdateLastContextSwitchTime(Thread* thread, Process* process); - /// Lists all thread ids that aren't deleted/etc. - std::vector> thread_list; - - /// Lists only ready thread ids. - Common::MultiLevelQueue ready_queue; - SharedPtr current_thread = nullptr; + SharedPtr selected_thread = nullptr; + Core::System& system; Core::ARM_Interface& cpu_core; u64 last_context_switch_time = 0; + u64 idle_selection_count = 0; + const u32 id; - Core::System& system; - static std::mutex scheduler_mutex; + bool context_switch_pending = false; }; } // namespace Kernel -- cgit v1.2.3 From 3a94e7ea3386cbd14e74255e0a4c7f8615a396c9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 2 Apr 2019 08:03:44 -0400 Subject: Comment and reorganize the scheduler --- src/core/hle/kernel/scheduler.h | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 50fa7376b..82ed64b55 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -48,14 +48,12 @@ public: } void Schedule(u32 priority, u32 core, Thread* thread) { - ASSERT_MSG(thread->GetProcessorID() == core, - "Thread must be assigned to this core."); + ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core."); scheduled_queue[core].add(thread, priority); } void SchedulePrepend(u32 priority, u32 core, Thread* thread) { - ASSERT_MSG(thread->GetProcessorID() == core, - "Thread must be assigned to this core."); + ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core."); scheduled_queue[core].add(thread, priority, false); } @@ -84,17 +82,47 @@ public: Suggest(priority, source_core, thread); } + /* + * UnloadThread selects a core and forces it to unload its current thread's context + */ void UnloadThread(s32 core); - void SelectThreads(); + /* + * SelectThread takes care of selecting the new scheduled thread. + * It does it in 3 steps: + * - First a thread is selected from the top of the priority queue. If no thread + * is obtained then we move to step two, else we are done. + * - Second we try to get a suggested thread that's not assigned to any core or + * that is not the top thread in that core. + * - Third is no suggested thread is found, we do a second pass and pick a running + * thread in another core and swap it with its current thread. + */ void SelectThread(u32 core); bool HaveReadyThreads(u32 core_id) { return !scheduled_queue[core_id].empty(); } + /* + * YieldThread takes a thread and moves it to the back of the it's priority list + * This operation can be redundant and no scheduling is changed if marked as so. + */ void YieldThread(Thread* thread); + + /* + * YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list. + * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or + * a better priority than the next thread in the core. + * This operation can be redundant and no scheduling is changed if marked as so. + */ void YieldThreadAndBalanceLoad(Thread* thread); + + /* + * YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue + * and into the suggested queue. If no thread can be squeduled afterwards in that core, + * a suggested thread is obtained instead. + * This operation can be redundant and no scheduling is changed if marked as so. + */ void YieldThreadAndWaitForLoadBalancing(Thread* thread); u32 CpuCoresCount() const { -- cgit v1.2.3 From 82218c925af8bcbaa05ae9f39af2d2393de7681f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 19 Jun 2019 09:11:18 -0400 Subject: Kernel: Style and Corrections --- src/core/hle/kernel/scheduler.h | 53 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 82ed64b55..1c9d8a30f 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -24,62 +24,70 @@ class GlobalScheduler final { public: static constexpr u32 NUM_CPU_CORES = 4; - GlobalScheduler() { - reselection_pending = false; - } + explicit GlobalScheduler(Core::System& system); ~GlobalScheduler(); /// Adds a new thread to the scheduler void AddThread(SharedPtr thread); /// Removes a thread from the scheduler - void RemoveThread(Thread* thread); + void RemoveThread(const Thread* thread); /// Returns a list of all threads managed by the scheduler const std::vector>& GetThreadList() const { return thread_list; } + // Add a thread to the suggested queue of a cpu core. Suggested threads may be + // picked if no thread is scheduled to run on the core. void Suggest(u32 priority, u32 core, Thread* thread) { suggested_queue[core].add(thread, priority); } + // Remove a thread to the suggested queue of a cpu core. Suggested threads may be + // picked if no thread is scheduled to run on the core. void Unsuggest(u32 priority, u32 core, Thread* thread) { suggested_queue[core].remove(thread, priority); } - void Schedule(u32 priority, u32 core, Thread* thread) { - ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core."); - scheduled_queue[core].add(thread, priority); - } + // Add a thread to the scheduling queue of a cpu core. The thread is added at the + // back the queue in its priority level + void Schedule(u32 priority, u32 core, Thread* thread); - void SchedulePrepend(u32 priority, u32 core, Thread* thread) { - ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core."); - scheduled_queue[core].add(thread, priority, false); - } + // Add a thread to the scheduling queue of a cpu core. The thread is added at the + // front the queue in its priority level + void SchedulePrepend(u32 priority, u32 core, Thread* thread); + // Reschedule an already scheduled thread based on a new priority void Reschedule(u32 priority, u32 core, Thread* thread) { scheduled_queue[core].remove(thread, priority); scheduled_queue[core].add(thread, priority); } + // Unschedule a thread. void Unschedule(u32 priority, u32 core, Thread* thread) { scheduled_queue[core].remove(thread, priority); } + // Transfers a thread into an specific core. If the destination_core is -1 + // it will be unscheduled from its source code and added into its suggested + // queue. void TransferToCore(u32 priority, s32 destination_core, Thread* thread) { - bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; - s32 source_core = thread->GetProcessorID(); - if (source_core == destination_core || !schedulable) + const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; + const s32 source_core = thread->GetProcessorID(); + if (source_core == destination_core || !schedulable) { return; + } thread->SetProcessorID(destination_core); - if (source_core >= 0) + if (source_core >= 0) { Unschedule(priority, source_core, thread); + } if (destination_core >= 0) { Unsuggest(priority, destination_core, thread); Schedule(priority, destination_core, thread); } - if (source_core >= 0) + if (source_core >= 0) { Suggest(priority, source_core, thread); + } } /* @@ -99,7 +107,7 @@ public: */ void SelectThread(u32 core); - bool HaveReadyThreads(u32 core_id) { + bool HaveReadyThreads(u32 core_id) const { return !scheduled_queue[core_id].empty(); } @@ -133,8 +141,8 @@ public: reselection_pending.store(true, std::memory_order_release); } - bool IsReselectionPending() { - return reselection_pending.load(std::memory_order_acquire); + bool IsReselectionPending() const { + return reselection_pending.load(); } private: @@ -147,11 +155,12 @@ private: /// Lists all thread ids that aren't deleted/etc. std::vector> thread_list; + Core::System& system; }; class Scheduler final { public: - explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 id); + explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 core_id); ~Scheduler(); /// Returns whether there are any threads that are ready to run. @@ -204,7 +213,7 @@ private: Core::ARM_Interface& cpu_core; u64 last_context_switch_time = 0; u64 idle_selection_count = 0; - const u32 id; + const u32 core_id; bool context_switch_pending = false; }; -- cgit v1.2.3 From 103f3a2fe51a09caf3f478226b6957b23c6eff79 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Sep 2019 10:23:43 -0400 Subject: Scheduler: Add protections for Yield bombing In case of redundant yields, the scheduler will now idle the core for it's timeslice, in order to avoid continuously yielding the same thing over and over. --- src/core/hle/kernel/scheduler.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 1c9d8a30f..8fcc86bae 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -115,7 +115,7 @@ public: * YieldThread takes a thread and moves it to the back of the it's priority list * This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldThread(Thread* thread); + bool YieldThread(Thread* thread); /* * YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list. @@ -123,7 +123,7 @@ public: * a better priority than the next thread in the core. * This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldThreadAndBalanceLoad(Thread* thread); + bool YieldThreadAndBalanceLoad(Thread* thread); /* * YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue @@ -131,7 +131,7 @@ public: * a suggested thread is obtained instead. * This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldThreadAndWaitForLoadBalancing(Thread* thread); + bool YieldThreadAndWaitForLoadBalancing(Thread* thread); u32 CpuCoresCount() const { return NUM_CPU_CORES; @@ -146,7 +146,7 @@ public: } private: - void AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner); + bool AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner); static constexpr u32 min_regular_priority = 2; std::array, NUM_CPU_CORES> scheduled_queue; -- cgit v1.2.3 From b49c0dab8772afb06358e5d19af092226b3a59bb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Sep 2019 11:04:40 -0400 Subject: Kernel: Initial implementation of thread preemption. --- src/core/hle/kernel/scheduler.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 8fcc86bae..c13a368fd 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -133,6 +133,8 @@ public: */ bool YieldThreadAndWaitForLoadBalancing(Thread* thread); + void PreemptThreads(); + u32 CpuCoresCount() const { return NUM_CPU_CORES; } @@ -153,6 +155,8 @@ private: std::array, NUM_CPU_CORES> suggested_queue; std::atomic reselection_pending; + std::array preemption_priorities = {59, 59, 59, 62}; + /// Lists all thread ids that aren't deleted/etc. std::vector> thread_list; Core::System& system; -- cgit v1.2.3 From 2d382de6fa79123fae7842246588651ee99b15e2 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Sep 2019 15:26:24 -0400 Subject: Scheduler: Corrections to YieldAndBalanceLoad and Yield bombing protection. --- src/core/hle/kernel/scheduler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index c13a368fd..408e20c88 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -155,7 +155,7 @@ private: std::array, NUM_CPU_CORES> suggested_queue; std::atomic reselection_pending; - std::array preemption_priorities = {59, 59, 59, 62}; + std::array preemption_priorities = {59, 59, 59, 62}; /// Lists all thread ids that aren't deleted/etc. std::vector> thread_list; -- cgit v1.2.3 From 25f8606a6dab595eb7a92fce9be32e0489079964 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 12 Oct 2019 08:21:51 -0400 Subject: Kernel Scheduler: Make sure the global scheduler shutdowns correctly. --- src/core/hle/kernel/scheduler.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 408e20c88..617553ae3 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -147,6 +147,8 @@ public: return reselection_pending.load(); } + void Shutdown(); + private: bool AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner); @@ -189,6 +191,11 @@ public: return context_switch_pending; } + void Shutdown() { + current_thread = nullptr; + selected_thread = nullptr; + } + private: friend class GlobalScheduler; /** -- cgit v1.2.3 From 3073615dbc214a53badc88da68eecbaaa73898de Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 12 Oct 2019 10:13:25 -0400 Subject: Kernel: Address Feedback. --- src/core/hle/kernel/scheduler.h | 65 +++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 41 deletions(-) (limited to 'src/core/hle/kernel/scheduler.h') diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 617553ae3..fcae28e0a 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -39,15 +39,11 @@ public: // Add a thread to the suggested queue of a cpu core. Suggested threads may be // picked if no thread is scheduled to run on the core. - void Suggest(u32 priority, u32 core, Thread* thread) { - suggested_queue[core].add(thread, priority); - } + void Suggest(u32 priority, u32 core, Thread* thread); // Remove a thread to the suggested queue of a cpu core. Suggested threads may be // picked if no thread is scheduled to run on the core. - void Unsuggest(u32 priority, u32 core, Thread* thread) { - suggested_queue[core].remove(thread, priority); - } + void Unsuggest(u32 priority, u32 core, Thread* thread); // Add a thread to the scheduling queue of a cpu core. The thread is added at the // back the queue in its priority level @@ -58,37 +54,15 @@ public: void SchedulePrepend(u32 priority, u32 core, Thread* thread); // Reschedule an already scheduled thread based on a new priority - void Reschedule(u32 priority, u32 core, Thread* thread) { - scheduled_queue[core].remove(thread, priority); - scheduled_queue[core].add(thread, priority); - } + void Reschedule(u32 priority, u32 core, Thread* thread); // Unschedule a thread. - void Unschedule(u32 priority, u32 core, Thread* thread) { - scheduled_queue[core].remove(thread, priority); - } + void Unschedule(u32 priority, u32 core, Thread* thread); // Transfers a thread into an specific core. If the destination_core is -1 // it will be unscheduled from its source code and added into its suggested // queue. - void TransferToCore(u32 priority, s32 destination_core, Thread* thread) { - const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; - const s32 source_core = thread->GetProcessorID(); - if (source_core == destination_core || !schedulable) { - return; - } - thread->SetProcessorID(destination_core); - if (source_core >= 0) { - Unschedule(priority, source_core, thread); - } - if (destination_core >= 0) { - Unsuggest(priority, destination_core, thread); - Schedule(priority, destination_core, thread); - } - if (source_core >= 0) { - Suggest(priority, source_core, thread); - } - } + void TransferToCore(u32 priority, s32 destination_core, Thread* thread); /* * UnloadThread selects a core and forces it to unload its current thread's context @@ -133,6 +107,12 @@ public: */ bool YieldThreadAndWaitForLoadBalancing(Thread* thread); + /* + * PreemptThreads this operation rotates the scheduling queues of threads at + * a preemption priority and then does some core rebalancing. Preemption priorities + * can be found in the array 'preemption_priorities'. This operation happens + * every 10ms. + */ void PreemptThreads(); u32 CpuCoresCount() const { @@ -140,11 +120,11 @@ public: } void SetReselectionPending() { - reselection_pending.store(true, std::memory_order_release); + is_reselection_pending.store(true, std::memory_order_release); } bool IsReselectionPending() const { - return reselection_pending.load(); + return is_reselection_pending.load(std::memory_order_acquire); } void Shutdown(); @@ -155,8 +135,10 @@ private: static constexpr u32 min_regular_priority = 2; std::array, NUM_CPU_CORES> scheduled_queue; std::array, NUM_CPU_CORES> suggested_queue; - std::atomic reselection_pending; + std::atomic is_reselection_pending; + // `preemption_priorities` are the priority levels at which the global scheduler + // preempts threads every 10 ms. They are ordered from Core 0 to Core 3 std::array preemption_priorities = {59, 59, 59, 62}; /// Lists all thread ids that aren't deleted/etc. @@ -166,7 +148,7 @@ private: class Scheduler final { public: - explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 core_id); + explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id); ~Scheduler(); /// Returns whether there are any threads that are ready to run. @@ -175,26 +157,27 @@ public: /// Reschedules to the next available thread (call after current thread is suspended) void TryDoContextSwitch(); + /// Unloads currently running thread void UnloadThread(); + /// Select the threads in top of the scheduling multilist. void SelectThreads(); /// Gets the current running thread Thread* GetCurrentThread() const; + /// Gets the currently selected thread from the top of the multilevel queue Thread* GetSelectedThread() const; /// Gets the timestamp for the last context switch in ticks. u64 GetLastContextSwitchTicks() const; bool ContextSwitchPending() const { - return context_switch_pending; + return is_context_switch_pending; } - void Shutdown() { - current_thread = nullptr; - selected_thread = nullptr; - } + /// Shutdowns the scheduler. + void Shutdown(); private: friend class GlobalScheduler; @@ -226,7 +209,7 @@ private: u64 idle_selection_count = 0; const u32 core_id; - bool context_switch_pending = false; + bool is_context_switch_pending = false; }; } // namespace Kernel -- cgit v1.2.3