scheduler.cpp 4.3 KB

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