Skip to content

Commit

Permalink
Various fixes on shutdown procedures
Browse files Browse the repository at this point in the history
  • Loading branch information
k1-801 committed Apr 9, 2024
1 parent 80a82bc commit 2bc6b5f
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 17 deletions.
37 changes: 21 additions & 16 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -572,15 +572,13 @@ static void hid_internal_hotplug_cleanup()
return;
}

/* Before checking if the list is empty, clear any entries whose removal was postponed first */
hid_internal_hotplug_remove_postponed();

if (hid_hotplug_context.hotplug_cbs != NULL) {
return;
}

/* Forcibly wake up the thread so it can shut down immediately */
hidapi_thread_cond_signal(&hid_hotplug_context.callback_thread);

/* Wait for both threads to stop */
hidapi_thread_join(&hid_hotplug_context.libusb_thread);
}
Expand Down Expand Up @@ -1205,11 +1203,9 @@ static void process_hotplug_event(struct hid_hotplug_queue* msg)
/* Release the libusb device - we are done with it */
libusb_unref_device(msg->device);

/* Clean up if the last callback was removed */
/* TODO: make the threads stop immediately if all callbacks are gone */
pthread_mutex_lock(&hid_hotplug_context.mutex);
hid_internal_hotplug_remove_postponed();
pthread_mutex_unlock(&hid_hotplug_context.mutex);
/* Cleanup note: this function is called inside a thread that the clenup function would be waiting to finish */
/* Any callbacks that await removal are removed in hid_internal_invoke_callbacks */
/* No further cleaning is needed */
}

static void* callback_thread(void* user_data)
Expand All @@ -1224,21 +1220,26 @@ static void* callback_thread(void* user_data)

hidapi_thread_mutex_lock(&hid_hotplug_context.callback_thread);

/* We use the presence of callbacks as a marker to continue running the thread */
/* However, if there are any events left, we keep running even if there are no callbacks left, to empty the queue before the thread stops */
while (hid_hotplug_context.hotplug_cbs || hid_hotplug_context.queue) {
/* We stop the thread if by the moment there are no events left in the queue there are no callbacks left */
while (1) {
/* Make the tread fall asleep and wait for a condition to wake it up */
hidapi_thread_cond_timedwait(&hid_hotplug_context.callback_thread, &ts);

/* We use this thread's mutex to protect the queue */
hidapi_thread_mutex_lock(&hid_hotplug_context.libusb_thread);
while (hid_hotplug_context.queue) {
process_hotplug_event(hid_hotplug_context.queue);
hid_hotplug_event* cur_event = hid_hotplug_context.queue;
process_hotplug_event(cur_event);

/* Empty the queue */
hid_hotplug_context.queue = hid_hotplug_context.queue->next;
cur_event = cur_event->next;
free(hid_hotplug_context.queue);
hid_hotplug_context.queue = cur_event;
}
hidapi_thread_mutex_unlock(&hid_hotplug_context.libusb_thread);

/* Make the tread fall asleep and wait for a condition to wake it up */
hidapi_thread_cond_timedwait(&hid_hotplug_context.callback_thread, &ts);
if (!hid_hotplug_context.hotplug_cbs) {
break;
}
}

/* Cleanup connected device list */
Expand Down Expand Up @@ -1271,7 +1272,11 @@ static void* hotplug_thread(void* user_data)
libusb_hotplug_deregister_callback(usb_context, hid_hotplug_context.callback_handle);
libusb_exit(hid_hotplug_context.context);

/* Forcibly wake up the thread so it can shut down immediately and wait for it to stop */
hidapi_thread_cond_signal(&hid_hotplug_context.callback_thread);

hidapi_thread_join(&hid_hotplug_context.callback_thread);

return NULL;
}

Expand Down
3 changes: 2 additions & 1 deletion linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ static void hid_internal_hotplug_remove_postponed()
if (!hid_hotplug_context.mutex_ready || hid_hotplug_context.mutex_in_use || !hid_hotplug_context.cb_list_dirty) {
return;
}

/* Traverse the list of callbacks and check if any were marked for removal */
struct hid_hotplug_callback **current = &hid_hotplug_context.hotplug_cbs;
while (*current) {
Expand All @@ -965,6 +965,7 @@ static void hid_internal_hotplug_cleanup()
return;
}

/* Before checking if the list is empty, clear any entries whose removal was postponed first */
hid_internal_hotplug_remove_postponed();

if (hid_hotplug_context.hotplug_cbs != NULL) {
Expand Down
32 changes: 32 additions & 0 deletions mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,40 @@ static struct hid_hotplug_context {
.devs = NULL
};

static void hid_internal_hotplug_remove_postponed()
{
/* Unregister the callbacks whose removal was postponed */
/* This function is always called inside a locked mutex */
/* However, any actions are only allowed if the mutex is NOT in use and if the DIRTY flag is set */
if (!hid_hotplug_context.mutex_ready || hid_hotplug_context.mutex_in_use || !hid_hotplug_context.cb_list_dirty) {
return;
}

/* Traverse the list of callbacks and check if any were marked for removal */
struct hid_hotplug_callback **current = &hid_hotplug_context.hotplug_cbs;
while (*current) {
struct hid_hotplug_callback *callback = *current;
if (!callback->events) {
*current = (*current)->next;
free(callback);
continue;
}
current = &callback->next;
}

/* Clear the flag so we don't start the cycle unless necessary */
hid_hotplug_context.cb_list_dirty = 0;
}

static void hid_internal_hotplug_cleanup()
{
if (!hid_hotplug_context.mutex_ready || hid_hotplug_context.mutex_in_use) {
return;
}

/* Before checking if the list is empty, clear any entries whose removal was postponed first */
hid_internal_hotplug_remove_postponed();

if (hid_hotplug_context.hotplug_cbs != NULL) {
return;
}
Expand Down

0 comments on commit 2bc6b5f

Please sign in to comment.