Просмотр исходного кода

CPU_Manager: Unload/Reload threads on preemption on SingleCore

Fernando Sahmkow 6 лет назад
Родитель
Сommit
a439cdf22e
4 измененных файлов с 64 добавлено и 7 удалено
  1. 9 5
      src/core/cpu_manager.cpp
  2. 3 2
      src/core/cpu_manager.h
  3. 42 0
      src/core/hle/kernel/scheduler.cpp
  4. 10 0
      src/core/hle/kernel/scheduler.h

+ 9 - 5
src/core/cpu_manager.cpp

@@ -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* next_thread = system.Kernel().Scheduler(current_core).GetCurrentThread();
-    Common::Fiber::YieldTo(current_thread->GetHostContext(), next_thread->GetHostContext());
+    Kernel::Thread* current_thread = scheduler.GetCurrentThread();
+    scheduler.Unload();
+    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) {

+ 3 - 2
src/core/cpu_manager.h

@@ -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;
 
 

+ 42 - 0
src/core/hle/kernel/scheduler.cpp

@@ -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();

+ 10 - 0
src/core/hle/kernel/scheduler.h

@@ -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;