scheduler.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <atomic>
  6. #include <memory>
  7. #include <mutex>
  8. #include <vector>
  9. #include "common/common_types.h"
  10. #include "common/multi_level_queue.h"
  11. #include "common/spin_lock.h"
  12. #include "core/hardware_properties.h"
  13. #include "core/hle/kernel/thread.h"
  14. namespace Common {
  15. class Fiber;
  16. }
  17. namespace Core {
  18. class ARM_Interface;
  19. class System;
  20. } // namespace Core
  21. namespace Kernel {
  22. class KernelCore;
  23. class Process;
  24. class SchedulerLock;
  25. class GlobalScheduler final {
  26. public:
  27. explicit GlobalScheduler(KernelCore& kernel);
  28. ~GlobalScheduler();
  29. /// Adds a new thread to the scheduler
  30. void AddThread(std::shared_ptr<Thread> thread);
  31. /// Removes a thread from the scheduler
  32. void RemoveThread(std::shared_ptr<Thread> thread);
  33. /// Returns a list of all threads managed by the scheduler
  34. const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
  35. return thread_list;
  36. }
  37. /// Notify the scheduler a thread's status has changed.
  38. void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags);
  39. /// Notify the scheduler a thread's priority has changed.
  40. void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority);
  41. /// Notify the scheduler a thread's core and/or affinity mask has changed.
  42. void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core);
  43. /**
  44. * Takes care of selecting the new scheduled threads in three steps:
  45. *
  46. * 1. First a thread is selected from the top of the priority queue. If no thread
  47. * is obtained then we move to step two, else we are done.
  48. *
  49. * 2. Second we try to get a suggested thread that's not assigned to any core or
  50. * that is not the top thread in that core.
  51. *
  52. * 3. Third is no suggested thread is found, we do a second pass and pick a running
  53. * thread in another core and swap it with its current thread.
  54. *
  55. * returns the cores needing scheduling.
  56. */
  57. u32 SelectThreads();
  58. bool HaveReadyThreads(std::size_t core_id) const {
  59. return !scheduled_queue[core_id].empty();
  60. }
  61. /**
  62. * Takes a thread and moves it to the back of the it's priority list.
  63. *
  64. * @note This operation can be redundant and no scheduling is changed if marked as so.
  65. */
  66. bool YieldThread(Thread* thread);
  67. /**
  68. * Takes a thread and moves it to the back of the it's priority list.
  69. * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
  70. * a better priority than the next thread in the core.
  71. *
  72. * @note This operation can be redundant and no scheduling is changed if marked as so.
  73. */
  74. bool YieldThreadAndBalanceLoad(Thread* thread);
  75. /**
  76. * Takes a thread and moves it out of the scheduling queue.
  77. * and into the suggested queue. If no thread can be scheduled afterwards in that core,
  78. * a suggested thread is obtained instead.
  79. *
  80. * @note This operation can be redundant and no scheduling is changed if marked as so.
  81. */
  82. bool YieldThreadAndWaitForLoadBalancing(Thread* thread);
  83. /**
  84. * Rotates the scheduling queues of threads at a preemption priority and then does
  85. * some core rebalancing. Preemption priorities can be found in the array
  86. * 'preemption_priorities'.
  87. *
  88. * @note This operation happens every 10ms.
  89. */
  90. void PreemptThreads();
  91. u32 CpuCoresCount() const {
  92. return Core::Hardware::NUM_CPU_CORES;
  93. }
  94. void SetReselectionPending() {
  95. is_reselection_pending.store(true, std::memory_order_release);
  96. }
  97. bool IsReselectionPending() const {
  98. return is_reselection_pending.load(std::memory_order_acquire);
  99. }
  100. void Shutdown();
  101. private:
  102. friend class SchedulerLock;
  103. /// Lock the scheduler to the current thread.
  104. void Lock();
  105. /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
  106. /// and reschedules current core if needed.
  107. void Unlock();
  108. void EnableInterruptAndSchedule(u32 cores_pending_reschedule,
  109. Core::EmuThreadHandle global_thread);
  110. /**
  111. * Add a thread to the suggested queue of a cpu core. Suggested threads may be
  112. * picked if no thread is scheduled to run on the core.
  113. */
  114. void Suggest(u32 priority, std::size_t core, Thread* thread);
  115. /**
  116. * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
  117. * picked if no thread is scheduled to run on the core.
  118. */
  119. void Unsuggest(u32 priority, std::size_t core, Thread* thread);
  120. /**
  121. * Add a thread to the scheduling queue of a cpu core. The thread is added at the
  122. * back the queue in its priority level.
  123. */
  124. void Schedule(u32 priority, std::size_t core, Thread* thread);
  125. /**
  126. * Add a thread to the scheduling queue of a cpu core. The thread is added at the
  127. * front the queue in its priority level.
  128. */
  129. void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
  130. /// Reschedule an already scheduled thread based on a new priority
  131. void Reschedule(u32 priority, std::size_t core, Thread* thread);
  132. /// Unschedules a thread.
  133. void Unschedule(u32 priority, std::size_t core, Thread* thread);
  134. /**
  135. * Transfers a thread into an specific core. If the destination_core is -1
  136. * it will be unscheduled from its source code and added into its suggested
  137. * queue.
  138. */
  139. void TransferToCore(u32 priority, s32 destination_core, Thread* thread);
  140. bool AskForReselectionOrMarkRedundant(Thread* current_thread, const Thread* winner);
  141. static constexpr u32 min_regular_priority = 2;
  142. std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, Core::Hardware::NUM_CPU_CORES>
  143. scheduled_queue;
  144. std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, Core::Hardware::NUM_CPU_CORES>
  145. suggested_queue;
  146. std::atomic<bool> is_reselection_pending{false};
  147. // The priority levels at which the global scheduler preempts threads every 10 ms. They are
  148. // ordered from Core 0 to Core 3.
  149. std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
  150. /// Scheduler lock mechanisms.
  151. bool is_locked{};
  152. Common::SpinLock inner_lock{};
  153. std::atomic<s64> scope_lock{};
  154. Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
  155. Common::SpinLock global_list_guard{};
  156. /// Lists all thread ids that aren't deleted/etc.
  157. std::vector<std::shared_ptr<Thread>> thread_list;
  158. KernelCore& kernel;
  159. };
  160. class Scheduler final {
  161. public:
  162. explicit Scheduler(Core::System& system, std::size_t core_id);
  163. ~Scheduler();
  164. /// Returns whether there are any threads that are ready to run.
  165. bool HaveReadyThreads() const;
  166. /// Reschedules to the next available thread (call after current thread is suspended)
  167. void TryDoContextSwitch();
  168. /// The next two are for SingleCore Only.
  169. /// Unload current thread before preempting core.
  170. void Unload();
  171. /// Reload current thread after core preemption.
  172. void Reload();
  173. /// Gets the current running thread
  174. Thread* GetCurrentThread() const;
  175. /// Gets the currently selected thread from the top of the multilevel queue
  176. Thread* GetSelectedThread() const;
  177. /// Gets the timestamp for the last context switch in ticks.
  178. u64 GetLastContextSwitchTicks() const;
  179. bool ContextSwitchPending() const {
  180. return is_context_switch_pending;
  181. }
  182. void Initialize();
  183. /// Shutdowns the scheduler.
  184. void Shutdown();
  185. void OnThreadStart();
  186. std::shared_ptr<Common::Fiber>& ControlContext() {
  187. return switch_fiber;
  188. }
  189. const std::shared_ptr<Common::Fiber>& ControlContext() const {
  190. return switch_fiber;
  191. }
  192. private:
  193. friend class GlobalScheduler;
  194. /// Switches the CPU's active thread context to that of the specified thread
  195. void SwitchContext();
  196. /// When a thread wakes up, it must run this through it's new scheduler
  197. void SwitchContextStep2();
  198. /**
  199. * Called on every context switch to update the internal timestamp
  200. * This also updates the running time ticks for the given thread and
  201. * process using the following difference:
  202. *
  203. * ticks += most_recent_ticks - last_context_switch_ticks
  204. *
  205. * The internal tick timestamp for the scheduler is simply the
  206. * most recent tick count retrieved. No special arithmetic is
  207. * applied to it.
  208. */
  209. void UpdateLastContextSwitchTime(Thread* thread, Process* process);
  210. static void OnSwitch(void* this_scheduler);
  211. void SwitchToCurrent();
  212. std::shared_ptr<Thread> current_thread = nullptr;
  213. std::shared_ptr<Thread> selected_thread = nullptr;
  214. std::shared_ptr<Thread> current_thread_prev = nullptr;
  215. std::shared_ptr<Thread> selected_thread_set = nullptr;
  216. std::shared_ptr<Thread> idle_thread = nullptr;
  217. std::shared_ptr<Common::Fiber> switch_fiber = nullptr;
  218. Core::System& system;
  219. u64 last_context_switch_time = 0;
  220. u64 idle_selection_count = 0;
  221. const std::size_t core_id;
  222. Common::SpinLock guard{};
  223. bool is_context_switch_pending = false;
  224. };
  225. class SchedulerLock {
  226. public:
  227. explicit SchedulerLock(KernelCore& kernel);
  228. ~SchedulerLock();
  229. protected:
  230. KernelCore& kernel;
  231. };
  232. class SchedulerLockAndSleep : public SchedulerLock {
  233. public:
  234. explicit SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* time_task,
  235. s64 nanoseconds);
  236. ~SchedulerLockAndSleep();
  237. void CancelSleep() {
  238. sleep_cancelled = true;
  239. }
  240. void Release();
  241. private:
  242. Handle& event_handle;
  243. Thread* time_task;
  244. s64 nanoseconds;
  245. bool sleep_cancelled{};
  246. };
  247. } // namespace Kernel