rawinput: avoid deadlock when binding analog axis as a button (#809)

## Link to GitHub Issue or related Pull Request, if one exists
Regressed by #793

## Description of change
Due to lock inversion, when binding an analog axis as a button, spice
deadlocks. Fix that.

Also create a separate `unordered_map` that keeps track of device
handles so that `WM_INPUT` handle can look up devices without having to
acquire the larger `devices_mutex` which could be held by (potentially)
lengthy operations like hotplug.

Fix more synchronization issues around hotplug. Latent bug exposed by
MIDI 2.0 issues.

## Testing
This commit is contained in:
bicarus
2026-07-18 03:09:27 -07:00
committed by GitHub
parent e1d1b39567
commit c080bbe301
8 changed files with 160 additions and 78 deletions
+31
View File
@@ -0,0 +1,31 @@
#include "rawinput_handles.h"
void rawinput::RawInputHandles::add(Device *device) {
std::lock_guard<std::mutex> lock(this->mutex);
this->devices[device->handle] = device;
}
void rawinput::RawInputHandles::remove(Device *device) {
std::lock_guard<std::mutex> lock(this->mutex);
// teardown is shared by every device type, and handles can be reused; only erase
// an entry that still belongs to this exact RawInput device
auto it = this->devices.find(device->handle);
if (it != this->devices.end() && it->second == device) {
this->devices.erase(it);
}
}
rawinput::RawInputHandles::AcquiredDevice rawinput::RawInputHandles::acquire(HANDLE handle) {
std::lock_guard<std::mutex> index_lock(this->mutex);
auto it = this->devices.find(handle);
if (it == this->devices.end()) {
return {};
}
auto *device = it->second;
// take the device mutex while index_lock is still held so teardown can't free it
// between the lookup and the lock
return {device, std::unique_lock<std::mutex>(*device->mutex)};
}