mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 07:09:48 -06:00
GCAdapter: improve thread safety
make sure Reset() can’t be run concurrently with AddGCAdapter() or ResetRumble() (which is called on other threads) which can cause crashes (issue #9462)
This commit is contained in:
@ -22,6 +22,7 @@ namespace GCAdapter
|
|||||||
{
|
{
|
||||||
static bool CheckDeviceAccess(libusb_device* device);
|
static bool CheckDeviceAccess(libusb_device* device);
|
||||||
static void AddGCAdapter(libusb_device* device);
|
static void AddGCAdapter(libusb_device* device);
|
||||||
|
static void ResetRumbleLockNeeded();
|
||||||
|
|
||||||
static bool s_detected = false;
|
static bool s_detected = false;
|
||||||
static libusb_device_handle* s_handle = nullptr;
|
static libusb_device_handle* s_handle = nullptr;
|
||||||
@ -37,6 +38,7 @@ static std::atomic<int> s_controller_payload_size = {0};
|
|||||||
static std::thread s_adapter_thread;
|
static std::thread s_adapter_thread;
|
||||||
static Common::Flag s_adapter_thread_running;
|
static Common::Flag s_adapter_thread_running;
|
||||||
|
|
||||||
|
static std::mutex s_init_mutex;
|
||||||
static std::thread s_adapter_detect_thread;
|
static std::thread s_adapter_detect_thread;
|
||||||
static Common::Flag s_adapter_detect_thread_running;
|
static Common::Flag s_adapter_detect_thread_running;
|
||||||
|
|
||||||
@ -77,7 +79,10 @@ static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotpl
|
|||||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
||||||
{
|
{
|
||||||
if (s_handle == nullptr && CheckDeviceAccess(dev))
|
if (s_handle == nullptr && CheckDeviceAccess(dev))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(s_init_mutex);
|
||||||
AddGCAdapter(dev);
|
AddGCAdapter(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||||
{
|
{
|
||||||
@ -115,6 +120,7 @@ static void ScanThreadFunc()
|
|||||||
{
|
{
|
||||||
if (s_handle == nullptr)
|
if (s_handle == nullptr)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(s_init_mutex);
|
||||||
Setup();
|
Setup();
|
||||||
if (s_detected && s_detect_callback != nullptr)
|
if (s_detected && s_detect_callback != nullptr)
|
||||||
s_detect_callback();
|
s_detect_callback();
|
||||||
@ -306,7 +312,7 @@ static void AddGCAdapter(libusb_device* device)
|
|||||||
s_detected = true;
|
s_detected = true;
|
||||||
if (s_detect_callback != nullptr)
|
if (s_detect_callback != nullptr)
|
||||||
s_detect_callback();
|
s_detect_callback();
|
||||||
ResetRumble();
|
ResetRumbleLockNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
@ -329,6 +335,9 @@ void Shutdown()
|
|||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock);
|
||||||
|
if (!lock.try_lock())
|
||||||
|
return;
|
||||||
if (!s_detected)
|
if (!s_detected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -439,10 +448,20 @@ bool UseAdapter()
|
|||||||
|
|
||||||
void ResetRumble()
|
void ResetRumble()
|
||||||
{
|
{
|
||||||
if (!UseAdapter())
|
std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock);
|
||||||
|
if (!lock.try_lock())
|
||||||
return;
|
return;
|
||||||
if (s_handle == nullptr || !s_detected)
|
ResetRumbleLockNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to be called when s_init_mutex is locked in order to avoid
|
||||||
|
// being called while the libusb state is being reset
|
||||||
|
static void ResetRumbleLockNeeded()
|
||||||
|
{
|
||||||
|
if (!UseAdapter() || (s_handle == nullptr || !s_detected))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::fill(std::begin(s_controller_rumble), std::end(s_controller_rumble), 0);
|
std::fill(std::begin(s_controller_rumble), std::end(s_controller_rumble), 0);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user