scheduler.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <utility>
  6. #include "common/assert.h"
  7. #include "common/logging/log.h"
  8. #include "core/arm/arm_interface.h"
  9. #include "core/core.h"
  10. #include "core/core_timing.h"
  11. #include "core/hle/kernel/process.h"
  12. #include "core/hle/kernel/scheduler.h"
  13. namespace Kernel {
  14. std::mutex Scheduler::scheduler_mutex;
  15. Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
  16. Scheduler::~Scheduler() {
  17. for (auto& thread : thread_list) {
  18. thread->Stop();
  19. }
  20. }
  21. bool Scheduler::HaveReadyThreads() const {
  22. std::lock_guard<std::mutex> lock(scheduler_mutex);
  23. return ready_queue.get_first() != nullptr;
  24. }
  25. Thread* Scheduler::GetCurrentThread() const {
  26. return current_thread.get();
  27. }
  28. Thread* Scheduler::PopNextReadyThread() {
  29. Thread* next = nullptr;
  30. Thread* thread = GetCurrentThread();
  31. if (thread && thread->status == ThreadStatus::Running) {
  32. // We have to do better than the current thread.
  33. // This call returns null when that's not possible.
  34. next = ready_queue.pop_first_better(thread->current_priority);
  35. if (!next) {
  36. // Otherwise just keep going with the current thread
  37. next = thread;
  38. }
  39. } else {
  40. next = ready_queue.pop_first();
  41. }
  42. return next;
  43. }
  44. void Scheduler::SwitchContext(Thread* new_thread) {
  45. Thread* previous_thread = GetCurrentThread();
  46. // Save context for previous thread
  47. if (previous_thread) {
  48. previous_thread->last_running_ticks = CoreTiming::GetTicks();
  49. cpu_core.SaveContext(previous_thread->context);
  50. // Save the TPIDR_EL0 system register in case it was modified.
  51. previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
  52. if (previous_thread->status == ThreadStatus::Running) {
  53. // This is only the case when a reschedule is triggered without the current thread
  54. // yielding execution (i.e. an event triggered, system core time-sliced, etc)
  55. ready_queue.push_front(previous_thread->current_priority, previous_thread);
  56. previous_thread->status = ThreadStatus::Ready;
  57. }
  58. }
  59. // Load context of new thread
  60. if (new_thread) {
  61. ASSERT_MSG(new_thread->status == ThreadStatus::Ready,
  62. "Thread must be ready to become running.");
  63. // Cancel any outstanding wakeup events for this thread
  64. new_thread->CancelWakeupTimer();
  65. auto previous_process = Core::CurrentProcess();
  66. current_thread = new_thread;
  67. ready_queue.remove(new_thread->current_priority, new_thread);
  68. new_thread->status = ThreadStatus::Running;
  69. if (previous_process != current_thread->owner_process) {
  70. Core::CurrentProcess() = current_thread->owner_process;
  71. SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
  72. }
  73. cpu_core.LoadContext(new_thread->context);
  74. cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
  75. cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
  76. cpu_core.ClearExclusiveState();
  77. } else {
  78. current_thread = nullptr;
  79. // Note: We do not reset the current process and current page table when idling because
  80. // technically we haven't changed processes, our threads are just paused.
  81. }
  82. }
  83. void Scheduler::Reschedule() {
  84. std::lock_guard<std::mutex> lock(scheduler_mutex);
  85. Thread* cur = GetCurrentThread();
  86. Thread* next = PopNextReadyThread();
  87. if (cur && next) {
  88. LOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId());
  89. } else if (cur) {
  90. LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
  91. } else if (next) {
  92. LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
  93. }
  94. SwitchContext(next);
  95. }
  96. void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
  97. std::lock_guard<std::mutex> lock(scheduler_mutex);
  98. thread_list.push_back(std::move(thread));
  99. ready_queue.prepare(priority);
  100. }
  101. void Scheduler::RemoveThread(Thread* thread) {
  102. std::lock_guard<std::mutex> lock(scheduler_mutex);
  103. thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
  104. thread_list.end());
  105. }
  106. void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
  107. std::lock_guard<std::mutex> lock(scheduler_mutex);
  108. ASSERT(thread->status == ThreadStatus::Ready);
  109. ready_queue.push_back(priority, thread);
  110. }
  111. void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
  112. std::lock_guard<std::mutex> lock(scheduler_mutex);
  113. ASSERT(thread->status == ThreadStatus::Ready);
  114. ready_queue.remove(priority, thread);
  115. }
  116. void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
  117. std::lock_guard<std::mutex> lock(scheduler_mutex);
  118. // If thread was ready, adjust queues
  119. if (thread->status == ThreadStatus::Ready)
  120. ready_queue.move(thread, thread->current_priority, priority);
  121. else
  122. ready_queue.prepare(priority);
  123. }
  124. } // namespace Kernel