From 08f8c279274455ecf16a3a447378f37062408c9b Mon Sep 17 00:00:00 2001 From: Filoppi Date: Sat, 15 May 2021 12:28:30 +0300 Subject: [PATCH] ControllerInterface: fix DSU thread safety and use PlatformPopulateDevices() --- .../DualShockUDPClient/DualShockUDPClient.cpp | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp index 73eb0e515a..f8347aa51a 100644 --- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -19,6 +18,7 @@ #include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Common/Random.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Common/Thread.h" #include "Core/CoreTiming.h" @@ -192,12 +192,12 @@ struct Server std::string m_description; std::string m_address; u16 m_port; - std::mutex m_port_info_mutex; std::array m_port_info; sf::UdpSocket m_socket; SteadyClock::time_point m_disconnect_time = SteadyClock::now(); }; +static bool s_has_init; static bool s_servers_enabled; static std::vector s_servers; static u32 s_client_uid; @@ -217,6 +217,8 @@ static void HotplugThreadFunc() { Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread"); INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread started"); + Common::ScopeGuard thread_stop_guard{ + [] { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread stopped"); }}; std::vector timed_out_servers(s_servers.size(), false); @@ -331,7 +333,6 @@ static void HotplugThreadFunc() } } } - INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread stopped"); } static void StartHotplugThread() @@ -355,13 +356,15 @@ static void StopHotplugThread() return; } + s_hotplug_thread.join(); + for (auto& server : s_servers) { server.m_socket.unbind(); // interrupt blocking socket } - s_hotplug_thread.join(); } +// Also just start static void Restart() { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient Restart"); @@ -377,7 +380,8 @@ static void Restart() } } - PopulateDevices(); // Only removes devices + // Only removes devices as servers have been cleaned + g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); }); s_client_uid = Common::Random::GenerateValue(); s_next_listports_time = SteadyClock::now(); @@ -388,6 +392,9 @@ static void Restart() static void ConfigChanged() { + if (!s_has_init) + return; + const bool servers_enabled = Config::Get(Settings::SERVERS_ENABLED); const std::string servers_setting = Config::Get(Settings::SERVERS); @@ -400,6 +407,9 @@ static void ConfigChanged() if (servers_enabled != s_servers_enabled || servers_setting != new_servers_setting) { + // Stop the thread before writing to s_servers + StopHotplugThread(); + s_servers_enabled = servers_enabled; s_servers.clear(); @@ -427,6 +437,9 @@ static void ConfigChanged() void Init() { + // Does not support multiple init calls + s_has_init = true; + // The following is added for backwards compatibility const auto server_address_setting = Config::Get(Settings::SERVER_ADDRESS); const auto server_port_setting = Config::Get(Settings::SERVER_PORT); @@ -447,6 +460,10 @@ void Init() ConfigChanged(); // Call it immediately to load settings } +// This can be called by the host thread as well as the hotplug thread, concurrently. +// So use PlatformPopulateDevices(). +// s_servers is already safe because it can only be modified when the DSU thread is not running, +// from the main thread void PopulateDevices() { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient PopulateDevices"); @@ -457,9 +474,11 @@ void PopulateDevices() g_controller_interface.RemoveDevice( [](const auto* dev) { return dev->GetSource() == DUALSHOCKUDP_SOURCE_NAME; }); - for (auto& server : s_servers) + // Users might have created more than one server on the same IP/Port. + // Devices might end up being duplicated (if the server responds two all requests) + // but they won't conflict. + for (const auto& server : s_servers) { - std::lock_guard lock{server.m_port_info_mutex}; for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++) { const Proto::MessageType::PortInfo& port_info = server.m_port_info[port_index]; @@ -475,6 +494,10 @@ void PopulateDevices() void DeInit() { StopHotplugThread(); + + s_has_init = false; + s_servers_enabled = false; + s_servers.clear(); } Device::Device(std::string name, int index, std::string server_address, u16 server_port)