CPU_Manager: Unload/Reload threads on preemption on SingleCore
This commit is contained in:
parent
8a78fc2580
commit
a439cdf22e
|
@ -225,7 +225,7 @@ void CpuManager::SingleCoreRunGuestLoop() {
|
||||||
}
|
}
|
||||||
physical_core.ClearExclusive();
|
physical_core.ClearExclusive();
|
||||||
PreemptSingleCore();
|
PreemptSingleCore();
|
||||||
auto& scheduler = physical_core.Scheduler();
|
auto& scheduler = kernel.Scheduler(current_core);
|
||||||
scheduler.TryDoContextSwitch();
|
scheduler.TryDoContextSwitch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,11 +260,15 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||||
void CpuManager::PreemptSingleCore() {
|
void CpuManager::PreemptSingleCore() {
|
||||||
preemption_count = 0;
|
preemption_count = 0;
|
||||||
std::size_t old_core = current_core;
|
std::size_t old_core = current_core;
|
||||||
current_core = (current_core + 1) % Core::Hardware::NUM_CPU_CORES;
|
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||||
auto& scheduler = system.Kernel().Scheduler(old_core);
|
auto& scheduler = system.Kernel().Scheduler(old_core);
|
||||||
Kernel::Thread* current_thread = system.Kernel().Scheduler(old_core).GetCurrentThread();
|
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||||
Kernel::Thread* next_thread = system.Kernel().Scheduler(current_core).GetCurrentThread();
|
scheduler.Unload();
|
||||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_thread->GetHostContext());
|
auto& next_scheduler = system.Kernel().Scheduler(current_core);
|
||||||
|
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||||
|
/// May have changed scheduler
|
||||||
|
auto& current_scheduler = system.Kernel().Scheduler(current_core);
|
||||||
|
current_scheduler.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::SingleCorePause(bool paused) {
|
void CpuManager::SingleCorePause(bool paused) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
@ -45,7 +46,7 @@ public:
|
||||||
void* GetStartFuncParamater();
|
void* GetStartFuncParamater();
|
||||||
|
|
||||||
std::size_t CurrentCore() const {
|
std::size_t CurrentCore() const {
|
||||||
return current_core;
|
return current_core.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -88,7 +89,7 @@ private:
|
||||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||||
|
|
||||||
bool is_multicore{};
|
bool is_multicore{};
|
||||||
std::size_t current_core{};
|
std::atomic<std::size_t> current_core{};
|
||||||
std::size_t preemption_count{};
|
std::size_t preemption_count{};
|
||||||
static constexpr std::size_t max_cycle_runs = 5;
|
static constexpr std::size_t max_cycle_runs = 5;
|
||||||
|
|
||||||
|
|
|
@ -602,6 +602,48 @@ void Scheduler::OnThreadStart() {
|
||||||
SwitchContextStep2();
|
SwitchContextStep2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Scheduler::Unload() {
|
||||||
|
Thread* thread = current_thread.get();
|
||||||
|
if (thread) {
|
||||||
|
thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
|
||||||
|
thread->SetIsRunning(false);
|
||||||
|
if (!thread->IsHLEThread()) {
|
||||||
|
auto& cpu_core = system.ArmInterface(core_id);
|
||||||
|
cpu_core.SaveContext(thread->GetContext32());
|
||||||
|
cpu_core.SaveContext(thread->GetContext64());
|
||||||
|
// Save the TPIDR_EL0 system register in case it was modified.
|
||||||
|
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||||
|
cpu_core.ClearExclusiveState();
|
||||||
|
}
|
||||||
|
thread->context_guard.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::Reload() {
|
||||||
|
Thread* thread = current_thread.get();
|
||||||
|
if (thread) {
|
||||||
|
ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
|
||||||
|
"Thread must be runnable.");
|
||||||
|
|
||||||
|
// Cancel any outstanding wakeup events for this thread
|
||||||
|
thread->SetIsRunning(true);
|
||||||
|
thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
|
||||||
|
|
||||||
|
auto* const thread_owner_process = thread->GetOwnerProcess();
|
||||||
|
if (thread_owner_process != nullptr) {
|
||||||
|
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||||
|
}
|
||||||
|
if (!thread->IsHLEThread()) {
|
||||||
|
auto& cpu_core = system.ArmInterface(core_id);
|
||||||
|
cpu_core.LoadContext(thread->GetContext32());
|
||||||
|
cpu_core.LoadContext(thread->GetContext64());
|
||||||
|
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||||
|
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||||
|
cpu_core.ClearExclusiveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Scheduler::SwitchContextStep2() {
|
void Scheduler::SwitchContextStep2() {
|
||||||
Thread* previous_thread = current_thread_prev.get();
|
Thread* previous_thread = current_thread_prev.get();
|
||||||
Thread* new_thread = selected_thread.get();
|
Thread* new_thread = selected_thread.get();
|
||||||
|
|
|
@ -210,6 +210,12 @@ public:
|
||||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||||
void TryDoContextSwitch();
|
void TryDoContextSwitch();
|
||||||
|
|
||||||
|
/// The next two are for SingleCore Only.
|
||||||
|
/// Unload current thread before preempting core.
|
||||||
|
void Unload();
|
||||||
|
/// Reload current thread after core preemption.
|
||||||
|
void Reload();
|
||||||
|
|
||||||
/// Gets the current running thread
|
/// Gets the current running thread
|
||||||
Thread* GetCurrentThread() const;
|
Thread* GetCurrentThread() const;
|
||||||
|
|
||||||
|
@ -230,6 +236,10 @@ public:
|
||||||
|
|
||||||
void OnThreadStart();
|
void OnThreadStart();
|
||||||
|
|
||||||
|
std::shared_ptr<Common::Fiber> ControlContext() {
|
||||||
|
return switch_fiber;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class GlobalScheduler;
|
friend class GlobalScheduler;
|
||||||
|
|
||||||
|
|
Reference in New Issue