Merge pull request #8388 from liamwhite/simpler-pause
CpuManager: simplify pausing
This commit is contained in:
		| @@ -16,7 +16,8 @@ | |||||||
|  |  | ||||||
| namespace Core { | namespace Core { | ||||||
|  |  | ||||||
| CpuManager::CpuManager(System& system_) : system{system_} {} | CpuManager::CpuManager(System& system_) | ||||||
|  |     : pause_barrier{std::make_unique<Common::Barrier>(1)}, system{system_} {} | ||||||
| CpuManager::~CpuManager() = default; | CpuManager::~CpuManager() = default; | ||||||
|  |  | ||||||
| void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, | void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, | ||||||
| @@ -30,8 +31,10 @@ void CpuManager::Initialize() { | |||||||
|         for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |         for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||||||
|             core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); |             core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); | ||||||
|         } |         } | ||||||
|  |         pause_barrier = std::make_unique<Common::Barrier>(Core::Hardware::NUM_CPU_CORES + 1); | ||||||
|     } else { |     } else { | ||||||
|         core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); |         core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); | ||||||
|  |         pause_barrier = std::make_unique<Common::Barrier>(2); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -138,51 +141,14 @@ void CpuManager::MultiCoreRunSuspendThread() { | |||||||
|         auto core = kernel.CurrentPhysicalCoreIndex(); |         auto core = kernel.CurrentPhysicalCoreIndex(); | ||||||
|         auto& scheduler = *kernel.CurrentScheduler(); |         auto& scheduler = *kernel.CurrentScheduler(); | ||||||
|         Kernel::KThread* current_thread = scheduler.GetCurrentThread(); |         Kernel::KThread* current_thread = scheduler.GetCurrentThread(); | ||||||
|  |         current_thread->DisableDispatch(); | ||||||
|  |  | ||||||
|         Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); |         Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); | ||||||
|         ASSERT(scheduler.ContextSwitchPending()); |  | ||||||
|         ASSERT(core == kernel.CurrentPhysicalCoreIndex()); |         ASSERT(core == kernel.CurrentPhysicalCoreIndex()); | ||||||
|         scheduler.RescheduleCurrentCore(); |         scheduler.RescheduleCurrentCore(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void CpuManager::MultiCorePause(bool paused) { |  | ||||||
|     if (!paused) { |  | ||||||
|         bool all_not_barrier = false; |  | ||||||
|         while (!all_not_barrier) { |  | ||||||
|             all_not_barrier = true; |  | ||||||
|             for (const auto& data : core_data) { |  | ||||||
|                 all_not_barrier &= !data.is_running.load() && data.initialized.load(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         for (auto& data : core_data) { |  | ||||||
|             data.enter_barrier->Set(); |  | ||||||
|         } |  | ||||||
|         if (paused_state.load()) { |  | ||||||
|             bool all_barrier = false; |  | ||||||
|             while (!all_barrier) { |  | ||||||
|                 all_barrier = true; |  | ||||||
|                 for (const auto& data : core_data) { |  | ||||||
|                     all_barrier &= data.is_paused.load() && data.initialized.load(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             for (auto& data : core_data) { |  | ||||||
|                 data.exit_barrier->Set(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         /// Wait until all cores are paused. |  | ||||||
|         bool all_barrier = false; |  | ||||||
|         while (!all_barrier) { |  | ||||||
|             all_barrier = true; |  | ||||||
|             for (const auto& data : core_data) { |  | ||||||
|                 all_barrier &= data.is_paused.load() && data.initialized.load(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         /// Don't release the barrier |  | ||||||
|     } |  | ||||||
|     paused_state = paused; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// | ||||||
| ///                             SingleCore                                   /// | ///                             SingleCore                                   /// | ||||||
| /////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// | ||||||
| @@ -235,8 +201,9 @@ void CpuManager::SingleCoreRunSuspendThread() { | |||||||
|         auto core = kernel.GetCurrentHostThreadID(); |         auto core = kernel.GetCurrentHostThreadID(); | ||||||
|         auto& scheduler = *kernel.CurrentScheduler(); |         auto& scheduler = *kernel.CurrentScheduler(); | ||||||
|         Kernel::KThread* current_thread = scheduler.GetCurrentThread(); |         Kernel::KThread* current_thread = scheduler.GetCurrentThread(); | ||||||
|  |         current_thread->DisableDispatch(); | ||||||
|  |  | ||||||
|         Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); |         Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); | ||||||
|         ASSERT(scheduler.ContextSwitchPending()); |  | ||||||
|         ASSERT(core == kernel.GetCurrentHostThreadID()); |         ASSERT(core == kernel.GetCurrentHostThreadID()); | ||||||
|         scheduler.RescheduleCurrentCore(); |         scheduler.RescheduleCurrentCore(); | ||||||
|     } |     } | ||||||
| @@ -274,37 +241,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void CpuManager::SingleCorePause(bool paused) { |  | ||||||
|     if (!paused) { |  | ||||||
|         bool all_not_barrier = false; |  | ||||||
|         while (!all_not_barrier) { |  | ||||||
|             all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); |  | ||||||
|         } |  | ||||||
|         core_data[0].enter_barrier->Set(); |  | ||||||
|         if (paused_state.load()) { |  | ||||||
|             bool all_barrier = false; |  | ||||||
|             while (!all_barrier) { |  | ||||||
|                 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); |  | ||||||
|             } |  | ||||||
|             core_data[0].exit_barrier->Set(); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         /// Wait until all cores are paused. |  | ||||||
|         bool all_barrier = false; |  | ||||||
|         while (!all_barrier) { |  | ||||||
|             all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); |  | ||||||
|         } |  | ||||||
|         /// Don't release the barrier |  | ||||||
|     } |  | ||||||
|     paused_state = paused; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CpuManager::Pause(bool paused) { | void CpuManager::Pause(bool paused) { | ||||||
|     if (is_multicore) { |     std::scoped_lock lk{pause_lock}; | ||||||
|         MultiCorePause(paused); |  | ||||||
|     } else { |     if (pause_state == paused) { | ||||||
|         SingleCorePause(paused); |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Set the new state | ||||||
|  |     pause_state.store(paused); | ||||||
|  |  | ||||||
|  |     // Wake up any waiting threads | ||||||
|  |     pause_state.notify_all(); | ||||||
|  |  | ||||||
|  |     // Wait for all threads to successfully change state before returning | ||||||
|  |     pause_barrier->Sync(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { | void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { | ||||||
| @@ -320,27 +271,29 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { | |||||||
|     Common::SetCurrentThreadName(name.c_str()); |     Common::SetCurrentThreadName(name.c_str()); | ||||||
|     Common::SetCurrentThreadPriority(Common::ThreadPriority::High); |     Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||||||
|     auto& data = core_data[core]; |     auto& data = core_data[core]; | ||||||
|     data.enter_barrier = std::make_unique<Common::Event>(); |  | ||||||
|     data.exit_barrier = std::make_unique<Common::Event>(); |  | ||||||
|     data.host_context = Common::Fiber::ThreadToFiber(); |     data.host_context = Common::Fiber::ThreadToFiber(); | ||||||
|     data.is_running = false; |  | ||||||
|     data.initialized = true; |  | ||||||
|     const bool sc_sync = !is_async_gpu && !is_multicore; |     const bool sc_sync = !is_async_gpu && !is_multicore; | ||||||
|     bool sc_sync_first_use = sc_sync; |     bool sc_sync_first_use = sc_sync; | ||||||
|  |  | ||||||
|     // Cleanup |     // Cleanup | ||||||
|     SCOPE_EXIT({ |     SCOPE_EXIT({ | ||||||
|         data.host_context->Exit(); |         data.host_context->Exit(); | ||||||
|         data.enter_barrier.reset(); |  | ||||||
|         data.exit_barrier.reset(); |  | ||||||
|         data.initialized = false; |  | ||||||
|         MicroProfileOnThreadExit(); |         MicroProfileOnThreadExit(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     /// Running |     /// Running | ||||||
|     while (running_mode) { |     while (running_mode) { | ||||||
|         data.is_running = false; |         if (pause_state.load(std::memory_order_relaxed)) { | ||||||
|         data.enter_barrier->Wait(); |             // Wait for caller to acknowledge pausing | ||||||
|  |             pause_barrier->Sync(); | ||||||
|  |  | ||||||
|  |             // Wait until unpaused | ||||||
|  |             pause_state.wait(true, std::memory_order_relaxed); | ||||||
|  |  | ||||||
|  |             // Wait for caller to acknowledge unpausing | ||||||
|  |             pause_barrier->Sync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (sc_sync_first_use) { |         if (sc_sync_first_use) { | ||||||
|             system.GPU().ObtainContext(); |             system.GPU().ObtainContext(); | ||||||
|             sc_sync_first_use = false; |             sc_sync_first_use = false; | ||||||
| @@ -352,12 +305,7 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); |         auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); | ||||||
|         data.is_running = true; |  | ||||||
|         Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); |         Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); | ||||||
|         data.is_running = false; |  | ||||||
|         data.is_paused = true; |  | ||||||
|         data.exit_barrier->Wait(); |  | ||||||
|         data.is_paused = false; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -69,13 +69,11 @@ private: | |||||||
|     void MultiCoreRunGuestLoop(); |     void MultiCoreRunGuestLoop(); | ||||||
|     void MultiCoreRunIdleThread(); |     void MultiCoreRunIdleThread(); | ||||||
|     void MultiCoreRunSuspendThread(); |     void MultiCoreRunSuspendThread(); | ||||||
|     void MultiCorePause(bool paused); |  | ||||||
|  |  | ||||||
|     void SingleCoreRunGuestThread(); |     void SingleCoreRunGuestThread(); | ||||||
|     void SingleCoreRunGuestLoop(); |     void SingleCoreRunGuestLoop(); | ||||||
|     void SingleCoreRunIdleThread(); |     void SingleCoreRunIdleThread(); | ||||||
|     void SingleCoreRunSuspendThread(); |     void SingleCoreRunSuspendThread(); | ||||||
|     void SingleCorePause(bool paused); |  | ||||||
|  |  | ||||||
|     static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); |     static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); | ||||||
|  |  | ||||||
| @@ -83,16 +81,13 @@ private: | |||||||
|  |  | ||||||
|     struct CoreData { |     struct CoreData { | ||||||
|         std::shared_ptr<Common::Fiber> host_context; |         std::shared_ptr<Common::Fiber> host_context; | ||||||
|         std::unique_ptr<Common::Event> enter_barrier; |  | ||||||
|         std::unique_ptr<Common::Event> exit_barrier; |  | ||||||
|         std::atomic<bool> is_running; |  | ||||||
|         std::atomic<bool> is_paused; |  | ||||||
|         std::atomic<bool> initialized; |  | ||||||
|         std::jthread host_thread; |         std::jthread host_thread; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     std::atomic<bool> running_mode{}; |     std::atomic<bool> running_mode{}; | ||||||
|     std::atomic<bool> paused_state{}; |     std::atomic<bool> pause_state{}; | ||||||
|  |     std::unique_ptr<Common::Barrier> pause_barrier{}; | ||||||
|  |     std::mutex pause_lock{}; | ||||||
|  |  | ||||||
|     std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; |     std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -252,6 +252,7 @@ struct KernelCore::Impl { | |||||||
|                                                          core_id) |                                                          core_id) | ||||||
|                        .IsSuccess()); |                        .IsSuccess()); | ||||||
|             suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); |             suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); | ||||||
|  |             suspend_threads[core_id]->DisableDispatch(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1073,9 +1074,6 @@ void KernelCore::Suspend(bool in_suspention) { | |||||||
|             impl->suspend_threads[core_id]->SetState(state); |             impl->suspend_threads[core_id]->SetState(state); | ||||||
|             impl->suspend_threads[core_id]->SetWaitReasonForDebugging( |             impl->suspend_threads[core_id]->SetWaitReasonForDebugging( | ||||||
|                 ThreadWaitReasonForDebugging::Suspended); |                 ThreadWaitReasonForDebugging::Suspended); | ||||||
|             if (!should_suspend) { |  | ||||||
|                 impl->suspend_threads[core_id]->DisableDispatch(); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user