From 3455a0d15184076a2d010e88f0870523d79fd345 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 20 Aug 2024 11:50:52 +1000 Subject: [PATCH] System: Fix lockup with runahead enabled --- src/core/cpu_core.cpp | 2 +- src/core/cpu_core.h | 2 +- src/core/system.cpp | 33 ++++++++++++++------------------- src/core/timing_event.cpp | 12 ++++++++++-- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index ffc2ea3fa..5335cff52 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -2016,7 +2016,7 @@ bool CPU::UpdateDebugDispatcherFlag() return true; } -void CPU::ExitExecution() +[[noreturn]] void CPU::ExitExecution() { // can't exit while running events without messing things up DebugAssert(!TimingEvents::IsRunningEvents()); diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index 019f04205..e65e22566 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -145,7 +145,7 @@ void ExecutionModeChanged(); void Execute(); // Forces an early exit from the CPU dispatcher. -void ExitExecution(); +[[noreturn]] void ExitExecution(); ALWAYS_INLINE static Registers& GetRegs() { diff --git a/src/core/system.cpp b/src/core/system.cpp index 217103181..7adbcd2c1 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -204,7 +204,7 @@ static u32 CompressAndWriteStateData(std::FILE* fp, std::span src, Sav static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state); static bool IsExecutionInterrupted(); -static void ExitExecution(); +static void CheckForAndExitExecution(); static void SetRewinding(bool enabled); static bool SaveRewindState(); @@ -557,15 +557,20 @@ bool System::IsRunning() return s_state == State::Running; } -bool System::IsExecutionInterrupted() +ALWAYS_INLINE bool System::IsExecutionInterrupted() { return s_state != State::Running || s_system_interrupted; } -void System::ExitExecution() +ALWAYS_INLINE_RELEASE void System::CheckForAndExitExecution() { - TimingEvents::CancelRunningEvent(); - CPU::ExitExecution(); + if (IsExecutionInterrupted()) [[unlikely]] + { + s_system_interrupted = false; + + TimingEvents::CancelRunningEvent(); + CPU::ExitExecution(); + } } bool System::IsPaused() @@ -2048,13 +2053,7 @@ void System::FrameDone() Host::PumpMessagesOnCPUThread(); InputManager::PollSources(); g_gpu->RestoreDeviceContext(); - - if (IsExecutionInterrupted()) - { - s_system_interrupted = false; - ExitExecution(); - return; - } + CheckForAndExitExecution(); } if (DoRunahead()) @@ -2159,13 +2158,7 @@ void System::FrameDone() { Host::PumpMessagesOnCPUThread(); InputManager::PollSources(); - - if (IsExecutionInterrupted()) - { - s_system_interrupted = false; - ExitExecution(); - return; - } + CheckForAndExitExecution(); } g_gpu->RestoreDeviceContext(); @@ -4831,6 +4824,8 @@ bool System::DoRunahead() // we don't want to save the frame we just loaded. but we are "one frame ahead", because the frame we just tossed // was never saved, so return but don't decrement the counter + InterruptExecution(); + CheckForAndExitExecution(); return true; } else if (s_runahead_replay_frames == 0) diff --git a/src/core/timing_event.cpp b/src/core/timing_event.cpp index c9cd621d6..caf980f1a 100644 --- a/src/core/timing_event.cpp +++ b/src/core/timing_event.cpp @@ -384,7 +384,7 @@ void TimingEvents::CommitLeftoverTicks() { #ifdef _DEBUG if (s_state.event_run_tick_counter > s_state.global_tick_counter) - WARNING_LOG("Late-running {} ticks before execution", s_state.event_run_tick_counter - s_state.global_tick_counter); + DEV_LOG("Late-running {} ticks before execution", s_state.event_run_tick_counter - s_state.global_tick_counter); #endif CommitGlobalTicks(s_state.event_run_tick_counter); @@ -441,6 +441,7 @@ bool TimingEvents::DoState(StateWrapper& sw) } DEBUG_LOG("Loaded {} events from save state.", event_count); + s_state.current_event = nullptr; // Add pending ticks to the CPU, this'll happen if we saved state when we weren't paused. const TickCount pending_ticks = @@ -489,7 +490,12 @@ bool TimingEvents::DoState(StateWrapper& sw) } DEBUG_LOG("Loaded {} events from save state.", event_count); + + // Even if we're actually running an event, we don't want to set it to a new counter. + s_state.current_event = nullptr; + SortEvents(); + UpdateCPUDowncount(); } else { @@ -498,7 +504,9 @@ bool TimingEvents::DoState(StateWrapper& sw) for (TimingEvent* event = s_state.active_events_head; event; event = event->next) { sw.Do(&event->m_name); - sw.Do(&event->m_next_run_time); + GlobalTicks next_run_time = + (s_state.current_event == event) ? s_state.current_event_next_run_time : event->m_next_run_time; + sw.Do(&next_run_time); sw.Do(&event->m_last_run_time); sw.Do(&event->m_period); sw.Do(&event->m_interval);