Merge pull request #12508 from AdmiralCurtiss/wgi-deadlock

InputCommon/WGInput: Handle add/remove events on separate thread to prevent deadlocks.
This commit is contained in:
Admiral H. Curtiss 2024-01-18 10:25:46 +01:00 committed by GitHub
commit db43f905e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 56 additions and 5 deletions

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <map> #include <map>
#include <string_view> #include <string_view>
#include <utility>
// For CoGetApartmentType // For CoGetApartmentType
#include <objbase.h> #include <objbase.h>
@ -25,7 +26,9 @@
#include "Common/HRWrap.h" #include "Common/HRWrap.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/WorkQueueThread.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
namespace WGI = winrt::Windows::Gaming::Input; namespace WGI = winrt::Windows::Gaming::Input;
@ -605,8 +608,21 @@ private:
ControlState m_battery_level = 0; ControlState m_battery_level = 0;
}; };
enum class AddRemoveEventType
{
AddOrReplace,
Remove,
};
struct AddRemoveEvent
{
AddRemoveEventType type;
WGI::RawGameController raw_game_controller;
};
static thread_local bool s_initialized_winrt; static thread_local bool s_initialized_winrt;
static winrt::event_token s_event_added, s_event_removed; static winrt::event_token s_event_added, s_event_removed;
static Common::WorkQueueThread<AddRemoveEvent> s_device_add_remove_queue;
static bool COMIsInitialized() static bool COMIsInitialized()
{ {
@ -668,6 +684,36 @@ static void RemoveDevice(const WGI::RawGameController& raw_game_controller)
// (H is the lambda) // (H is the lambda)
#pragma warning(disable : 4265) #pragma warning(disable : 4265)
static void HandleAddRemoveEvent(AddRemoveEvent evt)
{
try
{
winrt::init_apartment();
}
catch (const winrt::hresult_error& ex)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE,
"WGInput: Failed to CoInitialize for add/remove controller event: {}",
WStringToUTF8(ex.message()));
return;
}
Common::ScopeGuard coinit_guard([] { winrt::uninit_apartment(); });
switch (evt.type)
{
case AddRemoveEventType::AddOrReplace:
RemoveDevice(evt.raw_game_controller);
AddDevice(evt.raw_game_controller);
break;
case AddRemoveEventType::Remove:
RemoveDevice(evt.raw_game_controller);
break;
default:
ERROR_LOG_FMT(CONTROLLERINTERFACE, "WGInput: Invalid add/remove controller event: {}",
std::to_underlying(evt.type));
}
}
void Init() void Init()
{ {
if (!COMIsInitialized()) if (!COMIsInitialized())
@ -678,18 +724,21 @@ void Init()
s_initialized_winrt = true; s_initialized_winrt = true;
} }
s_device_add_remove_queue.Reset("WGInput Add/Remove Device Thread", HandleAddRemoveEvent);
try try
{ {
// These events will be invoked from WGI-managed threadpool. // These events will be invoked from WGI-managed threadpool.
s_event_added = WGI::RawGameController::RawGameControllerAdded( s_event_added = WGI::RawGameController::RawGameControllerAdded(
[](auto&&, const WGI::RawGameController raw_game_controller) { [](auto&&, WGI::RawGameController raw_game_controller) {
RemoveDevice(raw_game_controller); s_device_add_remove_queue.EmplaceItem(
AddDevice(raw_game_controller); AddRemoveEvent{AddRemoveEventType::AddOrReplace, std::move(raw_game_controller)});
}); });
s_event_removed = WGI::RawGameController::RawGameControllerRemoved( s_event_removed = WGI::RawGameController::RawGameControllerRemoved(
[](auto&&, const WGI::RawGameController raw_game_controller) { [](auto&&, WGI::RawGameController raw_game_controller) {
RemoveDevice(raw_game_controller); s_device_add_remove_queue.EmplaceItem(
AddRemoveEvent{AddRemoveEventType::Remove, std::move(raw_game_controller)});
}); });
} }
catch (winrt::hresult_error) catch (winrt::hresult_error)
@ -702,6 +751,8 @@ void Init()
void DeInit() void DeInit()
{ {
s_device_add_remove_queue.Shutdown();
WGI::RawGameController::RawGameControllerAdded(s_event_added); WGI::RawGameController::RawGameControllerAdded(s_event_added);
WGI::RawGameController::RawGameControllerRemoved(s_event_removed); WGI::RawGameController::RawGameControllerRemoved(s_event_removed);