Skip to content

Commit

Permalink
InputCommon/WGInput: Handle add/remove events on separate thread to p…
Browse files Browse the repository at this point in the history
…revent deadlocks.

In particular this is triggered when running Dolphin with the Steam overlay.
  • Loading branch information
AdmiralCurtiss committed Jan 17, 2024
1 parent 12318f9 commit 83d4b69
Showing 1 changed file with 56 additions and 5 deletions.
61 changes: 56 additions & 5 deletions Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <array>
#include <map>
#include <string_view>
#include <utility>

// For CoGetApartmentType
#include <objbase.h>
Expand All @@ -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;
Expand Down Expand Up @@ -605,8 +608,21 @@ class Device : public Core::Device
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()
{
Expand Down Expand Up @@ -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())
Expand All @@ -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)
Expand All @@ -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);

Expand Down

0 comments on commit 83d4b69

Please sign in to comment.