diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 7b090ccb5e..a3aafce1b0 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/assert.h" #include "core/core.h" @@ -17,56 +18,58 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel) GlobalSchedulerContext::~GlobalSchedulerContext() = default; -void GlobalSchedulerContext::AddThread(KThread* thread) { +/// @brief Adds a new thread to the scheduler +void GlobalSchedulerContext::AddThread(KThread* thread) noexcept { std::scoped_lock lock{m_global_list_guard}; m_thread_list.push_back(thread); } -void GlobalSchedulerContext::RemoveThread(KThread* thread) { +/// @brief Removes a thread from the scheduler +void GlobalSchedulerContext::RemoveThread(KThread* thread) noexcept { std::scoped_lock lock{m_global_list_guard}; - std::erase(m_thread_list, thread); + m_thread_list.erase(std::ranges::find(m_thread_list, thread)); } -void GlobalSchedulerContext::PreemptThreads() { +/// @brief 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'. +/// @note This operation happens every 10ms. +void GlobalSchedulerContext::PreemptThreads() noexcept { // The priority levels at which the global scheduler preempts threads every 10 ms. They are // ordered from Core 0 to Core 3. - static constexpr std::array preemption_priorities{ + static constexpr std::array per_core{ 59, 59, 59, 63, }; - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - const u32 priority = preemption_priorities[core_id]; - KScheduler::RotateScheduledQueue(m_kernel, core_id, priority); - } + for (u32 core_id = 0; core_id < per_core.size(); core_id++) + KScheduler::RotateScheduledQueue(m_kernel, core_id, per_core[core_id]); } -bool GlobalSchedulerContext::IsLocked() const { +/// @brief Returns true if the global scheduler lock is acquired +bool GlobalSchedulerContext::IsLocked() const noexcept { return m_scheduler_lock.IsLockedByCurrentThread(); } -void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) { +void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) noexcept { ASSERT(this->IsLocked()); - - m_woken_dummy_threads.insert(thread); + m_woken_dummy_threads.push_back(thread); } -void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) { +void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noexcept { ASSERT(this->IsLocked()); - - m_woken_dummy_threads.erase(thread); + if(auto it = std::ranges::find(m_woken_dummy_threads, thread); it != m_woken_dummy_threads.end()) { + *it = m_woken_dummy_threads.back(); + m_woken_dummy_threads.pop_back(); + } } -void GlobalSchedulerContext::WakeupWaitingDummyThreads() { +void GlobalSchedulerContext::WakeupWaitingDummyThreads() noexcept { ASSERT(this->IsLocked()); - - for (auto* thread : m_woken_dummy_threads) { + for (auto* thread : m_woken_dummy_threads) thread->DummyThreadEndWait(); - } - m_woken_dummy_threads.clear(); } diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index c48e8cd120..74b2ef608e 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -4,8 +4,7 @@ #pragma once #include -#include -#include +#include #include "common/common_types.h" #include "core/hardware_properties.h" @@ -31,59 +30,42 @@ class GlobalSchedulerContext final { friend class KScheduler; public: + static constexpr size_t MAX_THREADS = 256; using LockType = KAbstractSchedulerLock; + using ThreadList = boost::container::small_vector; explicit GlobalSchedulerContext(KernelCore& kernel); ~GlobalSchedulerContext(); - /// Adds a new thread to the scheduler - void AddThread(KThread* thread); - - /// Removes a thread from the scheduler - void RemoveThread(KThread* thread); - - /// Returns a list of all threads managed by the scheduler + /// @brief Returns a list of all threads managed by the scheduler /// This is only safe to iterate while holding the scheduler lock - const std::vector& GetThreadList() const { + ThreadList const& GetThreadList() const noexcept { return m_thread_list; } - - /** - * 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'. - * - * @note This operation happens every 10ms. - */ - void PreemptThreads(); - - /// Returns true if the global scheduler lock is acquired - bool IsLocked() const; - - void UnregisterDummyThreadForWakeup(KThread* thread); - void RegisterDummyThreadForWakeup(KThread* thread); - void WakeupWaitingDummyThreads(); - - LockType& SchedulerLock() { + LockType& SchedulerLock() noexcept { return m_scheduler_lock; } + void AddThread(KThread* thread) noexcept; + void RemoveThread(KThread* thread) noexcept; + void PreemptThreads() noexcept; + bool IsLocked() const noexcept; + void UnregisterDummyThreadForWakeup(KThread* thread) noexcept; + void RegisterDummyThreadForWakeup(KThread* thread) noexcept; + void WakeupWaitingDummyThreads() noexcept; private: friend class KScopedSchedulerLock; friend class KScopedSchedulerLockAndSleep; KernelCore& m_kernel; - std::atomic_bool m_scheduler_update_needed{}; KSchedulerPriorityQueue m_priority_queue; LockType m_scheduler_lock; - + std::mutex m_global_list_guard; /// Lists dummy threads pending wakeup on lock release - std::set m_woken_dummy_threads; - + ThreadList m_woken_dummy_threads; /// Lists all thread ids that aren't deleted/etc. - std::vector m_thread_list; - std::mutex m_global_list_guard; + ThreadList m_thread_list; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 676e19190a..28d39320e4 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -527,35 +527,27 @@ void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) { ASSERT(IsSchedulerLockedByCurrentThread(kernel)); - // Check if the state has changed, because if it hasn't there's nothing to do. - const ThreadState cur_state = thread->GetRawState(); - if (cur_state == old_state) { - return; - } - - // Update the priority queues. - if (old_state == ThreadState::Runnable) { - // If we were previously runnable, then we're not runnable now, and we should remove. - GetPriorityQueue(kernel).Remove(thread); - IncrementScheduledCount(thread); - SetSchedulerUpdateNeeded(kernel); - - if (thread->IsDummyThread()) { + if (const ThreadState cur_state = thread->GetRawState(); cur_state != old_state) { + // Update the priority queues. + if (old_state == ThreadState::Runnable) { + // If we were previously runnable, then we're not runnable now, and we should remove. + GetPriorityQueue(kernel).Remove(thread); + IncrementScheduledCount(thread); + SetSchedulerUpdateNeeded(kernel); // HACK: if this is a dummy thread, it should no longer wake up when the // scheduler lock is released. - kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); - } - } else if (cur_state == ThreadState::Runnable) { - // If we're now runnable, then we weren't previously, and we should add. - GetPriorityQueue(kernel).PushBack(thread); - IncrementScheduledCount(thread); - SetSchedulerUpdateNeeded(kernel); - - if (thread->IsDummyThread()) { + if (thread->IsDummyThread()) + kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); + } else if (cur_state == ThreadState::Runnable) { + // If we're now runnable, then we weren't previously, and we should add. + GetPriorityQueue(kernel).PushBack(thread); + IncrementScheduledCount(thread); + SetSchedulerUpdateNeeded(kernel); // HACK: if this is a dummy thread, it should wake up when the scheduler // lock is released. - kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); + if (thread->IsDummyThread()) + kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); } } }