scheduler.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. //
  5. // SelectThreads, Yield functions originally by TuxSH.
  6. // licensed under GPLv2 or later under exception provided by the author.
  7. #include <algorithm>
  8. #include <mutex>
  9. #include <set>
  10. #include <unordered_set>
  11. #include <utility>
  12. #include "common/assert.h"
  13. #include "common/bit_util.h"
  14. #include "common/fiber.h"
  15. #include "common/logging/log.h"
  16. #include "core/arm/arm_interface.h"
  17. #include "core/core.h"
  18. #include "core/core_timing.h"
  19. #include "core/cpu_manager.h"
  20. #include "core/hle/kernel/kernel.h"
  21. #include "core/hle/kernel/physical_core.h"
  22. #include "core/hle/kernel/process.h"
  23. #include "core/hle/kernel/scheduler.h"
  24. #include "core/hle/kernel/time_manager.h"
  25. namespace Kernel {
  26. GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
  27. GlobalScheduler::~GlobalScheduler() = default;
  28. void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
  29. std::scoped_lock lock{global_list_guard};
  30. thread_list.push_back(std::move(thread));
  31. }
  32. void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
  33. std::scoped_lock lock{global_list_guard};
  34. thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
  35. thread_list.end());
  36. }
  37. u32 GlobalScheduler::SelectThreads() {
  38. ASSERT(is_locked);
  39. const auto update_thread = [](Thread* thread, Scheduler& sched) {
  40. std::scoped_lock lock{sched.guard};
  41. if (thread != sched.selected_thread_set.get()) {
  42. if (thread == nullptr) {
  43. ++sched.idle_selection_count;
  44. }
  45. sched.selected_thread_set = SharedFrom(thread);
  46. }
  47. const bool reschedule_pending =
  48. sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
  49. sched.is_context_switch_pending = reschedule_pending;
  50. std::atomic_thread_fence(std::memory_order_seq_cst);
  51. return reschedule_pending;
  52. };
  53. if (!is_reselection_pending.load()) {
  54. return 0;
  55. }
  56. std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{};
  57. u32 idle_cores{};
  58. // Step 1: Get top thread in schedule queue.
  59. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  60. Thread* top_thread =
  61. scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
  62. if (top_thread != nullptr) {
  63. // TODO(Blinkhawk): Implement Thread Pinning
  64. } else {
  65. idle_cores |= (1ul << core);
  66. }
  67. top_threads[core] = top_thread;
  68. }
  69. while (idle_cores != 0) {
  70. u32 core_id = Common::CountTrailingZeroes32(idle_cores);
  71. if (!suggested_queue[core_id].empty()) {
  72. std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{};
  73. std::size_t num_candidates = 0;
  74. auto iter = suggested_queue[core_id].begin();
  75. Thread* suggested = nullptr;
  76. // Step 2: Try selecting a suggested thread.
  77. while (iter != suggested_queue[core_id].end()) {
  78. suggested = *iter;
  79. iter++;
  80. s32 suggested_core_id = suggested->GetProcessorID();
  81. Thread* top_thread =
  82. suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr;
  83. if (top_thread != suggested) {
  84. if (top_thread != nullptr &&
  85. top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) {
  86. suggested = nullptr;
  87. break;
  88. // There's a too high thread to do core migration, cancel
  89. }
  90. TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested);
  91. break;
  92. }
  93. suggested = nullptr;
  94. migration_candidates[num_candidates++] = suggested_core_id;
  95. }
  96. // Step 3: Select a suggested thread from another core
  97. if (suggested == nullptr) {
  98. for (std::size_t i = 0; i < num_candidates; i++) {
  99. s32 candidate_core = migration_candidates[i];
  100. suggested = top_threads[candidate_core];
  101. auto it = scheduled_queue[candidate_core].begin();
  102. it++;
  103. Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr;
  104. if (next != nullptr) {
  105. TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id),
  106. suggested);
  107. top_threads[candidate_core] = next;
  108. break;
  109. } else {
  110. suggested = nullptr;
  111. }
  112. }
  113. }
  114. top_threads[core_id] = suggested;
  115. }
  116. idle_cores &= ~(1ul << core_id);
  117. }
  118. u32 cores_needing_context_switch{};
  119. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  120. Scheduler& sched = kernel.Scheduler(core);
  121. ASSERT(top_threads[core] == nullptr ||
  122. static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
  123. if (update_thread(top_threads[core], sched)) {
  124. cores_needing_context_switch |= (1ul << core);
  125. }
  126. }
  127. return cores_needing_context_switch;
  128. }
  129. bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
  130. ASSERT(is_locked);
  131. // Note: caller should use critical section, etc.
  132. if (!yielding_thread->IsRunnable()) {
  133. // Normally this case shouldn't happen except for SetThreadActivity.
  134. is_reselection_pending.store(true, std::memory_order_release);
  135. return false;
  136. }
  137. const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
  138. const u32 priority = yielding_thread->GetPriority();
  139. // Yield the thread
  140. Reschedule(priority, core_id, yielding_thread);
  141. const Thread* const winner = scheduled_queue[core_id].front();
  142. if (kernel.GetCurrentHostThreadID() != core_id) {
  143. is_reselection_pending.store(true, std::memory_order_release);
  144. }
  145. return AskForReselectionOrMarkRedundant(yielding_thread, winner);
  146. }
  147. bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
  148. ASSERT(is_locked);
  149. // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
  150. // etc.
  151. if (!yielding_thread->IsRunnable()) {
  152. // Normally this case shouldn't happen except for SetThreadActivity.
  153. is_reselection_pending.store(true, std::memory_order_release);
  154. return false;
  155. }
  156. const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
  157. const u32 priority = yielding_thread->GetPriority();
  158. // Yield the thread
  159. Reschedule(priority, core_id, yielding_thread);
  160. std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
  161. for (std::size_t i = 0; i < current_threads.size(); i++) {
  162. current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
  163. }
  164. Thread* next_thread = scheduled_queue[core_id].front(priority);
  165. Thread* winner = nullptr;
  166. for (auto& thread : suggested_queue[core_id]) {
  167. const s32 source_core = thread->GetProcessorID();
  168. if (source_core >= 0) {
  169. if (current_threads[source_core] != nullptr) {
  170. if (thread == current_threads[source_core] ||
  171. current_threads[source_core]->GetPriority() < min_regular_priority) {
  172. continue;
  173. }
  174. }
  175. }
  176. if (next_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks() ||
  177. next_thread->GetPriority() < thread->GetPriority()) {
  178. if (thread->GetPriority() <= priority) {
  179. winner = thread;
  180. break;
  181. }
  182. }
  183. }
  184. if (winner != nullptr) {
  185. if (winner != yielding_thread) {
  186. TransferToCore(winner->GetPriority(), s32(core_id), winner);
  187. }
  188. } else {
  189. winner = next_thread;
  190. }
  191. if (kernel.GetCurrentHostThreadID() != core_id) {
  192. is_reselection_pending.store(true, std::memory_order_release);
  193. }
  194. return AskForReselectionOrMarkRedundant(yielding_thread, winner);
  195. }
  196. bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
  197. ASSERT(is_locked);
  198. // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
  199. // etc.
  200. if (!yielding_thread->IsRunnable()) {
  201. // Normally this case shouldn't happen except for SetThreadActivity.
  202. is_reselection_pending.store(true, std::memory_order_release);
  203. return false;
  204. }
  205. Thread* winner = nullptr;
  206. const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
  207. // Remove the thread from its scheduled mlq, put it on the corresponding "suggested" one instead
  208. TransferToCore(yielding_thread->GetPriority(), -1, yielding_thread);
  209. // If the core is idle, perform load balancing, excluding the threads that have just used this
  210. // function...
  211. if (scheduled_queue[core_id].empty()) {
  212. // Here, "current_threads" is calculated after the ""yield"", unlike yield -1
  213. std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
  214. for (std::size_t i = 0; i < current_threads.size(); i++) {
  215. current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
  216. }
  217. for (auto& thread : suggested_queue[core_id]) {
  218. const s32 source_core = thread->GetProcessorID();
  219. if (source_core < 0 || thread == current_threads[source_core]) {
  220. continue;
  221. }
  222. if (current_threads[source_core] == nullptr ||
  223. current_threads[source_core]->GetPriority() >= min_regular_priority) {
  224. winner = thread;
  225. }
  226. break;
  227. }
  228. if (winner != nullptr) {
  229. if (winner != yielding_thread) {
  230. TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
  231. }
  232. } else {
  233. winner = yielding_thread;
  234. }
  235. } else {
  236. winner = scheduled_queue[core_id].front();
  237. }
  238. if (kernel.GetCurrentHostThreadID() != core_id) {
  239. is_reselection_pending.store(true, std::memory_order_release);
  240. }
  241. return AskForReselectionOrMarkRedundant(yielding_thread, winner);
  242. }
  243. void GlobalScheduler::PreemptThreads() {
  244. ASSERT(is_locked);
  245. for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
  246. const u32 priority = preemption_priorities[core_id];
  247. if (scheduled_queue[core_id].size(priority) > 0) {
  248. if (scheduled_queue[core_id].size(priority) > 1) {
  249. scheduled_queue[core_id].front(priority)->IncrementYieldCount();
  250. }
  251. scheduled_queue[core_id].yield(priority);
  252. if (scheduled_queue[core_id].size(priority) > 1) {
  253. scheduled_queue[core_id].front(priority)->IncrementYieldCount();
  254. }
  255. }
  256. Thread* current_thread =
  257. scheduled_queue[core_id].empty() ? nullptr : scheduled_queue[core_id].front();
  258. Thread* winner = nullptr;
  259. for (auto& thread : suggested_queue[core_id]) {
  260. const s32 source_core = thread->GetProcessorID();
  261. if (thread->GetPriority() != priority) {
  262. continue;
  263. }
  264. if (source_core >= 0) {
  265. Thread* next_thread = scheduled_queue[source_core].empty()
  266. ? nullptr
  267. : scheduled_queue[source_core].front();
  268. if (next_thread != nullptr && next_thread->GetPriority() < 2) {
  269. break;
  270. }
  271. if (next_thread == thread) {
  272. continue;
  273. }
  274. }
  275. if (current_thread != nullptr &&
  276. current_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks()) {
  277. winner = thread;
  278. break;
  279. }
  280. }
  281. if (winner != nullptr) {
  282. TransferToCore(winner->GetPriority(), s32(core_id), winner);
  283. current_thread =
  284. winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
  285. }
  286. if (current_thread != nullptr && current_thread->GetPriority() > priority) {
  287. for (auto& thread : suggested_queue[core_id]) {
  288. const s32 source_core = thread->GetProcessorID();
  289. if (thread->GetPriority() < priority) {
  290. continue;
  291. }
  292. if (source_core >= 0) {
  293. Thread* next_thread = scheduled_queue[source_core].empty()
  294. ? nullptr
  295. : scheduled_queue[source_core].front();
  296. if (next_thread != nullptr && next_thread->GetPriority() < 2) {
  297. break;
  298. }
  299. if (next_thread == thread) {
  300. continue;
  301. }
  302. }
  303. if (current_thread != nullptr &&
  304. current_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks()) {
  305. winner = thread;
  306. break;
  307. }
  308. }
  309. if (winner != nullptr) {
  310. TransferToCore(winner->GetPriority(), s32(core_id), winner);
  311. current_thread = winner;
  312. }
  313. }
  314. is_reselection_pending.store(true, std::memory_order_release);
  315. }
  316. }
  317. void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
  318. Core::EmuThreadHandle global_thread) {
  319. u32 current_core = global_thread.host_handle;
  320. bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
  321. (current_core < Core::Hardware::NUM_CPU_CORES);
  322. while (cores_pending_reschedule != 0) {
  323. u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule);
  324. ASSERT(core < Core::Hardware::NUM_CPU_CORES);
  325. if (!must_context_switch || core != current_core) {
  326. auto& phys_core = kernel.PhysicalCore(core);
  327. phys_core.Interrupt();
  328. } else {
  329. must_context_switch = true;
  330. }
  331. cores_pending_reschedule &= ~(1ul << core);
  332. }
  333. if (must_context_switch) {
  334. auto& core_scheduler = kernel.CurrentScheduler();
  335. kernel.ExitSVCProfile();
  336. core_scheduler.TryDoContextSwitch();
  337. kernel.EnterSVCProfile();
  338. }
  339. }
  340. void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
  341. ASSERT(is_locked);
  342. suggested_queue[core].add(thread, priority);
  343. }
  344. void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) {
  345. ASSERT(is_locked);
  346. suggested_queue[core].remove(thread, priority);
  347. }
  348. void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) {
  349. ASSERT(is_locked);
  350. ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
  351. scheduled_queue[core].add(thread, priority);
  352. }
  353. void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) {
  354. ASSERT(is_locked);
  355. ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
  356. scheduled_queue[core].add(thread, priority, false);
  357. }
  358. void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) {
  359. ASSERT(is_locked);
  360. scheduled_queue[core].remove(thread, priority);
  361. scheduled_queue[core].add(thread, priority);
  362. }
  363. void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) {
  364. ASSERT(is_locked);
  365. scheduled_queue[core].remove(thread, priority);
  366. }
  367. void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
  368. ASSERT(is_locked);
  369. const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
  370. const s32 source_core = thread->GetProcessorID();
  371. if (source_core == destination_core || !schedulable) {
  372. return;
  373. }
  374. thread->SetProcessorID(destination_core);
  375. if (source_core >= 0) {
  376. Unschedule(priority, static_cast<u32>(source_core), thread);
  377. }
  378. if (destination_core >= 0) {
  379. Unsuggest(priority, static_cast<u32>(destination_core), thread);
  380. Schedule(priority, static_cast<u32>(destination_core), thread);
  381. }
  382. if (source_core >= 0) {
  383. Suggest(priority, static_cast<u32>(source_core), thread);
  384. }
  385. }
  386. bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
  387. const Thread* winner) {
  388. if (current_thread == winner) {
  389. current_thread->IncrementYieldCount();
  390. return true;
  391. } else {
  392. is_reselection_pending.store(true, std::memory_order_release);
  393. return false;
  394. }
  395. }
  396. void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
  397. if (old_flags == thread->scheduling_state) {
  398. return;
  399. }
  400. ASSERT(is_locked);
  401. if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) {
  402. // In this case the thread was running, now it's pausing/exitting
  403. if (thread->processor_id >= 0) {
  404. Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
  405. }
  406. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  407. if (core != static_cast<u32>(thread->processor_id) &&
  408. ((thread->affinity_mask >> core) & 1) != 0) {
  409. Unsuggest(thread->current_priority, core, thread);
  410. }
  411. }
  412. } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
  413. // The thread is now set to running from being stopped
  414. if (thread->processor_id >= 0) {
  415. Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
  416. }
  417. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  418. if (core != static_cast<u32>(thread->processor_id) &&
  419. ((thread->affinity_mask >> core) & 1) != 0) {
  420. Suggest(thread->current_priority, core, thread);
  421. }
  422. }
  423. }
  424. SetReselectionPending();
  425. }
  426. void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
  427. if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) {
  428. return;
  429. }
  430. ASSERT(is_locked);
  431. if (thread->processor_id >= 0) {
  432. Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread);
  433. }
  434. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  435. if (core != static_cast<u32>(thread->processor_id) &&
  436. ((thread->affinity_mask >> core) & 1) != 0) {
  437. Unsuggest(old_priority, core, thread);
  438. }
  439. }
  440. if (thread->processor_id >= 0) {
  441. if (thread == kernel.CurrentScheduler().GetCurrentThread()) {
  442. SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id),
  443. thread);
  444. } else {
  445. Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
  446. }
  447. }
  448. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  449. if (core != static_cast<u32>(thread->processor_id) &&
  450. ((thread->affinity_mask >> core) & 1) != 0) {
  451. Suggest(thread->current_priority, core, thread);
  452. }
  453. }
  454. thread->IncrementYieldCount();
  455. SetReselectionPending();
  456. }
  457. void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
  458. s32 old_core) {
  459. if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) ||
  460. thread->current_priority >= THREADPRIO_COUNT) {
  461. return;
  462. }
  463. ASSERT(is_locked);
  464. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  465. if (((old_affinity_mask >> core) & 1) != 0) {
  466. if (core == static_cast<u32>(old_core)) {
  467. Unschedule(thread->current_priority, core, thread);
  468. } else {
  469. Unsuggest(thread->current_priority, core, thread);
  470. }
  471. }
  472. }
  473. for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  474. if (((thread->affinity_mask >> core) & 1) != 0) {
  475. if (core == static_cast<u32>(thread->processor_id)) {
  476. Schedule(thread->current_priority, core, thread);
  477. } else {
  478. Suggest(thread->current_priority, core, thread);
  479. }
  480. }
  481. }
  482. thread->IncrementYieldCount();
  483. SetReselectionPending();
  484. }
  485. void GlobalScheduler::Shutdown() {
  486. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  487. scheduled_queue[core].clear();
  488. suggested_queue[core].clear();
  489. }
  490. thread_list.clear();
  491. }
  492. void GlobalScheduler::Lock() {
  493. Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
  494. ASSERT(!current_thread.IsInvalid());
  495. if (current_thread == current_owner) {
  496. ++scope_lock;
  497. } else {
  498. inner_lock.lock();
  499. is_locked = true;
  500. current_owner = current_thread;
  501. ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
  502. scope_lock = 1;
  503. }
  504. }
  505. void GlobalScheduler::Unlock() {
  506. if (--scope_lock != 0) {
  507. ASSERT(scope_lock > 0);
  508. return;
  509. }
  510. u32 cores_pending_reschedule = SelectThreads();
  511. Core::EmuThreadHandle leaving_thread = current_owner;
  512. current_owner = Core::EmuThreadHandle::InvalidHandle();
  513. scope_lock = 1;
  514. is_locked = false;
  515. inner_lock.unlock();
  516. EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread);
  517. }
  518. Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) {
  519. switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this);
  520. }
  521. Scheduler::~Scheduler() = default;
  522. bool Scheduler::HaveReadyThreads() const {
  523. return system.GlobalScheduler().HaveReadyThreads(core_id);
  524. }
  525. Thread* Scheduler::GetCurrentThread() const {
  526. if (current_thread) {
  527. return current_thread.get();
  528. }
  529. return idle_thread.get();
  530. }
  531. Thread* Scheduler::GetSelectedThread() const {
  532. return selected_thread.get();
  533. }
  534. u64 Scheduler::GetLastContextSwitchTicks() const {
  535. return last_context_switch_time;
  536. }
  537. void Scheduler::TryDoContextSwitch() {
  538. auto& phys_core = system.Kernel().CurrentPhysicalCore();
  539. if (phys_core.IsInterrupted()) {
  540. phys_core.ClearInterrupt();
  541. }
  542. guard.lock();
  543. if (is_context_switch_pending) {
  544. SwitchContext();
  545. } else {
  546. guard.unlock();
  547. }
  548. }
  549. void Scheduler::OnThreadStart() {
  550. SwitchContextStep2();
  551. }
  552. void Scheduler::Unload() {
  553. Thread* thread = current_thread.get();
  554. if (thread) {
  555. thread->SetContinuousOnSVC(false);
  556. thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
  557. thread->SetIsRunning(false);
  558. if (!thread->IsHLEThread() && !thread->HasExited()) {
  559. Core::ARM_Interface& cpu_core = thread->ArmInterface();
  560. cpu_core.SaveContext(thread->GetContext32());
  561. cpu_core.SaveContext(thread->GetContext64());
  562. // Save the TPIDR_EL0 system register in case it was modified.
  563. thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
  564. cpu_core.ClearExclusiveState();
  565. }
  566. thread->context_guard.unlock();
  567. }
  568. }
  569. void Scheduler::Reload() {
  570. Thread* thread = current_thread.get();
  571. if (thread) {
  572. ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
  573. "Thread must be runnable.");
  574. // Cancel any outstanding wakeup events for this thread
  575. thread->SetIsRunning(true);
  576. thread->SetWasRunning(false);
  577. thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
  578. auto* const thread_owner_process = thread->GetOwnerProcess();
  579. if (thread_owner_process != nullptr) {
  580. system.Kernel().MakeCurrentProcess(thread_owner_process);
  581. }
  582. if (!thread->IsHLEThread()) {
  583. Core::ARM_Interface& cpu_core = thread->ArmInterface();
  584. cpu_core.LoadContext(thread->GetContext32());
  585. cpu_core.LoadContext(thread->GetContext64());
  586. cpu_core.SetTlsAddress(thread->GetTLSAddress());
  587. cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
  588. cpu_core.ChangeProcessorID(this->core_id);
  589. cpu_core.ClearExclusiveState();
  590. }
  591. }
  592. }
  593. void Scheduler::SwitchContextStep2() {
  594. // Load context of new thread
  595. if (selected_thread) {
  596. ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
  597. "Thread must be runnable.");
  598. // Cancel any outstanding wakeup events for this thread
  599. selected_thread->SetIsRunning(true);
  600. selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
  601. selected_thread->SetWasRunning(false);
  602. auto* const thread_owner_process = current_thread->GetOwnerProcess();
  603. if (thread_owner_process != nullptr) {
  604. system.Kernel().MakeCurrentProcess(thread_owner_process);
  605. }
  606. if (!selected_thread->IsHLEThread()) {
  607. Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
  608. cpu_core.LoadContext(selected_thread->GetContext32());
  609. cpu_core.LoadContext(selected_thread->GetContext64());
  610. cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
  611. cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
  612. cpu_core.ChangeProcessorID(this->core_id);
  613. cpu_core.ClearExclusiveState();
  614. }
  615. }
  616. TryDoContextSwitch();
  617. }
  618. void Scheduler::SwitchContext() {
  619. current_thread_prev = current_thread;
  620. selected_thread = selected_thread_set;
  621. Thread* previous_thread = current_thread_prev.get();
  622. Thread* new_thread = selected_thread.get();
  623. current_thread = selected_thread;
  624. is_context_switch_pending = false;
  625. if (new_thread == previous_thread) {
  626. guard.unlock();
  627. return;
  628. }
  629. Process* const previous_process = system.Kernel().CurrentProcess();
  630. UpdateLastContextSwitchTime(previous_thread, previous_process);
  631. // Save context for previous thread
  632. if (previous_thread) {
  633. if (new_thread != nullptr && new_thread->IsSuspendThread()) {
  634. previous_thread->SetWasRunning(true);
  635. }
  636. previous_thread->SetContinuousOnSVC(false);
  637. previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
  638. previous_thread->SetIsRunning(false);
  639. if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) {
  640. Core::ARM_Interface& cpu_core = previous_thread->ArmInterface();
  641. cpu_core.SaveContext(previous_thread->GetContext32());
  642. cpu_core.SaveContext(previous_thread->GetContext64());
  643. // Save the TPIDR_EL0 system register in case it was modified.
  644. previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
  645. cpu_core.ClearExclusiveState();
  646. }
  647. previous_thread->context_guard.unlock();
  648. }
  649. std::shared_ptr<Common::Fiber>* old_context;
  650. if (previous_thread != nullptr) {
  651. old_context = &previous_thread->GetHostContext();
  652. } else {
  653. old_context = &idle_thread->GetHostContext();
  654. }
  655. guard.unlock();
  656. Common::Fiber::YieldTo(*old_context, switch_fiber);
  657. /// When a thread wakes up, the scheduler may have changed to other in another core.
  658. auto& next_scheduler = system.Kernel().CurrentScheduler();
  659. next_scheduler.SwitchContextStep2();
  660. }
  661. void Scheduler::OnSwitch(void* this_scheduler) {
  662. Scheduler* sched = static_cast<Scheduler*>(this_scheduler);
  663. sched->SwitchToCurrent();
  664. }
  665. void Scheduler::SwitchToCurrent() {
  666. while (true) {
  667. {
  668. std::scoped_lock lock{guard};
  669. selected_thread = selected_thread_set;
  670. current_thread = selected_thread;
  671. is_context_switch_pending = false;
  672. }
  673. while (!is_context_switch_pending) {
  674. if (current_thread != nullptr && !current_thread->IsHLEThread()) {
  675. current_thread->context_guard.lock();
  676. if (!current_thread->IsRunnable()) {
  677. current_thread->context_guard.unlock();
  678. break;
  679. }
  680. if (current_thread->GetProcessorID() != core_id) {
  681. current_thread->context_guard.unlock();
  682. break;
  683. }
  684. }
  685. std::shared_ptr<Common::Fiber>* next_context;
  686. if (current_thread != nullptr) {
  687. next_context = &current_thread->GetHostContext();
  688. } else {
  689. next_context = &idle_thread->GetHostContext();
  690. }
  691. Common::Fiber::YieldTo(switch_fiber, *next_context);
  692. }
  693. }
  694. }
  695. void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
  696. const u64 prev_switch_ticks = last_context_switch_time;
  697. const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
  698. const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
  699. if (thread != nullptr) {
  700. thread->UpdateCPUTimeTicks(update_ticks);
  701. }
  702. if (process != nullptr) {
  703. process->UpdateCPUTimeTicks(update_ticks);
  704. }
  705. last_context_switch_time = most_recent_switch_ticks;
  706. }
  707. void Scheduler::Initialize() {
  708. std::string name = "Idle Thread Id:" + std::to_string(core_id);
  709. std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
  710. void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
  711. ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
  712. auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
  713. nullptr, std::move(init_func), init_func_parameter);
  714. idle_thread = std::move(thread_res).Unwrap();
  715. }
  716. void Scheduler::Shutdown() {
  717. current_thread = nullptr;
  718. selected_thread = nullptr;
  719. }
  720. SchedulerLock::SchedulerLock(KernelCore& kernel) : kernel{kernel} {
  721. kernel.GlobalScheduler().Lock();
  722. }
  723. SchedulerLock::~SchedulerLock() {
  724. kernel.GlobalScheduler().Unlock();
  725. }
  726. SchedulerLockAndSleep::SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle,
  727. Thread* time_task, s64 nanoseconds)
  728. : SchedulerLock{kernel}, event_handle{event_handle}, time_task{time_task}, nanoseconds{
  729. nanoseconds} {
  730. event_handle = InvalidHandle;
  731. }
  732. SchedulerLockAndSleep::~SchedulerLockAndSleep() {
  733. if (sleep_cancelled) {
  734. return;
  735. }
  736. auto& time_manager = kernel.TimeManager();
  737. time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
  738. }
  739. void SchedulerLockAndSleep::Release() {
  740. if (sleep_cancelled) {
  741. return;
  742. }
  743. auto& time_manager = kernel.TimeManager();
  744. time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
  745. sleep_cancelled = true;
  746. }
  747. } // namespace Kernel