@@ -172,7 +172,10 @@ static struct hid_hotplug_context {
172172 /* Linked list of the hotplug callbacks */
173173 struct hid_hotplug_callback * hotplug_cbs ;
174174
175- /* Linked list of the device infos (mandatory when the device is disconnected) */
175+ /* Linked list of the device infos (mandatory when the device is disconnected).
176+ * Protected by `mutex`: all reads, writes and the final free during teardown
177+ * are performed while holding it. The teardown free runs in
178+ * hid_internal_hotplug_cleanup() after the hotplug threads have been joined. */
176179 struct hid_device_info * devs ;
177180} hid_hotplug_context = {
178181 .next_handle = FIRST_HOTPLUG_CALLBACK_HANDLE ,
@@ -584,6 +587,11 @@ static void hid_internal_hotplug_cleanup()
584587
585588 /* Wait for both threads to stop */
586589 hidapi_thread_join (& hid_hotplug_context .libusb_thread );
590+
591+ /* Both hotplug threads have exited: we now have exclusive access to `devs`
592+ * (the caller holds `mutex` and no hotplug event can reach process_hotplug_event). */
593+ hid_free_enumeration (hid_hotplug_context .devs );
594+ hid_hotplug_context .devs = NULL ;
587595}
588596
589597static void hid_internal_hotplug_init ()
@@ -1143,8 +1151,8 @@ static int hid_libusb_hotplug_callback(libusb_context *ctx, libusb_device *devic
11431151 msg -> event = event ;
11441152 msg -> next = NULL ;
11451153
1146- /* We use this thread 's mutex to protect the queue */
1147- hidapi_thread_mutex_lock (& hid_hotplug_context .libusb_thread );
1154+ /* Use callback_thread 's mutex to protect the queue and signal it */
1155+ hidapi_thread_mutex_lock (& hid_hotplug_context .callback_thread );
11481156 struct hid_hotplug_queue * end = hid_hotplug_context .queue ;
11491157 if (end ) {
11501158 while (end -> next ) {
@@ -1154,16 +1162,21 @@ static int hid_libusb_hotplug_callback(libusb_context *ctx, libusb_device *devic
11541162 } else {
11551163 hid_hotplug_context .queue = msg ;
11561164 }
1157- hidapi_thread_mutex_unlock (& hid_hotplug_context .libusb_thread );
11581165
1159- /* Wake up the other thread so it can react to the new message immediately */
1166+ /* Wake up the callback thread so it can react to the new message immediately */
11601167 hidapi_thread_cond_signal (& hid_hotplug_context .callback_thread );
1168+ hidapi_thread_mutex_unlock (& hid_hotplug_context .callback_thread );
11611169
11621170 return 0 ;
11631171}
11641172
11651173static void process_hotplug_event (struct hid_hotplug_queue * msg )
11661174{
1175+ /* Lock the mutex to avoid race conditions with hid_hotplug_register_callback(),
1176+ * which may iterate devs during HID_API_HOTPLUG_ENUMERATE while holding this mutex.
1177+ * The mutex is recursive, so hid_internal_invoke_callbacks() can safely re-acquire it. */
1178+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1179+
11671180 if (msg -> event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED ) {
11681181 struct hid_device_info * info = hid_enumerate_from_libusb (msg -> device , 0 , 0 );
11691182 struct hid_device_info * info_cur = info ;
@@ -1204,6 +1217,8 @@ static void process_hotplug_event(struct hid_hotplug_queue* msg)
12041217 }
12051218 }
12061219
1220+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1221+
12071222 /* Release the libusb device - we are done with it */
12081223 libusb_unref_device (msg -> device );
12091224
@@ -1216,40 +1231,32 @@ static void* callback_thread(void* user_data)
12161231{
12171232 (void ) user_data ;
12181233
1219- /* 5 msec timeout seems reasonable; don't set too low to avoid high CPU usage */
1220- /* This timeout only affects how much time it takes to stop the thread */
1221- hidapi_timespec ts ;
1222- ts .tv_sec = 0 ;
1223- ts .tv_nsec = 5000000 ;
1224-
12251234 hidapi_thread_mutex_lock (& hid_hotplug_context .callback_thread );
1226-
1235+
12271236 /* We stop the thread if by the moment there are no events left in the queue there are no callbacks left */
12281237 while (1 ) {
1229- /* Make the tread fall asleep and wait for a condition to wake it up */
1230- hidapi_thread_cond_timedwait (& hid_hotplug_context .callback_thread , & ts );
1238+ /* Wait for events to arrive or shutdown signal */
1239+ while (!hid_hotplug_context .queue && hid_hotplug_context .hotplug_cbs ) {
1240+ hidapi_thread_cond_wait (& hid_hotplug_context .callback_thread );
1241+ }
12311242
1232- /* We use this thread's mutex to protect the queue */
1233- hidapi_thread_mutex_lock (& hid_hotplug_context .libusb_thread );
1243+ /* Process all pending events from the queue */
12341244 while (hid_hotplug_context .queue ) {
12351245 struct hid_hotplug_queue * cur_event = hid_hotplug_context .queue ;
1236- process_hotplug_event ( cur_event ) ;
1246+ hid_hotplug_context . queue = cur_event -> next ;
12371247
1238- /* Empty the queue */
1239- cur_event = cur_event -> next ;
1240- free (hid_hotplug_context .queue );
1241- hid_hotplug_context .queue = cur_event ;
1248+ /* Release the lock while processing to avoid blocking event producers */
1249+ hidapi_thread_mutex_unlock (& hid_hotplug_context .callback_thread );
1250+ process_hotplug_event (cur_event );
1251+ free (cur_event );
1252+ hidapi_thread_mutex_lock (& hid_hotplug_context .callback_thread );
12421253 }
1243- hidapi_thread_mutex_unlock ( & hid_hotplug_context . libusb_thread );
1254+
12441255 if (!hid_hotplug_context .hotplug_cbs ) {
12451256 break ;
12461257 }
12471258 }
12481259
1249- /* Cleanup connected device list */
1250- hid_free_enumeration (hid_hotplug_context .devs );
1251- hid_hotplug_context .devs = NULL ;
1252-
12531260 hidapi_thread_mutex_unlock (& hid_hotplug_context .callback_thread );
12541261
12551262 return NULL ;
@@ -1276,8 +1283,12 @@ static void* hotplug_thread(void* user_data)
12761283 libusb_hotplug_deregister_callback (hid_hotplug_context .context , hid_hotplug_context .callback_handle );
12771284 libusb_exit (hid_hotplug_context .context );
12781285
1279- /* Forcibly wake up the thread so it can shut down immediately and wait for it to stop */
1286+ /* hotplug_cbs is already NULL here (the loop above exited because of that).
1287+ * Signal callback_thread under the mutex so it can observe the NULL hotplug_cbs
1288+ * and exit cleanly, rather than waiting indefinitely in cond_wait. */
1289+ hidapi_thread_mutex_lock (& hid_hotplug_context .callback_thread );
12801290 hidapi_thread_cond_signal (& hid_hotplug_context .callback_thread );
1291+ hidapi_thread_mutex_unlock (& hid_hotplug_context .callback_thread );
12811292
12821293 hidapi_thread_join (& hid_hotplug_context .callback_thread );
12831294
@@ -1354,8 +1365,12 @@ int HID_API_EXPORT HID_API_CALL hid_hotplug_register_callback(unsigned short ven
13541365 LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT ,
13551366 0 , LIBUSB_HOTPLUG_MATCH_ANY , LIBUSB_HOTPLUG_MATCH_ANY , LIBUSB_HOTPLUG_MATCH_ANY , & hid_libusb_hotplug_callback , NULL ,
13561367 & hid_hotplug_context .callback_handle )) {
1357- /* Major malfunction, failed to register a callback */
1368+ /* Major malfunction, failed to register a callback.
1369+ * No hotplug thread was started, so we must unwind `devs` ourselves
1370+ * (hid_internal_hotplug_cleanup() would try to join a non-existent thread). */
13581371 libusb_exit (hid_hotplug_context .context );
1372+ hid_free_enumeration (hid_hotplug_context .devs );
1373+ hid_hotplug_context .devs = NULL ;
13591374 free (hotplug_cb );
13601375 hid_hotplug_context .hotplug_cbs = NULL ;
13611376 pthread_mutex_unlock (& hid_hotplug_context .mutex );
0 commit comments