ControllerInterface: make evdev use PlatformPopulateDevices

Also fix evdev non thread safe acces to static variables
This commit is contained in:
Filoppi 2021-05-15 12:11:27 +03:00
parent 2aa941081e
commit 0718cfd7d7
1 changed files with 34 additions and 11 deletions

View File

@ -202,6 +202,9 @@ static int s_wakeup_eventfd;
// There is no easy way to get the device name from only a dev node
// during a device removed event, since libevdev can't work on removed devices;
// sysfs is not stable, so this is probably the easiest way to get a name for a node.
// This can, and will be modified by different thread, possibly concurrently,
// as devices can be destroyed by any thread at any time. As of now it's protected
// by ControllerInterface::m_devices_population_mutex.
static std::map<std::string, std::weak_ptr<evdevDevice>> s_devnode_objects;
static std::shared_ptr<evdevDevice>
@ -260,9 +263,13 @@ static void AddDeviceNode(const char* devnode)
// Remove and re-add device as naming and inputs may have changed.
// This will also give it the correct index and invoke device change callbacks.
g_controller_interface.RemoveDevice([&evdev_device](const auto* device) {
// Make sure to force the device removal immediately (as they are shared ptrs and
// they could be kept alive, preventing us from re-creating the device)
g_controller_interface.RemoveDevice(
[&evdev_device](const auto* device) {
return static_cast<const evdevDevice*>(device) == evdev_device.get();
});
},
true);
g_controller_interface.AddDevice(evdev_device);
}
@ -276,6 +283,8 @@ static void AddDeviceNode(const char* devnode)
g_controller_interface.AddDevice(evdev_device);
}
// If the devices failed to be added to g_controller_interface, it will be added here but then
// immediately removed in its destructor due to the shared ptr not having any references left
s_devnode_objects.emplace(devnode, std::move(evdev_device));
}
@ -318,8 +327,14 @@ static void HotplugThreadFunc()
if (!devnode)
continue;
// Use g_controller_interface.PlatformPopulateDevices() to protect access around
// s_devnode_objects. Note that even if we get these events at the same time as a
// a PopulateDevices() request (e.g. on start up, we might get all the add events
// for connected devices), this won't ever cause duplicate devices as AddDeviceNode()
// automatically removes the old one if it already existed
if (strcmp(action, "remove") == 0)
{
g_controller_interface.PlatformPopulateDevices([&devnode] {
std::shared_ptr<evdevDevice> ptr;
const auto it = s_devnode_objects.find(devnode);
@ -331,10 +346,11 @@ static void HotplugThreadFunc()
g_controller_interface.RemoveDevice([&ptr](const auto* device) {
return static_cast<const evdevDevice*>(device) == ptr.get();
});
});
}
else if (strcmp(action, "add") == 0)
{
AddDeviceNode(devnode);
g_controller_interface.PlatformPopulateDevices([&devnode] { AddDeviceNode(devnode); });
}
}
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread stopped");
@ -376,8 +392,15 @@ void Init()
StartHotplugThread();
}
// Only call this when ControllerInterface::m_devices_population_mutex is locked
void PopulateDevices()
{
// Don't run if we are not initialized
if (!s_hotplug_thread_running.IsSet())
{
return;
}
// We use udev to iterate over all /dev/input/event* devices.
// Note: the Linux kernel is currently limited to just 32 event devices. If
// this ever changes, hopefully udev will take care of this.