// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include "common/common_types.h" #include "core/hle/kernel/global_scheduler_context.h" #include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_scheduler_lock.h" #include "core/hle/kernel/k_scoped_lock.h" #include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_thread.h" namespace Common { class Fiber; } namespace Core { class System; } namespace Kernel { class KernelCore; class KInterruptTaskManager; class KProcess; class KThread; class KScopedDisableDispatch; class KScopedSchedulerLock; class KScopedSchedulerLockAndSleep; class KScheduler final { public: YUZU_NON_COPYABLE(KScheduler); YUZU_NON_MOVEABLE(KScheduler); using LockType = KAbstractSchedulerLock; explicit KScheduler(KernelCore& kernel); ~KScheduler(); void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id); void Activate(); void OnThreadStart(); void Unload(KThread* thread); void Reload(KThread* thread); void SetInterruptTaskRunnable(); void RequestScheduleOnInterrupt(); void PreemptSingleCore(); u64 GetIdleCount() { return m_state.idle_count; } KThread* GetIdleThread() const { return m_idle_thread; } bool IsIdle() const { return m_current_thread.load() == m_idle_thread; } KThread* GetPreviousThread() const { return m_state.prev_thread; } KThread* GetSchedulerCurrentThread() const { return m_current_thread.load(); } s64 GetLastContextSwitchTime() const { return m_last_context_switch_time; } // Static public API. static bool CanSchedule(KernelCore& kernel) { return GetCurrentThread(kernel).GetDisableDispatchCount() == 0; } static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) { return kernel.GlobalSchedulerContext().m_scheduler_lock.IsLockedByCurrentThread(); } static bool IsSchedulerUpdateNeeded(KernelCore& kernel) { return kernel.GlobalSchedulerContext().m_scheduler_update_needed; } static void SetSchedulerUpdateNeeded(KernelCore& kernel) { kernel.GlobalSchedulerContext().m_scheduler_update_needed = true; } static void ClearSchedulerUpdateNeeded(KernelCore& kernel) { kernel.GlobalSchedulerContext().m_scheduler_update_needed = false; } static void DisableScheduling(KernelCore& kernel); static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); static u64 UpdateHighestPriorityThreads(KernelCore& kernel); static void ClearPreviousThread(KernelCore& kernel, KThread* thread); static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state); static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority); static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core); static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority); static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling); static void YieldWithoutCoreMigration(KernelCore& kernel); static void YieldWithCoreMigration(KernelCore& kernel); static void YieldToAnyThread(KernelCore& kernel); private: // Static private API. static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) { return kernel.GlobalSchedulerContext().m_priority_queue; } static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel); static void RescheduleCurrentHLEThread(KernelCore& kernel); // Instanced private API. void ScheduleImpl(); void ScheduleImplFiber(); void SwitchThread(KThread* next_thread); void Schedule(); void ScheduleOnInterrupt(); void RescheduleOtherCores(u64 cores_needing_scheduling); void RescheduleCurrentCore(); void RescheduleCurrentCoreImpl(); u64 UpdateHighestPriorityThread(KThread* thread); private: friend class KScopedDisableDispatch; struct SchedulingState { std::atomic needs_scheduling{false}; bool interrupt_task_runnable{false}; bool should_count_idle{false}; u64 idle_count{0}; KThread* highest_priority_thread{nullptr}; void* idle_thread_stack{nullptr}; std::atomic prev_thread{nullptr}; KInterruptTaskManager* interrupt_task_manager{nullptr}; }; KernelCore& m_kernel; SchedulingState m_state; bool m_is_active{false}; s32 m_core_id{0}; s64 m_last_context_switch_time{0}; KThread* m_idle_thread{nullptr}; std::atomic m_current_thread{nullptr}; std::shared_ptr m_switch_fiber{}; KThread* m_switch_cur_thread{}; KThread* m_switch_highest_priority_thread{}; bool m_switch_from_schedule{}; }; class KScopedSchedulerLock : public KScopedLock { public: explicit KScopedSchedulerLock(KernelCore& kernel) : KScopedLock(kernel.GlobalSchedulerContext().m_scheduler_lock) {} ~KScopedSchedulerLock() = default; }; } // namespace Kernel