Merge pull request #13593 from Dentomologist/mainwindow_fix_use_after_free_during_dolphin_shutdown

MainWindow: Fix use-after-free during Dolphin shutdown
This commit is contained in:
JosJuice 2025-04-26 09:06:06 +02:00 committed by GitHub
commit 3e5286c1a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 72 additions and 23 deletions

View file

@ -29,9 +29,9 @@ void AddLayer(std::unique_ptr<ConfigLayerLoader> loader);
std::shared_ptr<Layer> GetLayer(LayerType layer); std::shared_ptr<Layer> GetLayer(LayerType layer);
void RemoveLayer(LayerType layer); void RemoveLayer(LayerType layer);
// Returns an ID that can be passed to RemoveConfigChangedCallback(). // Returns an ID that should be passed to RemoveConfigChangedCallback() when the callback is no
// The callback may be called from any thread. // longer needed. The callback may be called from any thread.
ConfigChangedCallbackID AddConfigChangedCallback(ConfigChangedCallback func); [[nodiscard]] ConfigChangedCallbackID AddConfigChangedCallback(ConfigChangedCallback func);
void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id); void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id);
void OnConfigChanged(); void OnConfigChanged();

View file

@ -76,7 +76,7 @@ void AchievementManager::Init(void* hwnd)
[](const char* message, const rc_client_t* client) { [](const char* message, const rc_client_t* client) {
INFO_LOG_FMT(ACHIEVEMENTS, "{}", message); INFO_LOG_FMT(ACHIEVEMENTS, "{}", message);
}); });
Config::AddConfigChangedCallback([this] { SetHardcoreMode(); }); m_config_changed_callback_id = Config::AddConfigChangedCallback([this] { SetHardcoreMode(); });
SetHardcoreMode(); SetHardcoreMode();
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); }); m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
m_image_queue.Reset("AchievementManagerImageQueue", m_image_queue.Reset("AchievementManagerImageQueue",
@ -764,6 +764,7 @@ void AchievementManager::Shutdown()
{ {
CloseGame(); CloseGame();
m_queue.Shutdown(); m_queue.Shutdown();
Config::RemoveConfigChangedCallback(m_config_changed_callback_id);
std::lock_guard lg{m_lock}; std::lock_guard lg{m_lock};
// DON'T log out - keep those credentials for next run. // DON'T log out - keep those credentials for next run.
rc_client_destroy(m_client); rc_client_destroy(m_client);

View file

@ -26,6 +26,7 @@
#include <rcheevos/include/rc_runtime.h> #include <rcheevos/include/rc_runtime.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/HttpRequest.h" #include "Common/HttpRequest.h"
#include "Common/JsonUtil.h" #include "Common/JsonUtil.h"
@ -264,6 +265,7 @@ private:
bool m_is_runtime_initialized = false; bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback = [](const UpdatedItems&) {}; UpdateCallback m_update_callback = [](const UpdatedItems&) {};
std::unique_ptr<DiscIO::Volume> m_loading_volume; std::unique_ptr<DiscIO::Volume> m_loading_volume;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
Badge m_default_player_badge; Badge m_default_player_badge;
Badge m_default_game_badge; Badge m_default_game_badge;
Badge m_default_unlocked_badge; Badge m_default_unlocked_badge;

View file

@ -18,8 +18,9 @@ struct ConfigChangedCallbackID
bool operator==(const ConfigChangedCallbackID&) const = default; bool operator==(const ConfigChangedCallbackID&) const = default;
}; };
// returns an ID that can be passed to RemoveConfigChangedCallback() // Returns an ID that should be passed to RemoveConfigChangedCallback() when the callback is no
ConfigChangedCallbackID AddConfigChangedCallback(Config::ConfigChangedCallback func); // longer needed.
[[nodiscard]] ConfigChangedCallbackID AddConfigChangedCallback(Config::ConfigChangedCallback func);
void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id); void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id);

View file

@ -3,6 +3,8 @@
#include "Core/FreeLookConfig.h" #include "Core/FreeLookConfig.h"
#include <optional>
#include "Core/AchievementManager.h" #include "Core/AchievementManager.h"
#include "Core/CPUThreadConfigCallback.h" #include "Core/CPUThreadConfigCallback.h"
#include "Core/Config/AchievementSettings.h" #include "Core/Config/AchievementSettings.h"
@ -14,7 +16,8 @@ namespace FreeLook
{ {
static Config s_config; static Config s_config;
static Config s_active_config; static Config s_active_config;
static bool s_has_registered_callback = false; static std::optional<CPUThreadConfigCallback::ConfigChangedCallbackID>
s_config_changed_callback_id = std::nullopt;
Config& GetConfig() Config& GetConfig()
{ {
@ -39,14 +42,23 @@ Config::Config()
void Config::Refresh() void Config::Refresh()
{ {
if (!s_has_registered_callback) if (!s_config_changed_callback_id.has_value())
{ {
CPUThreadConfigCallback::AddConfigChangedCallback([] { s_config.Refresh(); }); s_config_changed_callback_id =
s_has_registered_callback = true; CPUThreadConfigCallback::AddConfigChangedCallback([] { s_config.Refresh(); });
} }
camera_config.control_type = ::Config::Get(::Config::FL1_CONTROL_TYPE); camera_config.control_type = ::Config::Get(::Config::FL1_CONTROL_TYPE);
enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED) && enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED) &&
!AchievementManager::GetInstance().IsHardcoreModeActive(); !AchievementManager::GetInstance().IsHardcoreModeActive();
} }
void Config::Shutdown()
{
if (!s_config_changed_callback_id.has_value())
return;
CPUThreadConfigCallback::RemoveConfigChangedCallback(*s_config_changed_callback_id);
s_config_changed_callback_id.reset();
}
} // namespace FreeLook } // namespace FreeLook

View file

@ -27,6 +27,7 @@ struct Config final
{ {
Config(); Config();
void Refresh(); void Refresh();
void Shutdown();
CameraConfig camera_config; CameraConfig camera_config;
bool enabled; bool enabled;

View file

@ -325,8 +325,9 @@ InputConfig* GetInputConfig()
void Shutdown() void Shutdown()
{ {
s_config.UnregisterHotplugCallback(); s_config.UnregisterHotplugCallback();
s_config.ClearControllers(); s_config.ClearControllers();
GetConfig().Shutdown();
} }
void Initialize() void Initialize()

View file

@ -34,6 +34,7 @@
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#endif #endif
#include "Common/Config/Config.h"
#include "Common/ScopeGuard.h" #include "Common/ScopeGuard.h"
#include "Common/Version.h" #include "Common/Version.h"
#include "Common/WindowSystemInfo.h" #include "Common/WindowSystemInfo.h"
@ -277,7 +278,7 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
if (AchievementManager::GetInstance().IsHardcoreModeActive()) if (AchievementManager::GetInstance().IsHardcoreModeActive())
Settings::Instance().SetDebugModeEnabled(false); Settings::Instance().SetDebugModeEnabled(false);
// This needs to trigger on both RA_HARDCORE_ENABLED and RA_ENABLED // This needs to trigger on both RA_HARDCORE_ENABLED and RA_ENABLED
Config::AddConfigChangedCallback( m_config_changed_callback_id = Config::AddConfigChangedCallback(
[this]() { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); }); [this]() { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); });
// If hardcore is enabled when the emulator starts, make sure it turns off what it needs to // If hardcore is enabled when the emulator starts, make sure it turns off what it needs to
if (Config::Get(Config::RA_HARDCORE_ENABLED)) if (Config::Get(Config::RA_HARDCORE_ENABLED))
@ -351,6 +352,7 @@ MainWindow::~MainWindow()
Settings::Instance().ResetNetPlayServer(); Settings::Instance().ResetNetPlayServer();
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
Config::RemoveConfigChangedCallback(m_config_changed_callback_id);
AchievementManager::GetInstance().Shutdown(); AchievementManager::GetInstance().Shutdown();
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS

View file

@ -11,6 +11,10 @@
#include <optional> #include <optional>
#include <string> #include <string>
#ifdef USE_RETRO_ACHIEVEMENTS
#include "Common/Config/Config.h"
#endif // USE_RETRO_ACHIEVEMENTS
#include "Core/Boot/Boot.h" #include "Core/Boot/Boot.h"
class QMenu; class QMenu;
@ -261,6 +265,7 @@ private:
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
AchievementsWindow* m_achievements_window = nullptr; AchievementsWindow* m_achievements_window = nullptr;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
AssemblerWidget* m_assembler_widget; AssemblerWidget* m_assembler_widget;

View file

@ -59,7 +59,7 @@ Settings::Settings()
}); });
}); });
Config::AddConfigChangedCallback([this] { m_config_changed_callback_id = Config::AddConfigChangedCallback([this] {
static std::atomic<bool> do_once{true}; static std::atomic<bool> do_once{true};
if (do_once.exchange(false)) if (do_once.exchange(false))
{ {
@ -94,7 +94,10 @@ Settings::Settings()
}); });
} }
Settings::~Settings() = default; Settings::~Settings()
{
Config::RemoveConfigChangedCallback(m_config_changed_callback_id);
}
void Settings::UnregisterDevicesChangedCallback() void Settings::UnregisterDevicesChangedCallback()
{ {

View file

@ -10,6 +10,7 @@
#include <QRadioButton> #include <QRadioButton>
#include <QSettings> #include <QSettings>
#include "Common/Config/Config.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
@ -232,6 +233,7 @@ private:
std::shared_ptr<NetPlay::NetPlayClient> m_client; std::shared_ptr<NetPlay::NetPlayClient> m_client;
std::shared_ptr<NetPlay::NetPlayServer> m_server; std::shared_ptr<NetPlay::NetPlayServer> m_server;
ControllerInterface::HotplugCallbackHandle m_hotplug_callback_handle; ControllerInterface::HotplugCallbackHandle m_hotplug_callback_handle;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
}; };
Q_DECLARE_METATYPE(Core::State); Q_DECLARE_METATYPE(Core::State);

View file

@ -50,6 +50,9 @@
#include "UICommon/DiscordPresence.h" #include "UICommon/DiscordPresence.h"
#include "UICommon/USBUtils.h" #include "UICommon/USBUtils.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
#ifdef HAVE_QTDBUS #ifdef HAVE_QTDBUS
#include "UICommon/DBusUtils.h" #include "UICommon/DBusUtils.h"
#endif #endif
@ -58,8 +61,6 @@
#include <IOKit/pwr_mgt/IOPMLib.h> #include <IOKit/pwr_mgt/IOPMLib.h>
#endif #endif
#include "VideoCommon/VideoBackendBase.h"
namespace UICommon namespace UICommon
{ {
static Config::ConfigChangedCallbackID s_config_changed_callback_id; static Config::ConfigChangedCallbackID s_config_changed_callback_id;
@ -130,14 +131,17 @@ void Init()
Core::RestoreWiiSettings(Core::RestoreReason::CrashRecovery); Core::RestoreWiiSettings(Core::RestoreReason::CrashRecovery);
Config::Init(); Config::Init();
Config::AddConfigChangedCallback(InitCustomPaths); const auto config_changed_callback = []() {
InitCustomPaths();
RefreshConfig();
};
s_config_changed_callback_id = Config::AddConfigChangedCallback(config_changed_callback);
Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader()); Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader());
SConfig::Init(); SConfig::Init();
Discord::Init(); Discord::Init();
Common::Log::LogManager::Init(); Common::Log::LogManager::Init();
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND)); VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
s_config_changed_callback_id = Config::AddConfigChangedCallback(RefreshConfig);
RefreshConfig(); RefreshConfig();
} }
@ -149,6 +153,7 @@ void Shutdown()
WiimoteReal::Shutdown(); WiimoteReal::Shutdown();
Common::Log::LogManager::Shutdown(); Common::Log::LogManager::Shutdown();
Discord::Shutdown(); Discord::Shutdown();
g_Config.Shutdown();
SConfig::Shutdown(); SConfig::Shutdown();
Config::Shutdown(); Config::Shutdown();
} }

View file

@ -4,6 +4,7 @@
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#include <algorithm> #include <algorithm>
#include <optional>
#include "Common/CPUDetect.h" #include "Common/CPUDetect.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -33,7 +34,8 @@
VideoConfig g_Config; VideoConfig g_Config;
VideoConfig g_ActiveConfig; VideoConfig g_ActiveConfig;
BackendInfo g_backend_info; BackendInfo g_backend_info;
static bool s_has_registered_callback = false; static std::optional<CPUThreadConfigCallback::ConfigChangedCallbackID>
s_config_changed_callback_id = std::nullopt;
static bool IsVSyncActive(bool enabled) static bool IsVSyncActive(bool enabled)
{ {
@ -50,14 +52,14 @@ void UpdateActiveConfig()
void VideoConfig::Refresh() void VideoConfig::Refresh()
{ {
if (!s_has_registered_callback) if (!s_config_changed_callback_id.has_value())
{ {
// There was a race condition between the video thread and the host thread here, if // There was a race condition between the video thread and the host thread here, if
// corrections need to be made by VerifyValidity(). Briefly, the config will contain // corrections need to be made by VerifyValidity(). Briefly, the config will contain
// invalid values. Instead, pause the video thread first, update the config and correct // invalid values. Instead, pause the video thread first, update the config and correct
// it, then resume emulation, after which the video thread will detect the config has // it, then resume emulation, after which the video thread will detect the config has
// changed and act accordingly. // changed and act accordingly.
CPUThreadConfigCallback::AddConfigChangedCallback([]() { const auto config_changed_callback = []() {
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
const bool lock_gpu_thread = Core::IsRunning(system); const bool lock_gpu_thread = Core::IsRunning(system);
@ -69,8 +71,10 @@ void VideoConfig::Refresh()
if (lock_gpu_thread) if (lock_gpu_thread)
system.GetFifo().PauseAndLock(false, true); system.GetFifo().PauseAndLock(false, true);
}); };
s_has_registered_callback = true;
s_config_changed_callback_id =
CPUThreadConfigCallback::AddConfigChangedCallback(config_changed_callback);
} }
bVSync = Config::Get(Config::GFX_VSYNC); bVSync = Config::Get(Config::GFX_VSYNC);
@ -212,6 +216,15 @@ void VideoConfig::VerifyValidity()
} }
} }
void VideoConfig::Shutdown()
{
if (!s_config_changed_callback_id.has_value())
return;
CPUThreadConfigCallback::RemoveConfigChangedCallback(*s_config_changed_callback_id);
s_config_changed_callback_id.reset();
}
bool VideoConfig::UsingUberShaders() const bool VideoConfig::UsingUberShaders() const
{ {
return iShaderCompilationMode == ShaderCompilationMode::SynchronousUberShaders || return iShaderCompilationMode == ShaderCompilationMode::SynchronousUberShaders ||

View file

@ -190,6 +190,7 @@ struct VideoConfig final
VideoConfig() = default; VideoConfig() = default;
void Refresh(); void Refresh();
void VerifyValidity(); void VerifyValidity();
static void Shutdown();
// General // General
bool bVSync = false; bool bVSync = false;