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 <map>
#include <string_view>
#include <utility>
// For CoGetApartmentType
#include <objbase.h>
@ -25,7 +26,9 @@
#include "Common/HRWrap.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/StringUtil.h"
#include "Common/WorkQueueThread.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
namespace WGI = winrt::Windows::Gaming::Input;
@ -605,8 +608,21 @@ private:
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 winrt::event_token s_event_added, s_event_removed;
static Common::WorkQueueThread<AddRemoveEvent> s_device_add_remove_queue;
static bool COMIsInitialized()
{
@ -668,6 +684,36 @@ static void RemoveDevice(const WGI::RawGameController& raw_game_controller)
// (H is the lambda)
#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()
{
if (!COMIsInitialized())
@ -678,18 +724,21 @@ void Init()
s_initialized_winrt = true;
}
s_device_add_remove_queue.Reset("WGInput Add/Remove Device Thread", HandleAddRemoveEvent);
try
{
// These events will be invoked from WGI-managed threadpool.
s_event_added = WGI::RawGameController::RawGameControllerAdded(
[](auto&&, const WGI::RawGameController raw_game_controller) {
RemoveDevice(raw_game_controller);
AddDevice(raw_game_controller);
[](auto&&, WGI::RawGameController raw_game_controller) {
s_device_add_remove_queue.EmplaceItem(
AddRemoveEvent{AddRemoveEventType::AddOrReplace, std::move(raw_game_controller)});
});
s_event_removed = WGI::RawGameController::RawGameControllerRemoved(
[](auto&&, const WGI::RawGameController raw_game_controller) {
RemoveDevice(raw_game_controller);
[](auto&&, WGI::RawGameController raw_game_controller) {
s_device_add_remove_queue.EmplaceItem(
AddRemoveEvent{AddRemoveEventType::Remove, std::move(raw_game_controller)});
});
}
catch (winrt::hresult_error)
@ -702,6 +751,8 @@ void Init()
void DeInit()
{
s_device_add_remove_queue.Shutdown();
WGI::RawGameController::RawGameControllerAdded(s_event_added);
WGI::RawGameController::RawGameControllerRemoved(s_event_removed);