scheduler.cpp 31 KB

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