From cfb40102b0985d52cbd5d9348dedd1a4cc06b10c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 5 Oct 2022 16:18:40 +0100 Subject: Use GRecMutex to avoid a single-threaded lockup If we call libusb_get_bos_descriptor() in the hotplug device-add handler then we eventually call libusb_handle_events_completed() which can run handle_events -- which might emit devices. Using a mutex that's safe for recursing prevents the deadlock. This fixes half the problem when getting descriptors on deeply nested USB hubs. --- gusb/gusb-context.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/gusb/gusb-context.c b/gusb/gusb-context.c index 5fb7977..f19fd52 100644 --- a/gusb/gusb-context.c +++ b/gusb/gusb-context.c @@ -50,7 +50,7 @@ typedef struct { libusb_context *ctx; libusb_hotplug_callback_handle hotplug_id; GPtrArray *idle_events; - GMutex idle_events_mutex; + GRecMutex idle_events_mutex; guint idle_events_id; } GUsbContextPrivate; @@ -140,11 +140,19 @@ g_usb_context_dispose(GObject *object) g_clear_pointer(&priv->dict_replug, g_hash_table_unref); g_clear_pointer(&priv->ctx, libusb_exit); g_clear_pointer(&priv->idle_events, g_ptr_array_unref); - g_mutex_clear(&priv->idle_events_mutex); G_OBJECT_CLASS(g_usb_context_parent_class)->dispose(object); } +static void +g_usb_context_finalize(GObject *object) +{ + GUsbContext *self = G_USB_CONTEXT(object); + GUsbContextPrivate *priv = GET_PRIVATE(self); + g_rec_mutex_clear(&priv->idle_events_mutex); + G_OBJECT_CLASS(g_usb_context_parent_class)->finalize(object); +} + static void g_usb_context_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { @@ -191,6 +199,7 @@ g_usb_context_class_init(GUsbContextClass *klass) GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->dispose = g_usb_context_dispose; + object_class->finalize = g_usb_context_finalize; object_class->get_property = g_usb_context_get_property; object_class->set_property = g_usb_context_set_property; @@ -524,7 +533,7 @@ 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(GRecMutexLocker) locker = g_rec_mutex_locker_new(&priv->idle_events_mutex); g_assert(locker != NULL); @@ -558,7 +567,7 @@ g_usb_context_hotplug_cb(struct libusb_context *ctx, GUsbContext *self = G_USB_CONTEXT(user_data); GUsbContextIdleHelper *helper; GUsbContextPrivate *priv = GET_PRIVATE(self); - g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_events_mutex); + g_autoptr(GRecMutexLocker) locker = g_rec_mutex_locker_new(&priv->idle_events_mutex); g_assert(locker != NULL); @@ -824,7 +833,7 @@ g_usb_context_init(GUsbContext *self) priv->dict_replug = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* to escape the thread into the mainloop */ - g_mutex_init(&priv->idle_events_mutex); + g_rec_mutex_init(&priv->idle_events_mutex); priv->idle_events = g_ptr_array_new_with_free_func((GDestroyNotify)g_usb_context_idle_helper_free); } -- cgit v1.2.1