From f183472ff0044a3a073ab66623ddfc95c266007a Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 19 May 2025 11:35:50 +0200 Subject: [PATCH 1/3] Core: Let any thread call previously host-thread-only functions By letting threads other than the host thread use things like CPUThreadGuard, we can do a significant cleanup in AchievementsManager in a later commit of this pull request. Note: Some functions still can't be called from the CPU thread (or threads the CPU thread might block on, like the GPU thread), but can be called from any other thread. --- Source/Core/Core/Core.cpp | 172 +++++++++++++++++++++++--------------- Source/Core/Core/Core.h | 14 ++-- 2 files changed, 110 insertions(+), 76 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index ccb1ad68bd..598450726f 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -105,6 +105,11 @@ static bool s_is_throttler_temp_disabled = false; static bool s_frame_step = false; static std::atomic s_stop_frame_step; +// Threads other than the CPU thread must hold this when taking on the role of the CPU thread. +// The CPU thread is not required to hold this when doing normal work, but must hold it if writing +// to s_state. +static std::recursive_mutex s_core_mutex; + // 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; @@ -224,6 +229,8 @@ bool WantsDeterminism() // BootManager.cpp bool Init(Core::System& system, std::unique_ptr boot, const WindowSystemInfo& wsi) { + std::lock_guard lock(s_core_mutex); + if (s_emu_thread.joinable()) { if (!IsUninitialized(system)) @@ -271,16 +278,20 @@ static void ResetRumble() // Called from GUI thread void Stop(Core::System& system) // - Hammertime! { - const State state = s_state.load(); - if (state == State::Stopping || state == State::Uninitialized) - return; + { + std::lock_guard lock(s_core_mutex); - AchievementManager::GetInstance().CloseGame(); + const State state = s_state.load(); + if (state == State::Stopping || state == State::Uninitialized) + return; - s_state.store(State::Stopping); + s_state.store(State::Stopping); + } NotifyStateChanged(State::Stopping); + AchievementManager::GetInstance().CloseGame(); + // Dump left over jobs HostDispatchJobs(system); @@ -337,7 +348,7 @@ void UndeclareAsHostThread() static void CPUSetInitialExecutionState(bool force_paused = false) { // The CPU starts in stepping state, and will wait until a new state is set before executing. - // SetState must be called on the host thread, so we defer it for later. + // SetState isn't safe to call from the CPU thread, so we ask the host thread to call it. QueueHostJob([force_paused](Core::System& system) { bool paused = SConfig::GetInstance().bBootToPause || force_paused; SetState(system, paused ? State::Paused : State::Running, true, true); @@ -351,6 +362,7 @@ static void CPUSetInitialExecutionState(bool force_paused = false) static void CpuThread(Core::System& system, const std::optional& savestate_path, bool delete_savestate) { + std::unique_lock core_lock(s_core_mutex); DeclareAsCPUThread(); if (system.IsDualCoreMode()) @@ -381,7 +393,7 @@ static void CpuThread(Core::System& system, const std::optional& sa } // 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. + // because another thread called Stop, don't change it. State expected = State::Starting; s_state.compare_exchange_strong(expected, State::Running); @@ -409,6 +421,8 @@ static void CpuThread(Core::System& system, const std::optional& sa } } + core_lock.unlock(); + // Enter CPU run loop. When we leave it - we are done. system.GetCPU().Run(); @@ -440,14 +454,19 @@ static void FifoPlayerThread(Core::System& system, const std::optional boot { NotifyStateChanged(State::Starting); Common::ScopeGuard flag_guard{[] { - s_state.store(State::Uninitialized); + { + std::lock_guard lock(s_core_mutex); + s_state.store(State::Uninitialized); + } NotifyStateChanged(State::Uninitialized); @@ -674,35 +696,39 @@ static void EmuThread(Core::System& system, std::unique_ptr boot void SetState(Core::System& system, State state, bool report_state_change, bool override_achievement_restrictions) { - // State cannot be controlled until the CPU Thread is operational - if (s_state.load() != State::Running) - return; + { + std::lock_guard lock(s_core_mutex); - switch (state) - { - case State::Paused: -#ifdef USE_RETRO_ACHIEVEMENTS - if (!override_achievement_restrictions && !AchievementManager::GetInstance().CanPause()) + // State cannot be controlled until the CPU Thread is operational + if (s_state.load() != State::Running) return; -#endif // USE_RETRO_ACHIEVEMENTS - // NOTE: GetState() will return State::Paused immediately, even before anything has - // stopped (including the CPU). - system.GetCPU().SetStepping(true); // Break - Wiimote::Pause(); - ResetRumble(); + + switch (state) + { + case State::Paused: #ifdef USE_RETRO_ACHIEVEMENTS - AchievementManager::GetInstance().DoIdle(); + if (!override_achievement_restrictions && !AchievementManager::GetInstance().CanPause()) + return; #endif // USE_RETRO_ACHIEVEMENTS - break; - case State::Running: - { - system.GetCPU().SetStepping(false); - Wiimote::Resume(); - break; - } - default: - PanicAlertFmt("Invalid state"); - break; + // NOTE: GetState() will return State::Paused immediately, even before anything has + // stopped (including the CPU). + system.GetCPU().SetStepping(true); // Break + Wiimote::Pause(); + ResetRumble(); +#ifdef USE_RETRO_ACHIEVEMENTS + AchievementManager::GetInstance().DoIdle(); +#endif // USE_RETRO_ACHIEVEMENTS + break; + case State::Running: + { + system.GetCPU().SetStepping(false); + Wiimote::Resume(); + break; + } + default: + PanicAlertFmt("Invalid state"); + break; + } } // Certain callers only change the state momentarily. Sending a callback for them causes @@ -773,39 +799,44 @@ void SaveScreenShot(std::string_view name) static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock) { - // WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread - - if (!IsRunning(system)) - return true; - bool was_unpaused = true; + if (do_lock) + s_core_mutex.lock(); + + if (IsRunning(system)) { - // first pause the CPU - // This acquires a wrapper mutex and converts the current thread into - // a temporary replacement CPU Thread. - was_unpaused = system.GetCPU().PauseAndLock(true); + if (do_lock) + { + // first pause the CPU + // This acquires a wrapper mutex and converts the current thread into + // a temporary replacement CPU Thread. + was_unpaused = system.GetCPU().PauseAndLock(true); + } + + // audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle). + system.GetDSP().GetDSPEmulator()->PauseAndLock(do_lock); + + // video has to come after CPU, because CPU thread can wait for video thread + // (s_efbAccessRequested). + system.GetFifo().PauseAndLock(do_lock, false); + + ResetRumble(); + + // CPU is unlocked last because CPU::PauseAndLock contains the synchronization + // mechanism that prevents CPU::Break from racing. + if (!do_lock) + { + // The CPU is responsible for managing the Audio and FIFO state so we use its + // mechanism to unpause them. If we unpaused the systems above when releasing + // the locks then they could call CPU::Break which would require detecting it + // and re-pausing with CPU::SetStepping. + was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true); + } } - // audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle). - system.GetDSP().GetDSPEmulator()->PauseAndLock(do_lock); - - // video has to come after CPU, because CPU thread can wait for video thread - // (s_efbAccessRequested). - system.GetFifo().PauseAndLock(do_lock, false); - - ResetRumble(); - - // CPU is unlocked last because CPU::PauseAndLock contains the synchronization - // mechanism that prevents CPU::Break from racing. if (!do_lock) - { - // The CPU is responsible for managing the Audio and FIFO state so we use its - // mechanism to unpause them. If we unpaused the systems above when releasing - // the locks then they could call CPU::Break which would require detecting it - // and re-pausing with CPU::SetStepping. - was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true); - } + s_core_mutex.unlock(); return was_unpaused; } @@ -813,8 +844,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction function, bool wait_for_completion) { - // If the CPU thread is not running, assume there is no active CPU thread we can race against. - if (!IsRunning(system) || IsCPUThread()) + if (IsCPUThread()) { function(); return; @@ -956,6 +986,8 @@ void NotifyStateChanged(Core::State state) void UpdateWantDeterminism(Core::System& system, bool initial) { + const Core::CPUThreadGuard guard(system); + // For now, this value is not itself configurable. Instead, individual // settings that depend on it, such as GPU determinism mode. should have // override options for testing, @@ -964,7 +996,6 @@ void UpdateWantDeterminism(Core::System& system, bool initial) { NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false"); - const Core::CPUThreadGuard guard(system); s_wants_determinism = new_want_determinism; const auto ios = system.GetIOS(); if (ios) @@ -1026,6 +1057,9 @@ void DoFrameStep(Core::System& system) OSD::AddMessage("Frame stepping is disabled in RetroAchievements hardcore mode"); return; } + + std::lock_guard lock(s_core_mutex); + if (GetState(system) == State::Paused) { // if already paused, frame advance for 1 frame diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 68a402772a..72627d0038 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -91,10 +91,9 @@ enum class ConsoleType : u32 ReservedTDEVSystem = 0x20000007, }; -// This is an RAII alternative to using PauseAndLock. If constructed from the host thread, the CPU -// thread is paused, and the current thread temporarily becomes the CPU thread. If constructed from -// the CPU thread, nothing special happens. This should only be constructed on the CPU thread or the -// host thread. +// This is an RAII alternative to using PauseAndLock. If constructed from any thread other than the +// CPU thread, the CPU thread is paused, and the current thread temporarily becomes the CPU thread. +// If constructed from the CPU thread, nothing special happens. // // Some functions use a parameter of this type to indicate that the function should only be called // from the CPU thread. If the parameter is a pointer, the function has a fallback for being called @@ -118,6 +117,8 @@ private: bool m_was_unpaused = false; }; +// These three are normally called from the Host thread. However, they can be called from any thread +// that isn't launched by the emulator core. bool Init(Core::System& system, std::unique_ptr boot, const WindowSystemInfo& wsi); void Stop(Core::System& system); void Shutdown(Core::System& system); @@ -144,7 +145,8 @@ bool IsHostThread(); bool WantsDeterminism(); -// [NOT THREADSAFE] For use by Host only +// SetState can't be called by the CPU thread, but can be called by any thread that isn't launched +// by the emulator core. void SetState(Core::System& system, State state, bool report_state_change = true, bool override_achievement_restrictions = false); State GetState(Core::System& system); @@ -159,7 +161,6 @@ void FrameUpdateOnCPUThread(); void OnFrameEnd(Core::System& system); // Run a function on the CPU thread, asynchronously. -// This is only valid to call from the host thread, since it uses PauseAndLock() internally. void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction function, bool wait_for_completion); @@ -171,7 +172,6 @@ int AddOnStateChangedCallback(StateChangedCallbackFunc callback); bool RemoveOnStateChangedCallback(int* handle); void NotifyStateChanged(Core::State state); -// Run on the Host thread when the factors change. [NOT THREADSAFE] void UpdateWantDeterminism(Core::System& system, bool initial = false); // Queue an arbitrary function to asynchronously run once on the Host thread later. From 11f3d60ae8bf4fa00362285fff4090bc23e06d70 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 19 May 2025 11:46:43 +0200 Subject: [PATCH 2/3] RetroAchievements: Remove MemoryPeeker/MemoryPoker's copying approach This makes the code simpler, and saves us from the slow operation of copying the all of RAM on every frame when RAIntegration is enabled. --- Source/Core/Core/AchievementManager.cpp | 80 +------------------------ Source/Core/Core/AchievementManager.h | 3 - 2 files changed, 2 insertions(+), 81 deletions(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index be971055ff..3d79ec3cc5 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -65,7 +65,7 @@ void AchievementManager::Init(void* hwnd) { { std::lock_guard lg{m_lock}; - m_client = rc_client_create(MemoryVerifier, Request); + m_client = rc_client_create(MemoryPeeker, Request); } std::string host_url = Config::Get(Config::RA_HOST_URL); if (!host_url.empty()) @@ -180,7 +180,6 @@ void AchievementManager::LoadGame(const DiscIO::Volume* volume) } else { - rc_client_set_read_memory_function(m_client, MemoryVerifier); rc_client_begin_load_game(m_client, "", LoadGameCallback, NULL); } return; @@ -220,7 +219,6 @@ void AchievementManager::LoadGame(const DiscIO::Volume* volume) else { u32 console_id = FindConsoleID(volume->GetVolumeType()); - rc_client_set_read_memory_function(m_client, MemoryVerifier); rc_client_begin_identify_and_load_game(m_client, console_id, "", NULL, 0, LoadGameCallback, NULL); } @@ -330,22 +328,6 @@ void AchievementManager::DoFrame() if (!(IsGameLoaded() || m_dll_found) || !Core::IsCPUThread()) return; { -#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION - if (m_dll_found) - { - std::lock_guard lg{m_memory_lock}; - Core::System* system = m_system.load(std::memory_order_acquire); - if (!system) - return; - Core::CPUThreadGuard thread_guard(*system); - u32 mem2_size = (system->IsWii()) ? system->GetMemory().GetExRamSizeReal() : 0; - if (m_cloned_memory.size() != MEM1_SIZE + mem2_size) - m_cloned_memory.resize(MEM1_SIZE + mem2_size); - system->GetMemory().CopyFromEmu(m_cloned_memory.data(), 0, MEM1_SIZE); - if (mem2_size > 0) - system->GetMemory().CopyFromEmu(m_cloned_memory.data() + MEM1_SIZE, MEM2_START, mem2_size); - } -#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION std::lock_guard lg{m_lock}; rc_client_do_frame(m_client); } @@ -1019,7 +1001,6 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message, OSD::Color::RED); } - rc_client_set_read_memory_function(instance.m_client, MemoryPeeker); instance.FetchGameBadges(); instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release); instance.m_update_callback({.all = true}); @@ -1297,53 +1278,11 @@ void AchievementManager::Request(const rc_api_request_t* request, }); } -// Currently, when rc_client calls the memory peek method provided in its constructor (or in -// rc_client_set_read_memory_function) it will do so on the thread that calls DoFrame, which is -// currently the host thread, with one exception: an asynchronous callback in the load game process. -// This is done to validate/invalidate each memory reference in the downloaded assets, mark assets -// as unsupported, and notify the player upon startup that there are unsupported assets and how -// many. As such, all that call needs to do is return the number of bytes that can be read with this -// call. As only the CPU and host threads are allowed to read from memory, I provide a separate -// method for this verification. In lieu of a more convenient set of steps, I provide MemoryVerifier -// to rc_client at construction, and in the Load Game callback, after the verification has been -// complete, I call rc_client_set_read_memory_function to switch to the usual MemoryPeeker for all -// future synchronous calls. -u32 AchievementManager::MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client) -{ - auto& system = Core::System::GetInstance(); - u32 mem2_size = system.GetMemory().GetExRamSizeReal(); - if (address < MEM1_SIZE + mem2_size) - return std::min(MEM1_SIZE + mem2_size - address, num_bytes); - return 0; -} - u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client) { if (buffer == nullptr) return 0u; -#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION - auto& instance = AchievementManager::GetInstance(); - if (instance.m_dll_found) - { - std::lock_guard lg{instance.m_memory_lock}; - if (u64(address) + num_bytes > instance.m_cloned_memory.size()) - { - ERROR_LOG_FMT(ACHIEVEMENTS, - "Attempt to read past memory size: size {} address {} write length {}", - instance.m_cloned_memory.size(), address, num_bytes); - return 0; - } - std::copy(instance.m_cloned_memory.begin() + address, - instance.m_cloned_memory.begin() + address + num_bytes, buffer); - return num_bytes; - } -#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION auto& system = Core::System::GetInstance(); - if (!(Core::IsHostThread() || Core::IsCPUThread())) - { - ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread"); - return 0; - } Core::CPUThreadGuard thread_guard(system); if (address > MEM1_SIZE) address += (MEM2_START - MEM1_SIZE); @@ -1567,32 +1506,17 @@ void AchievementManager::MemoryPoker(u32 address, u8* buffer, u32 num_bytes, rc_ { if (buffer == nullptr) return; - if (!(Core::IsHostThread() || Core::IsCPUThread())) - { - Core::QueueHostJob([address, buffer, num_bytes, client](Core::System& system) { - MemoryPoker(address, buffer, num_bytes, client); - }); - return; - } auto& instance = AchievementManager::GetInstance(); - if (u64(address) + num_bytes >= instance.m_cloned_memory.size()) - { - ERROR_LOG_FMT(ACHIEVEMENTS, - "Attempt to write past memory size: size {} address {} write length {}", - instance.m_cloned_memory.size(), address, num_bytes); - return; - } Core::System* system = instance.m_system.load(std::memory_order_acquire); if (!system) return; Core::CPUThreadGuard thread_guard(*system); - std::lock_guard lg{instance.m_memory_lock}; if (address < MEM1_SIZE) system->GetMemory().CopyToEmu(address, buffer, num_bytes); else system->GetMemory().CopyToEmu(address - MEM1_SIZE + MEM2_START, buffer, num_bytes); - std::copy(buffer, buffer + num_bytes, instance.m_cloned_memory.begin() + address); } + void AchievementManager::GameTitleEstimateHandler(char* buffer, u32 buffer_size, rc_client_t* client) { diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index ad69f03de6..174f4bb19c 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -248,7 +248,6 @@ private: static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback, void* callback_data, rc_client_t* client); - static u32 MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client); static u32 MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client); void FetchBadge(Badge* badge, u32 badge_type, const BadgeNameFunction function, const UpdatedItems callback_data); @@ -295,8 +294,6 @@ private: bool m_dll_found = false; #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION std::function m_dev_menu_callback; - std::vector m_cloned_memory; - std::recursive_mutex m_memory_lock; std::string m_title_estimate; #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION From dc5622a9bc5edc91972a498e2c1a05a2a0a673d7 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 19 May 2025 12:08:10 +0200 Subject: [PATCH 3/3] Core: Remove IsHostThread The core no longer cares which thread is the host thread. Cleaning up Android's HostThreadLock is left for another PR, in part because the HostThreadLock in NativeConfig.cpp still serves a purpose, and in part to make any issues easier to bisect. --- Source/Android/jni/Host.h | 23 +++++------------------ Source/Core/Core/Core.cpp | 16 ---------------- Source/Core/Core/Core.h | 3 --- Source/Core/DolphinNoGUI/MainNoGUI.cpp | 2 -- Source/Core/DolphinQt/Main.cpp | 2 -- Source/Core/DolphinQt/Settings.cpp | 3 ++- Source/Core/DolphinTool/ToolMain.cpp | 2 -- Source/UnitTests/UnitTestsMain.cpp | 1 - 8 files changed, 7 insertions(+), 45 deletions(-) diff --git a/Source/Android/jni/Host.h b/Source/Android/jni/Host.h index 3a7a304825..0183038d9a 100644 --- a/Source/Android/jni/Host.h +++ b/Source/Android/jni/Host.h @@ -5,37 +5,24 @@ #include -#include "Core/Core.h" - // The Core only supports using a single Host thread. // If multiple threads want to call host functions then they need to queue // sequentially for access. +// TODO: The above isn't true anymore, so we should get rid of this class. struct HostThreadLock { - explicit HostThreadLock() : m_lock(s_host_identity_mutex) { Core::DeclareAsHostThread(); } + explicit HostThreadLock() : m_lock(s_host_identity_mutex) {} - ~HostThreadLock() - { - if (m_lock.owns_lock()) - Core::UndeclareAsHostThread(); - } + ~HostThreadLock() = default; HostThreadLock(const HostThreadLock& other) = delete; HostThreadLock(HostThreadLock&& other) = delete; HostThreadLock& operator=(const HostThreadLock& other) = delete; HostThreadLock& operator=(HostThreadLock&& other) = delete; - void Lock() - { - m_lock.lock(); - Core::DeclareAsHostThread(); - } + void Lock() { m_lock.lock(); } - void Unlock() - { - m_lock.unlock(); - Core::UndeclareAsHostThread(); - } + void Unlock() { m_lock.unlock(); } private: static std::mutex s_host_identity_mutex; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 598450726f..435632e9aa 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -131,7 +131,6 @@ static Common::Event s_cpu_thread_job_finished; static thread_local bool tls_is_cpu_thread = false; static thread_local bool tls_is_gpu_thread = false; -static thread_local bool tls_is_host_thread = false; static void EmuThread(Core::System& system, std::unique_ptr boot, WindowSystemInfo wsi); @@ -215,11 +214,6 @@ bool IsGPUThread() return tls_is_gpu_thread; } -bool IsHostThread() -{ - return tls_is_host_thread; -} - bool WantsDeterminism() { return s_wants_determinism; @@ -334,16 +328,6 @@ void UndeclareAsGPUThread() tls_is_gpu_thread = false; } -void DeclareAsHostThread() -{ - tls_is_host_thread = true; -} - -void UndeclareAsHostThread() -{ - tls_is_host_thread = false; -} - // For the CPU Thread only. static void CPUSetInitialExecutionState(bool force_paused = false) { diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 72627d0038..462a49fa27 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -127,8 +127,6 @@ void DeclareAsCPUThread(); void UndeclareAsCPUThread(); void DeclareAsGPUThread(); void UndeclareAsGPUThread(); -void DeclareAsHostThread(); -void UndeclareAsHostThread(); std::string StopMessage(bool main_thread, std::string_view message); @@ -141,7 +139,6 @@ bool IsUninitialized(Core::System& system); bool IsCPUThread(); // this tells us whether we are the CPU thread. bool IsGPUThread(); -bool IsHostThread(); bool WantsDeterminism(); diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 81051adb78..9487bb1fac 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -199,8 +199,6 @@ static std::unique_ptr GetPlatform(const optparse::Values& options) int main(const int argc, char* argv[]) { - Core::DeclareAsHostThread(); - const auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions); parser->add_option("-p", "--platform") diff --git a/Source/Core/DolphinQt/Main.cpp b/Source/Core/DolphinQt/Main.cpp index 535c2370e4..dff9a82c0d 100644 --- a/Source/Core/DolphinQt/Main.cpp +++ b/Source/Core/DolphinQt/Main.cpp @@ -125,8 +125,6 @@ int main(int argc, char* argv[]) } #endif - Core::DeclareAsHostThread(); - #ifdef __APPLE__ // On macOS, a command line option matching the format "-psn_X_XXXXXX" is passed when // the application is launched for the first time. This is to set the "ProcessSerialNumber", diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index cb76f0f5bf..03e68863ed 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "AudioCommon/AudioCommon.h" @@ -76,7 +77,7 @@ Settings::Settings() }); m_hotplug_callback_handle = g_controller_interface.RegisterDevicesChangedCallback([this] { - if (Core::IsHostThread()) + if (qApp->thread() == QThread::currentThread()) { emit DevicesChanged(); } diff --git a/Source/Core/DolphinTool/ToolMain.cpp b/Source/Core/DolphinTool/ToolMain.cpp index 224ead3d7b..058bccc7b5 100644 --- a/Source/Core/DolphinTool/ToolMain.cpp +++ b/Source/Core/DolphinTool/ToolMain.cpp @@ -32,8 +32,6 @@ static void PrintUsage() int main(int argc, char* argv[]) { - Core::DeclareAsHostThread(); - if (argc < 2) { PrintUsage(); diff --git a/Source/UnitTests/UnitTestsMain.cpp b/Source/UnitTests/UnitTestsMain.cpp index 4eed0c6099..3edb1c1909 100644 --- a/Source/UnitTests/UnitTestsMain.cpp +++ b/Source/UnitTests/UnitTestsMain.cpp @@ -25,7 +25,6 @@ int main(int argc, char** argv) { fmt::print(stderr, "Running main() from UnitTestsMain.cpp\n"); Common::RegisterMsgAlertHandler(TestMsgHandler); - Core::DeclareAsHostThread(); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();