diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 9fcf1b33a3..66a0694a94 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -279,7 +279,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunnin JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndStarted(JNIEnv*, jclass) { - return static_cast(Core::IsRunningAndStarted()); + return static_cast(Core::IsRunning(Core::System::GetInstance())); } JNIEXPORT jboolean JNICALL diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index e3c1ec1d15..8882076daa 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -185,22 +185,22 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri m_title_description = title_database.Describe(m_gametdb_id, language); NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description); Host_TitleChanged(); - if (Core::IsRunning(system)) - { + + const bool is_running_or_starting = Core::IsRunningOrStarting(system); + if (is_running_or_starting) Core::UpdateTitle(system); - } Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision)); Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision)); - if (Core::IsRunning(system)) + if (is_running_or_starting) DolphinAnalytics::Instance().ReportGameStart(); } void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard) { auto& system = guard.GetSystem(); - if (!Core::IsRunning(system)) + if (!Core::IsRunningOrStarting(system)) return; auto& ppc_symbol_db = system.GetPPCSymbolDB(); diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index f28a41f836..58423377be 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -101,10 +101,6 @@ namespace Core static bool s_wants_determinism; // Declarations and definitions -static bool s_is_stopping = false; -static bool s_hardware_initialized = false; -static bool s_is_started = false; -static Common::Flag s_is_booting; static std::thread s_emu_thread; static std::vector s_on_state_changed_callbacks; @@ -114,6 +110,10 @@ static std::atomic s_last_actual_emulation_speed{1.0}; static bool s_frame_step = false; static std::atomic s_stop_frame_step; +// The value Paused is never stored in this variable. The core is considered to be in +// the Paused state if this variable is Running and the CPU reports that it's stepping. +static std::atomic s_state = State::Uninitialized; + #ifdef USE_MEMORYWATCHER static std::unique_ptr s_memory_watcher; #endif @@ -190,7 +190,7 @@ std::string StopMessage(bool main_thread, std::string_view message) void DisplayMessage(std::string message, int time_in_ms) { - if (!IsRunning(Core::System::GetInstance())) + if (!IsRunningOrStarting(Core::System::GetInstance())) return; // Actually displaying non-ASCII could cause things to go pear-shaped @@ -202,12 +202,13 @@ void DisplayMessage(std::string message, int time_in_ms) bool IsRunning(Core::System& system) { - return (GetState(system) != State::Uninitialized || s_hardware_initialized) && !s_is_stopping; + return s_state.load() == State::Running; } -bool IsRunningAndStarted() +bool IsRunningOrStarting(Core::System& system) { - return s_is_started && !s_is_stopping; + const State state = s_state.load(); + return state == State::Running || state == State::Starting; } bool IsCPUThread() @@ -262,7 +263,7 @@ bool Init(Core::System& system, std::unique_ptr boot, const Wind g_video_backend->PrepareWindow(prepared_wsi); // Start the emu thread - s_is_booting.Set(); + s_state.store(State::Starting); s_emu_thread = std::thread(EmuThread, std::ref(system), std::move(boot), prepared_wsi); return true; } @@ -281,15 +282,13 @@ static void ResetRumble() // Called from GUI thread void Stop(Core::System& system) // - Hammertime! { - if (const State state = GetState(system); - state == State::Stopping || state == State::Uninitialized) - { + const State state = s_state.load(); + if (state == State::Stopping || state == State::Uninitialized) return; - } AchievementManager::GetInstance().CloseGame(); - s_is_stopping = true; + s_state.store(State::Stopping); CallOnStateChangedCallbacks(State::Stopping); @@ -394,7 +393,11 @@ static void CpuThread(Core::System& system, const std::optional& sa File::Delete(*savestate_path); } - s_is_started = true; + // If s_state is Starting, change it to Running. But if it's already been set to Stopping + // by the host thread, don't change it. + State expected = State::Starting; + s_state.compare_exchange_strong(expected, State::Running); + { #ifndef _WIN32 std::string gdb_socket = Config::Get(Config::MAIN_GDB_SOCKET); @@ -426,8 +429,6 @@ static void CpuThread(Core::System& system, const std::optional& sa s_memory_watcher.reset(); #endif - s_is_started = false; - if (exception_handler) EMM::UninstallExceptionHandler(); @@ -453,12 +454,15 @@ static void FifoPlayerThread(Core::System& system, const std::optional boot { CallOnStateChangedCallbacks(State::Starting); Common::ScopeGuard flag_guard{[] { - s_is_booting.Clear(); - s_is_started = false; - s_is_stopping = false; - s_wants_determinism = false; + s_state.store(State::Uninitialized); CallOnStateChangedCallbacks(State::Uninitialized); @@ -557,8 +558,6 @@ static void EmuThread(Core::System& system, std::unique_ptr boot NetPlay::IsNetPlayRunning() ? &(boot_session_data.GetNetplaySettings()->sram) : nullptr); Common::ScopeGuard hw_guard{[&system] { - // We must set up this flag before executing HW::Shutdown() - s_hardware_initialized = false; INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Shutting down HW")); HW::Shutdown(system); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "HW shutdown")); @@ -602,10 +601,6 @@ static void EmuThread(Core::System& system, std::unique_ptr boot AudioCommon::PostInitSoundStream(system); - // The hardware is initialized. - s_hardware_initialized = true; - s_is_booting.Clear(); - // Set execution state to known values (CPU/FIFO/Audio Paused) system.GetCPU().Break(); @@ -701,7 +696,7 @@ static void EmuThread(Core::System& system, std::unique_ptr boot void SetState(Core::System& system, State state, bool report_state_change) { // State cannot be controlled until the CPU Thread is operational - if (!IsRunningAndStarted()) + if (s_state.load() != State::Running) return; switch (state) @@ -732,21 +727,11 @@ void SetState(Core::System& system, State state, bool report_state_change) State GetState(Core::System& system) { - if (s_is_stopping) - return State::Stopping; - - if (s_hardware_initialized) - { - if (system.GetCPU().IsStepping()) - return State::Paused; - - return State::Running; - } - - if (s_is_booting.IsSet()) - return State::Starting; - - return State::Uninitialized; + const State state = s_state.load(); + if (state == State::Running && system.GetCPU().IsStepping()) + return State::Paused; + else + return state; } static std::string GenerateScreenshotFolderPath() @@ -800,7 +785,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl { // WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread - if (!IsRunningAndStarted()) + if (!IsRunning(system)) return true; bool was_unpaused = true; @@ -1027,13 +1012,12 @@ void HostDispatchJobs(Core::System& system) HostJob job = std::move(s_host_jobs_queue.front()); s_host_jobs_queue.pop(); - // NOTE: Memory ordering is important. The booting flag needs to be - // checked first because the state transition is: - // Core::State::Uninitialized: s_is_booting -> s_hardware_initialized - // We need to check variables in the same order as the state - // transition, otherwise we race and get transient failures. - if (!job.run_after_stop && !s_is_booting.IsSet() && !IsRunning(system)) - continue; + if (!job.run_after_stop) + { + const State state = s_state.load(); + if (state == State::Stopping || state == State::Uninitialized) + continue; + } guard.unlock(); job.job(system); diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 980d336750..e6c440566c 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -135,8 +135,8 @@ void UndeclareAsHostThread(); std::string StopMessage(bool main_thread, std::string_view message); bool IsRunning(Core::System& system); -bool IsRunningAndStarted(); // is running and the CPU loop has been entered -bool IsCPUThread(); // this tells us whether we are the CPU thread. +bool IsRunningOrStarting(Core::System& system); +bool IsCPUThread(); // this tells us whether we are the CPU thread. bool IsGPUThread(); bool IsHostThread(); diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.cpp b/Source/Core/Core/Debugger/PPCDebugInterface.cpp index a003e98156..9389fe76d8 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.cpp +++ b/Source/Core/Core/Debugger/PPCDebugInterface.cpp @@ -349,7 +349,7 @@ u32 PPCDebugInterface::ReadInstruction(const Core::CPUThreadGuard& guard, u32 ad bool PPCDebugInterface::IsAlive() const { - return Core::IsRunningAndStarted(); + return Core::IsRunning(m_system); } bool PPCDebugInterface::IsBreakpoint(u32 address) const diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 6bd894dd27..d04a3ef889 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -112,9 +112,9 @@ ESCore::~ESCore() = default; ESDevice::ESDevice(EmulationKernel& ios, ESCore& core, const std::string& device_name) : EmulationDevice(ios, device_name), m_core(core) { - if (Core::IsRunningAndStarted()) + auto& system = ios.GetSystem(); + if (Core::IsRunning(system)) { - auto& system = ios.GetSystem(); auto& core_timing = system.GetCoreTiming(); core_timing.RemoveEvent(s_finish_init_event); core_timing.ScheduleEvent(GetESBootTicks(ios.GetVersion()), s_finish_init_event); @@ -446,7 +446,7 @@ bool ESDevice::LaunchPPCTitle(u64 title_id) } const u64 required_ios = tmd.GetIOSId(); - if (!Core::IsRunningAndStarted()) + if (!Core::IsRunning(system)) return LaunchTitle(required_ios, HangPPC::Yes); core_timing.RemoveEvent(s_reload_ios_for_ppc_launch_event); core_timing.ScheduleEvent(ticks, s_reload_ios_for_ppc_launch_event, required_ios); @@ -475,7 +475,7 @@ bool ESDevice::LaunchPPCTitle(u64 title_id) return false; m_pending_ppc_boot_content_path = m_core.GetContentPath(tmd.GetTitleId(), content); - if (!Core::IsRunningAndStarted()) + if (!Core::IsRunning(system)) return BootstrapPPC(); INFO_LOG_FMT(ACHIEVEMENTS, diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 0e046b8534..d15eb9317d 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -518,7 +518,7 @@ bool EmulationKernel::BootIOS(const u64 ios_title_id, HangPPC hang_ppc, if (hang_ppc == HangPPC::Yes) ResetAndPausePPC(m_system); - if (Core::IsRunningAndStarted()) + if (Core::IsRunning(m_system)) { m_system.GetCoreTiming().ScheduleEvent(GetIOSBootTicks(GetVersion()), s_event_finish_ios_boot, ios_title_id); diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 47ad3c8c97..325381f0e9 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -536,7 +536,7 @@ bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers, m_bongos |= (1 << i); } - if (Core::IsRunningAndStarted()) + if (Core::IsRunning(m_system)) { const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav"; if (File::Exists(save_path)) @@ -551,7 +551,7 @@ bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers, } // Wiimotes cause desync issues if they're not reset before launching the game - if (!Core::IsRunningAndStarted()) + if (!Core::IsRunning(m_system)) { // This will also reset the Wiimotes for GameCube games, but that shouldn't do anything Wiimote::ResetAllWiimotes(); @@ -1339,7 +1339,7 @@ void MovieManager::EndPlayInput(bool cont) { // We can be called by EmuThread during boot (CPU::State::PowerDown) auto& cpu = m_system.GetCPU(); - const bool was_running = Core::IsRunningAndStarted() && !cpu.IsStepping(); + const bool was_running = Core::IsRunning(m_system) && !cpu.IsStepping(); if (was_running && Config::Get(Config::MAIN_MOVIE_PAUSE_MOVIE)) cpu.Break(); m_rerecords = 0; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index adc1cbd57a..11c8e9660e 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -854,7 +854,7 @@ static void LoadFileStateData(const std::string& filename, std::vector& ret_ void LoadAs(Core::System& system, const std::string& filename) { - if (!Core::IsRunning(system)) + if (!Core::IsRunningOrStarting(system)) return; if (NetPlay::IsNetPlayRunning()) diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 10f7540ca7..0a6d13e6c0 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -159,7 +159,8 @@ void HotkeyScheduler::Run() if (!HotkeyManagerEmu::IsEnabled()) continue; - if (Core::GetState(Core::System::GetInstance()) != Core::State::Stopping) + Core::System& system = Core::System::GetInstance(); + if (Core::GetState(system) != Core::State::Stopping) { // Obey window focus (config permitting) before checking hotkeys. Core::UpdateInputGate(Config::Get(Config::MAIN_FOCUSED_HOTKEYS)); @@ -187,7 +188,7 @@ void HotkeyScheduler::Run() if (IsHotkey(HK_EXIT)) emit ExitHotkey(); - if (!Core::IsRunningAndStarted()) + if (!Core::IsRunning(system)) { // Only check for Play Recording hotkey when no game is running if (IsHotkey(HK_PLAY_RECORDING)) diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 67860c464a..f62a7496e7 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1882,7 +1882,7 @@ void MainWindow::OnStartRecording() { auto& system = Core::System::GetInstance(); auto& movie = system.GetMovie(); - if ((!Core::IsRunningAndStarted() && Core::IsRunning(system)) || movie.IsRecordingInput() || + if (Core::GetState(system) == Core::State::Starting || movie.IsRecordingInput() || movie.IsPlayingInput()) { return; diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index 597ac78e6c..d88a17e3d3 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -513,7 +513,7 @@ bool RenderWidget::event(QEvent* event) void RenderWidget::PassEventToPresenter(const QEvent* event) { - if (!Core::IsRunningAndStarted()) + if (!Core::IsRunning(Core::System::GetInstance())) return; switch (event->type()) diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 0266cb9c19..efa159795c 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -66,7 +66,7 @@ void VideoConfig::Refresh() CPUThreadConfigCallback::AddConfigChangedCallback([]() { auto& system = Core::System::GetInstance(); - const bool lock_gpu_thread = Core::IsRunningAndStarted(); + const bool lock_gpu_thread = Core::IsRunning(system); if (lock_gpu_thread) system.GetFifo().PauseAndLock(true, false);