scheduler.h 8.4 KB

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