diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 134 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.h | 74 |
3 files changed, 210 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2f1fd7880..76f70a844 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -53,6 +53,8 @@ add_library(core STATIC | |||
| 53 | hle/kernel/process.h | 53 | hle/kernel/process.h |
| 54 | hle/kernel/resource_limit.cpp | 54 | hle/kernel/resource_limit.cpp |
| 55 | hle/kernel/resource_limit.h | 55 | hle/kernel/resource_limit.h |
| 56 | hle/kernel/scheduler.cpp | ||
| 57 | hle/kernel/scheduler.h | ||
| 56 | hle/kernel/server_port.cpp | 58 | hle/kernel/server_port.cpp |
| 57 | hle/kernel/server_port.h | 59 | hle/kernel/server_port.h |
| 58 | hle/kernel/server_session.cpp | 60 | hle/kernel/server_session.cpp |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp new file mode 100644 index 000000000..2fe0d5a38 --- /dev/null +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/kernel/scheduler.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | Scheduler::Scheduler(std::shared_ptr<ARM_Interface> cpu_core) : cpu_core(cpu_core) {} | ||
| 12 | |||
| 13 | Scheduler::~Scheduler() { | ||
| 14 | for (auto& thread : thread_list) { | ||
| 15 | thread->Stop(); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | bool Scheduler::HaveReadyThreads() { | ||
| 20 | return ready_queue.get_first() != nullptr; | ||
| 21 | } | ||
| 22 | |||
| 23 | Thread* Scheduler::GetCurrentThread() const { | ||
| 24 | return current_thread.get(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Thread* Scheduler::PopNextReadyThread() { | ||
| 28 | Thread* next = nullptr; | ||
| 29 | Thread* thread = GetCurrentThread(); | ||
| 30 | |||
| 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 | |||
| 43 | return next; | ||
| 44 | } | ||
| 45 | |||
| 46 | void Scheduler::SwitchContext(Thread* new_thread) { | ||
| 47 | Thread* previous_thread = GetCurrentThread(); | ||
| 48 | |||
| 49 | // Save context for previous thread | ||
| 50 | if (previous_thread) { | ||
| 51 | previous_thread->last_running_ticks = CoreTiming::GetTicks(); | ||
| 52 | cpu_core->SaveContext(previous_thread->context); | ||
| 53 | |||
| 54 | if (previous_thread->status == THREADSTATUS_RUNNING) { | ||
| 55 | // This is only the case when a reschedule is triggered without the current thread | ||
| 56 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | ||
| 57 | ready_queue.push_front(previous_thread->current_priority, previous_thread); | ||
| 58 | previous_thread->status = THREADSTATUS_READY; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | // Load context of new thread | ||
| 63 | if (new_thread) { | ||
| 64 | ASSERT_MSG(new_thread->status == THREADSTATUS_READY, | ||
| 65 | "Thread must be ready to become running."); | ||
| 66 | |||
| 67 | // Cancel any outstanding wakeup events for this thread | ||
| 68 | new_thread->CancelWakeupTimer(); | ||
| 69 | |||
| 70 | auto previous_process = Kernel::g_current_process; | ||
| 71 | |||
| 72 | current_thread = new_thread; | ||
| 73 | |||
| 74 | ready_queue.remove(new_thread->current_priority, new_thread); | ||
| 75 | new_thread->status = THREADSTATUS_RUNNING; | ||
| 76 | |||
| 77 | if (previous_process != current_thread->owner_process) { | ||
| 78 | Kernel::g_current_process = current_thread->owner_process; | ||
| 79 | SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||
| 80 | } | ||
| 81 | |||
| 82 | cpu_core->LoadContext(new_thread->context); | ||
| 83 | cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); | ||
| 84 | } else { | ||
| 85 | current_thread = nullptr; | ||
| 86 | // Note: We do not reset the current process and current page table when idling because | ||
| 87 | // technically we haven't changed processes, our threads are just paused. | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void Scheduler::Reschedule() { | ||
| 92 | Thread* cur = GetCurrentThread(); | ||
| 93 | Thread* next = PopNextReadyThread(); | ||
| 94 | |||
| 95 | if (cur && next) { | ||
| 96 | LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); | ||
| 97 | } else if (cur) { | ||
| 98 | LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); | ||
| 99 | } else if (next) { | ||
| 100 | LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); | ||
| 101 | } | ||
| 102 | |||
| 103 | SwitchContext(next); | ||
| 104 | } | ||
| 105 | |||
| 106 | void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { | ||
| 107 | thread_list.push_back(thread); | ||
| 108 | ready_queue.prepare(priority); | ||
| 109 | } | ||
| 110 | |||
| 111 | void Scheduler::RemoveThread(Thread* thread) { | ||
| 112 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | ||
| 113 | thread_list.end()); | ||
| 114 | } | ||
| 115 | |||
| 116 | void Scheduler::ScheduleThread(Thread* thread, u32 priority) { | ||
| 117 | ASSERT(thread->status == THREADSTATUS_READY); | ||
| 118 | ready_queue.push_back(priority, thread); | ||
| 119 | } | ||
| 120 | |||
| 121 | void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { | ||
| 122 | ASSERT(thread->status == THREADSTATUS_READY); | ||
| 123 | ready_queue.remove(priority, thread); | ||
| 124 | } | ||
| 125 | |||
| 126 | void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | ||
| 127 | // If thread was ready, adjust queues | ||
| 128 | if (thread->status == THREADSTATUS_READY) | ||
| 129 | ready_queue.move(thread, thread->current_priority, priority); | ||
| 130 | else | ||
| 131 | ready_queue.prepare(priority); | ||
| 132 | } | ||
| 133 | |||
| 134 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h new file mode 100644 index 000000000..72e658ec6 --- /dev/null +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/thread_queue_list.h" | ||
| 11 | #include "core/arm/arm_interface.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | class Scheduler final { | ||
| 17 | public: | ||
| 18 | Scheduler(std::shared_ptr<ARM_Interface> cpu_core); | ||
| 19 | ~Scheduler(); | ||
| 20 | |||
| 21 | /// Returns whether there are any threads that are ready to run. | ||
| 22 | bool HaveReadyThreads(); | ||
| 23 | |||
| 24 | /// Reschedules to the next available thread (call after current thread is suspended) | ||
| 25 | void Reschedule(); | ||
| 26 | |||
| 27 | /// Gets the current running thread | ||
| 28 | Thread* GetCurrentThread() const; | ||
| 29 | |||
| 30 | /// Adds a new thread to the scheduler | ||
| 31 | void AddThread(SharedPtr<Thread> thread, u32 priority); | ||
| 32 | |||
| 33 | /// Removes a thread from the scheduler | ||
| 34 | void RemoveThread(Thread* thread); | ||
| 35 | |||
| 36 | /// Schedules a thread that has become "ready" | ||
| 37 | void ScheduleThread(Thread* thread, u32 priority); | ||
| 38 | |||
| 39 | /// Unschedules a thread that was already scheduled | ||
| 40 | void UnscheduleThread(Thread* thread, u32 priority); | ||
| 41 | |||
| 42 | /// Sets the priority of a thread in the scheduler | ||
| 43 | void SetThreadPriority(Thread* thread, u32 priority); | ||
| 44 | |||
| 45 | /// Returns a list of all threads managed by the scheduler | ||
| 46 | const std::vector<SharedPtr<Thread>>& GetThreadList() const { | ||
| 47 | return thread_list; | ||
| 48 | } | ||
| 49 | |||
| 50 | private: | ||
| 51 | /** | ||
| 52 | * Pops and returns the next thread from the thread queue | ||
| 53 | * @return A pointer to the next ready thread | ||
| 54 | */ | ||
| 55 | Thread* PopNextReadyThread(); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Switches the CPU's active thread context to that of the specified thread | ||
| 59 | * @param new_thread The thread to switch to | ||
| 60 | */ | ||
| 61 | void SwitchContext(Thread* new_thread); | ||
| 62 | |||
| 63 | /// Lists all thread ids that aren't deleted/etc. | ||
| 64 | std::vector<SharedPtr<Thread>> thread_list; | ||
| 65 | |||
| 66 | /// Lists only ready thread ids. | ||
| 67 | Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; | ||
| 68 | |||
| 69 | SharedPtr<Thread> current_thread = nullptr; | ||
| 70 | |||
| 71 | std::shared_ptr<ARM_Interface> cpu_core; | ||
| 72 | }; | ||
| 73 | |||
| 74 | } // namespace Kernel | ||