Core: Store current state in less places
Core::GetState reads from four different pieces of state: s_is_stopping, s_hardware_initialized, s_is_booting, and CPUManager::IsStepping. I'm keeping that last one as is for now because there's code in Dolphin that sets it directly, but we can unify the other three to make things easier to reason about. This commit also gets rid of s_is_started. This was previously used in Core::IsRunningAndStarted to ensure true wouldn't be returned until the CPU thread was started, but it wasn't used in Core::GetState, so Core::GetState would happily return State::Running after we had initialized the hardware but before we had initialized the CPU thread. As far as I know, there are no callers that have any real need to know whether the boot process is currently initializing the hardware or the CPU thread. Perhaps once upon a time there was a desire to make the apploader debuggable, but a long time has passed without anyone stepping up to implement it, and the way CBoot::RunApploader is implemented makes it rather difficult. So this commit makes all the functions in Core.cpp consider the core to still be starting until the CPU thread is started.
This commit is contained in:
parent
04c246d11f
commit
962230f91e
|
@ -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<jboolean>(Core::IsRunningAndStarted());
|
||||
return static_cast<jboolean>(Core::IsRunning(Core::System::GetInstance()));
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,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<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 +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<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 +429,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 +454,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 +483,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 +558,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 +601,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 +696,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 +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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -854,7 +854,7 @@ static void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_
|
|||
|
||||
void LoadAs(Core::System& system, const std::string& filename)
|
||||
{
|
||||
if (!Core::IsRunning(system))
|
||||
if (!Core::IsRunningOrStarting(system))
|
||||
return;
|
||||
|
||||
if (NetPlay::IsNetPlayRunning())
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue