From d1db347c8af0a0c609736a4db05d62304c3f3220 Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Tue, 28 Nov 2023 13:25:59 +0000 Subject: [PATCH] evdev: close file descriptors in a separate thread For some reason Linux is surprisingly slow at closing file descriptors of event devices. This commit improves GUI startup times on my computer by about 1.5 seconds. --- .../ControllerInterface/evdev/evdev.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp index 6e9764fda9..e9f4f21980 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -21,6 +21,7 @@ #include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Common/Thread.h" +#include "Common/WorkQueueThread.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" namespace ciface::evdev @@ -34,6 +35,12 @@ public: void RemoveDevnodeObject(const std::string&); + // Linux has the strange behavior that closing file descriptors of event devices can be + // surprisingly slow, in the range of 20-70 milliseconds. For modern systems that have maybe 30 + // event devices this can quickly add up, leading to visibly slow startup. So we close FDs on a + // separate thread *shrug* + void CloseDescriptor(int fd) { m_cleanup_thread.Push(fd); } + private: std::shared_ptr FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location); @@ -55,6 +62,8 @@ private: // as devices can be destroyed by any thread at any time. As of now it's protected // by ControllerInterface::m_devices_population_mutex. std::map> m_devnode_objects; + + Common::WorkQueueThread m_cleanup_thread; }; std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) @@ -273,7 +282,7 @@ void InputBackend::AddDeviceNode(const char* devnode) if (libevdev_new_from_fd(fd, &dev) != 0) { // This usually fails because the device node isn't an evdev device, such as /dev/input/js0 - close(fd); + CloseDescriptor(fd); return; } @@ -415,7 +424,7 @@ void InputBackend::StopHotplugThread() } InputBackend::InputBackend(ControllerInterface* controller_interface) - : ciface::InputBackend(controller_interface) + : ciface::InputBackend(controller_interface), m_cleanup_thread("evdev cleanup", close) { StartHotplugThread(); } @@ -665,7 +674,7 @@ evdevDevice::~evdevDevice() { m_input_backend.RemoveDevnodeObject(node.devnode); libevdev_free(node.device); - close(node.fd); + m_input_backend.CloseDescriptor(node.fd); } }