summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2022-10-05 16:18:40 +0100
committerRichard Hughes <richard@hughsie.com>2022-10-12 15:58:15 +0100
commitcfb40102b0985d52cbd5d9348dedd1a4cc06b10c (patch)
treee1d99c182dbe9cba6e49ef81233c42085ca8854f
parentfcd54ab3ea6c69f3f9043cfdcf640921fecd3254 (diff)
downloadgusb-cfb40102b0985d52cbd5d9348dedd1a4cc06b10c.tar.gz
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.
-rw-r--r--gusb/gusb-context.c19
1 files 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,12 +140,20 @@ 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)
{
GUsbContext *self = G_USB_CONTEXT(object);
@@ -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);
}