mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-01 02:29:59 -06:00
Merge pull request #12828 from JosJuice/unify-state-variables-2
Clean up Core::GetState
This commit is contained in:
@ -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<StateChangedCallbackFunc> s_on_state_changed_callbacks;
|
||||
|
||||
@ -114,6 +110,10 @@ static std::atomic<double> s_last_actual_emulation_speed{1.0};
|
||||
static bool s_frame_step = false;
|
||||
static std::atomic<bool> 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<State> s_state = State::Uninitialized;
|
||||
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
static std::unique_ptr<MemoryWatcher> 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,18 @@ 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 IsUninitialized(Core::System& system)
|
||||
{
|
||||
return s_state.load() == State::Uninitialized;
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
@ -236,7 +242,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
|
||||
{
|
||||
if (s_emu_thread.joinable())
|
||||
{
|
||||
if (IsRunning(system))
|
||||
if (!IsUninitialized(system))
|
||||
{
|
||||
PanicAlertFmtT("Emu Thread already running");
|
||||
return false;
|
||||
@ -262,7 +268,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> 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 +287,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 +398,11 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& 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 +434,6 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
||||
s_memory_watcher.reset();
|
||||
#endif
|
||||
|
||||
s_is_started = false;
|
||||
|
||||
if (exception_handler)
|
||||
EMM::UninstallExceptionHandler();
|
||||
|
||||
@ -453,12 +459,15 @@ static void FifoPlayerThread(Core::System& system, const std::optional<std::stri
|
||||
if (auto cpu_core = system.GetFifoPlayer().GetCPUCore())
|
||||
{
|
||||
system.GetPowerPC().InjectExternalCPUCore(cpu_core.get());
|
||||
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);
|
||||
|
||||
CPUSetInitialExecutionState();
|
||||
system.GetCPU().Run();
|
||||
|
||||
s_is_started = false;
|
||||
system.GetPowerPC().InjectExternalCPUCore(nullptr);
|
||||
system.GetFifoPlayer().Close();
|
||||
}
|
||||
@ -479,10 +488,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> 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 +563,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> 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 +606,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> 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 +701,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> 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 +732,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 +790,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 +1017,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);
|
||||
|
Reference in New Issue
Block a user