diff options
Diffstat (limited to 'src/core/cpu_manager.cpp')
| -rw-r--r-- | src/core/cpu_manager.cpp | 194 |
1 files changed, 153 insertions, 41 deletions
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 70ddbdcca..494850992 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -2,80 +2,192 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/fiber.h" | ||
| 6 | #include "common/thread.h" | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 7 | #include "core/arm/exclusive_monitor.h" |
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 9 | #include "core/cpu_manager.h" | 10 | #include "core/cpu_manager.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 11 | #include "core/gdbstub/gdbstub.h" |
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/physical_core.h" | ||
| 14 | #include "core/hle/kernel/scheduler.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 11 | 16 | ||
| 12 | namespace Core { | 17 | namespace Core { |
| 13 | 18 | ||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | 19 | CpuManager::CpuManager(System& system) : system{system} {} |
| 15 | CpuManager::~CpuManager() = default; | 20 | CpuManager::~CpuManager() = default; |
| 16 | 21 | ||
| 22 | void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { | ||
| 23 | cpu_manager.RunThread(core); | ||
| 24 | } | ||
| 25 | |||
| 17 | void CpuManager::Initialize() { | 26 | void CpuManager::Initialize() { |
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | 27 | running_mode = true; |
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | 28 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 29 | core_data[core].host_thread = | ||
| 30 | std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); | ||
| 20 | } | 31 | } |
| 21 | } | 32 | } |
| 22 | 33 | ||
| 23 | void CpuManager::Shutdown() { | 34 | void CpuManager::Shutdown() { |
| 24 | for (auto& cpu_core : core_managers) { | 35 | running_mode = false; |
| 25 | cpu_core.reset(); | 36 | Pause(false); |
| 37 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 38 | core_data[core].host_thread->join(); | ||
| 26 | } | 39 | } |
| 27 | } | 40 | } |
| 28 | 41 | ||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | 42 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
| 30 | return *core_managers.at(index); | 43 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 44 | cpu_manager->RunGuestThread(); | ||
| 31 | } | 45 | } |
| 32 | 46 | ||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | 47 | void CpuManager::IdleThreadFunction(void* cpu_manager_) { |
| 34 | return *core_managers.at(index); | 48 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 49 | cpu_manager->RunIdleThread(); | ||
| 35 | } | 50 | } |
| 36 | 51 | ||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | 52 | void CpuManager::SuspendThreadFunction(void* cpu_manager_) { |
| 38 | // Otherwise, use single-threaded mode active_core variable | 53 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 39 | return *core_managers[active_core]; | 54 | cpu_manager->RunSuspendThread(); |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | 57 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 43 | // Otherwise, use single-threaded mode active_core variable | 58 | return std::function<void(void*)>(GuestThreadFunction); |
| 44 | return *core_managers[active_core]; | ||
| 45 | } | 59 | } |
| 46 | 60 | ||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | 61 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 48 | if (GDBStub::IsServerEnabled()) { | 62 | return std::function<void(void*)>(IdleThreadFunction); |
| 49 | GDBStub::HandlePacket(); | 63 | } |
| 50 | 64 | ||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | 65 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 52 | // execute. Otherwise, get out of the loop function. | 66 | return std::function<void(void*)>(SuspendThreadFunction); |
| 53 | if (GDBStub::GetCpuHaltFlag()) { | 67 | } |
| 54 | if (GDBStub::GetCpuStepFlag()) { | 68 | |
| 55 | tight_loop = false; | 69 | void* CpuManager::GetStartFuncParamater() { |
| 56 | } else { | 70 | return static_cast<void*>(this); |
| 57 | return; | 71 | } |
| 58 | } | 72 | |
| 59 | } | 73 | void CpuManager::RunGuestThread() { |
| 74 | auto& kernel = system.Kernel(); | ||
| 75 | { | ||
| 76 | auto& sched = kernel.CurrentScheduler(); | ||
| 77 | sched.OnThreadStart(); | ||
| 78 | } | ||
| 79 | while (true) { | ||
| 80 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 81 | LOG_CRITICAL(Core_ARM, "Running Guest Thread"); | ||
| 82 | physical_core.Idle(); | ||
| 83 | LOG_CRITICAL(Core_ARM, "Leaving Guest Thread"); | ||
| 84 | // physical_core.Run(); | ||
| 85 | auto& scheduler = physical_core.Scheduler(); | ||
| 86 | scheduler.TryDoContextSwitch(); | ||
| 60 | } | 87 | } |
| 88 | } | ||
| 61 | 89 | ||
| 62 | auto& core_timing = system.CoreTiming(); | 90 | void CpuManager::RunIdleThread() { |
| 63 | core_timing.ResetRun(); | 91 | auto& kernel = system.Kernel(); |
| 64 | bool keep_running{}; | 92 | while (true) { |
| 65 | do { | 93 | auto& physical_core = kernel.CurrentPhysicalCore(); |
| 66 | keep_running = false; | 94 | LOG_CRITICAL(Core_ARM, "Running Idle Thread"); |
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | 95 | physical_core.Idle(); |
| 68 | core_timing.SwitchContext(active_core); | 96 | auto& scheduler = physical_core.Scheduler(); |
| 69 | if (core_timing.CanCurrentContextRun()) { | 97 | scheduler.TryDoContextSwitch(); |
| 70 | core_managers[active_core]->RunLoop(tight_loop); | 98 | } |
| 99 | } | ||
| 100 | |||
| 101 | void CpuManager::RunSuspendThread() { | ||
| 102 | LOG_CRITICAL(Core_ARM, "Suspending Thread Entered"); | ||
| 103 | auto& kernel = system.Kernel(); | ||
| 104 | { | ||
| 105 | auto& sched = kernel.CurrentScheduler(); | ||
| 106 | sched.OnThreadStart(); | ||
| 107 | } | ||
| 108 | while (true) { | ||
| 109 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 110 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 111 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 112 | LOG_CRITICAL(Core_ARM, "Suspending Core {}", core); | ||
| 113 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); | ||
| 114 | LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core); | ||
| 115 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 116 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 117 | scheduler.TryDoContextSwitch(); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | void CpuManager::Pause(bool paused) { | ||
| 122 | if (!paused) { | ||
| 123 | bool all_not_barrier = false; | ||
| 124 | while (!all_not_barrier) { | ||
| 125 | all_not_barrier = true; | ||
| 126 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 127 | all_not_barrier &= | ||
| 128 | !core_data[core].is_running.load() && core_data[core].initialized.load(); | ||
| 71 | } | 129 | } |
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 73 | } | 130 | } |
| 74 | } while (keep_running); | 131 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 132 | core_data[core].enter_barrier->Set(); | ||
| 133 | } | ||
| 134 | if (paused_state.load()) { | ||
| 135 | bool all_barrier = false; | ||
| 136 | while (!all_barrier) { | ||
| 137 | all_barrier = true; | ||
| 138 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 139 | all_barrier &= | ||
| 140 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 144 | core_data[core].exit_barrier->Set(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } else { | ||
| 148 | /// Wait until all cores are paused. | ||
| 149 | bool all_barrier = false; | ||
| 150 | while (!all_barrier) { | ||
| 151 | all_barrier = true; | ||
| 152 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 153 | all_barrier &= | ||
| 154 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | /// Don't release the barrier | ||
| 158 | } | ||
| 159 | paused_state = paused; | ||
| 160 | } | ||
| 75 | 161 | ||
| 76 | if (GDBStub::IsServerEnabled()) { | 162 | void CpuManager::RunThread(std::size_t core) { |
| 77 | GDBStub::SetCpuStepFlag(false); | 163 | /// Initialization |
| 164 | system.RegisterCoreThread(core); | ||
| 165 | std::string name = "yuzu:CoreHostThread_" + std::to_string(core); | ||
| 166 | Common::SetCurrentThreadName(name.c_str()); | ||
| 167 | auto& data = core_data[core]; | ||
| 168 | data.enter_barrier = std::make_unique<Common::Event>(); | ||
| 169 | data.exit_barrier = std::make_unique<Common::Event>(); | ||
| 170 | data.host_context = Common::Fiber::ThreadToFiber(); | ||
| 171 | data.is_running = false; | ||
| 172 | data.initialized = true; | ||
| 173 | /// Running | ||
| 174 | while (running_mode) { | ||
| 175 | data.is_running = false; | ||
| 176 | data.enter_barrier->Wait(); | ||
| 177 | auto& scheduler = system.Kernel().CurrentScheduler(); | ||
| 178 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 179 | data.is_running = true; | ||
| 180 | Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); | ||
| 181 | data.is_running = false; | ||
| 182 | data.is_paused = true; | ||
| 183 | data.exit_barrier->Wait(); | ||
| 184 | data.is_paused = false; | ||
| 78 | } | 185 | } |
| 186 | /// Time to cleanup | ||
| 187 | data.host_context->Exit(); | ||
| 188 | data.enter_barrier.reset(); | ||
| 189 | data.exit_barrier.reset(); | ||
| 190 | data.initialized = false; | ||
| 79 | } | 191 | } |
| 80 | 192 | ||
| 81 | } // namespace Core | 193 | } // namespace Core |