From 07f83cd5a674ba49a102d942d3c6a2220a7ed32a Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Tue, 19 Jan 2021 19:50:10 +0300 Subject: [PATCH] hidapi/libusb/hid.c: fix race condition on device close (bug #5484) From hidapi mainstream git: https://github.com/libusb/hidapi/issues/142 https://github.com/libusb/hidapi/commit/d2c3a9862eefe2d3f4db0f00c0238277bfb4e44b Read callback may fire itself on its own even after its been requested to stop and exactly before the calling code waits for its completion in indefinite loop. Explicitly preventing re-fireing the submission loop fixes the issue. --- src/hidapi/libusb/hid.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index 8fa98b013..0ba007ebd 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -157,7 +157,7 @@ struct hid_device_ { SDL_cond *condition; SDL_ThreadBarrier barrier; /* Ensures correct startup sequence */ int shutdown_thread; - int cancelled; + int transfer_loop_finished; struct libusb_transfer *transfer; /* List of received input reports. */ @@ -823,13 +823,9 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer) } else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { dev->shutdown_thread = 1; - dev->cancelled = 1; - return; } else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { dev->shutdown_thread = 1; - dev->cancelled = 1; - return; } else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { //LOG("Timeout (normal)\n"); @@ -838,12 +834,17 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer) LOG("Unknown transfer code: %d\n", transfer->status); } + if (dev->shutdown_thread) { + dev->transfer_loop_finished = 1; + return; + } + /* Re-submit the transfer object. */ res = libusb_submit_transfer(transfer); if (res != 0) { LOG("Unable to submit URB. libusb error code: %d\n", res); dev->shutdown_thread = 1; - dev->cancelled = 1; + dev->transfer_loop_finished = 1; } } @@ -886,6 +887,7 @@ static int read_thread(void *param) res != LIBUSB_ERROR_TIMEOUT && res != LIBUSB_ERROR_OVERFLOW && res != LIBUSB_ERROR_INTERRUPTED) { + dev->shutdown_thread = 1; break; } } @@ -895,8 +897,8 @@ static int read_thread(void *param) if no transfers are pending, but that's OK. */ libusb_cancel_transfer(dev->transfer); - while (!dev->cancelled) - libusb_handle_events_completed(usb_context, &dev->cancelled); + while (!dev->transfer_loop_finished) + libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished); /* Now that the read thread is stopping, Wake any threads which are waiting on data (in hid_read_timeout()). Do this under a mutex to