From 74666dc64792ea6550f8dce78fed1f21bab7a21f Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 17 Oct 2022 13:59:22 +0100 Subject: Do not call the hotplug callback with the mutex held This fixes a regression introduced in 0ff5cca6 which causes a rare deadlock in fwupd. --- gusb/gusb-context.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/gusb/gusb-context.c b/gusb/gusb-context.c index 5fb7977..0a37b17 100644 --- a/gusb/gusb-context.c +++ b/gusb/gusb-context.c @@ -519,17 +519,35 @@ g_usb_context_idle_helper_free(GUsbContextIdleHelper *helper) g_free(helper); } +static gpointer +g_usb_context_idle_helper_copy(gconstpointer src, gpointer user_data) +{ + GUsbContextIdleHelper *helper_src = (GUsbContextIdleHelper *)src; + GUsbContextIdleHelper *helper_dst = g_new0(GUsbContextIdleHelper, 1); + helper_dst->self = g_object_ref(helper_src->self); + helper_dst->dev = libusb_ref_device(helper_src->dev); + helper_dst->event = helper_src->event; + return helper_dst; +} + +/* always in the main thread */ static gboolean g_usb_context_idle_hotplug_cb(gpointer user_data) { GUsbContext *self = G_USB_CONTEXT(user_data); GUsbContextPrivate *priv = GET_PRIVATE(self); - g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_events_mutex); + g_autoptr(GPtrArray) idle_events = NULL; - g_assert(locker != NULL); + /* drain the idle events with the lock held */ + g_mutex_lock(&priv->idle_events_mutex); + idle_events = g_ptr_array_copy(priv->idle_events, g_usb_context_idle_helper_copy, NULL); + g_ptr_array_set_size(priv->idle_events, 0); + priv->idle_events_id = 0; + g_mutex_unlock(&priv->idle_events_mutex); - for (guint i = 0; i < priv->idle_events->len; i++) { - GUsbContextIdleHelper *helper = g_ptr_array_index(priv->idle_events, i); + /* run the callbacks when not locked */ + for (guint i = 0; i < idle_events->len; i++) { + GUsbContextIdleHelper *helper = g_ptr_array_index(idle_events, i); switch (helper->event) { case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: g_usb_context_add_device(helper->self, helper->dev); @@ -543,8 +561,6 @@ g_usb_context_idle_hotplug_cb(gpointer user_data) } /* all done */ - g_ptr_array_set_size(priv->idle_events, 0); - priv->idle_events_id = 0; return FALSE; } -- cgit v1.2.1