From 373da242d0c82444e233bdeccb25963f68f8f4c4 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 23 Aug 2023 09:32:04 -0300 Subject: [PATCH] core/calendar: Ensure notify happens in main thread The notify signals of ESourceSelectable seem to come from the ESourceRegistry thread, and this has the potential to derail the execution of Calendar. This might be true for all signals we connect to. Make sure to always emit notify signals on the main thread. --- src/core/gcal-calendar.c | 124 +++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 11 deletions(-) diff --git a/src/core/gcal-calendar.c b/src/core/gcal-calendar.c index 5943b5eb..a56937a7 100644 --- a/src/core/gcal-calendar.c +++ b/src/core/gcal-calendar.c @@ -21,6 +21,7 @@ #define G_LOG_DOMAIN "GcalCalendar" #include "gcal-calendar.h" +#include "gcal-core-macros.h" #include "gcal-debug.h" #include "gcal-utils.h" @@ -38,10 +39,21 @@ typedef struct gulong readonly_changed_handler_id; gulong visible_changed_handler_id; gboolean initialized; + + struct { + GMutex mutex; + guint update_idle_id; + gboolean color_changed; + gboolean name_changed; + gboolean readonly_changed; + gboolean visibility_changed; + } shared; } GcalCalendarPrivate; static void g_initable_iface_init (GInitableIface *iface); +static gboolean update_idle_cb (gpointer data); + G_DEFINE_TYPE_WITH_CODE (GcalCalendar, gcal_calendar, G_TYPE_OBJECT, G_ADD_PRIVATE (GcalCalendar) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, g_initable_iface_init) @@ -84,6 +96,18 @@ save_calendar (GcalCalendar *self) e_source_write (priv->source, NULL, NULL, NULL); } +static void +schedule_idle_update_unlocked (GcalCalendar *self) +{ + GcalCalendarPrivate *priv = gcal_calendar_get_instance_private (self); + + if (priv->shared.update_idle_id == 0) + priv->shared.update_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + update_idle_cb, + g_object_ref (self), + g_object_unref); +} + static void update_color (GcalCalendar *self) { @@ -103,13 +127,76 @@ update_color (GcalCalendar *self) * Callbacks */ +static gboolean +update_idle_cb (gpointer data) +{ + g_autoptr (GMutexLocker) locker = NULL; + GcalCalendarPrivate *priv; + GcalCalendar *self; + + GCAL_ENTRY; + + g_assert (GCAL_IS_MAIN_THREAD ()); + + self = GCAL_CALENDAR (data); + priv = gcal_calendar_get_instance_private (self); + + locker = g_mutex_locker_new (&priv->shared.mutex); + + if (priv->shared.name_changed) + { + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]); + priv->shared.name_changed = FALSE; + } + + if (priv->shared.color_changed) + { + update_color (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]); + priv->shared.color_changed = FALSE; + } + + if (priv->shared.readonly_changed) + { + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_READ_ONLY]); + priv->shared.readonly_changed = FALSE; + } + + if (priv->shared.visibility_changed) + { + ESourceSelectable *selectable_extension; + gboolean visible; + + selectable_extension = e_source_get_extension (priv->source, E_SOURCE_EXTENSION_CALENDAR); + visible = e_source_selectable_get_selected (selectable_extension); + + if (visible != priv->visible) + { + priv->visible = visible; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]); + } + + priv->shared.visibility_changed = FALSE; + } + + priv->shared.update_idle_id = 0; + + GCAL_RETURN (G_SOURCE_REMOVE); +} + static void on_source_color_changed_cb (ESourceSelectable *source, GParamSpec *pspec, GcalCalendar *self) { - update_color (self); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]); + g_autoptr (GMutexLocker) locker = NULL; + GcalCalendarPrivate *priv; + + priv = gcal_calendar_get_instance_private (self); + locker = g_mutex_locker_new (&priv->shared.mutex); + + priv->shared.color_changed = TRUE; + schedule_idle_update_unlocked (self); } static void @@ -117,7 +204,14 @@ on_source_name_changed_cb (ESource *source, GParamSpec *pspec, GcalCalendar *self) { - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]); + g_autoptr (GMutexLocker) locker = NULL; + GcalCalendarPrivate *priv; + + priv = gcal_calendar_get_instance_private (self); + locker = g_mutex_locker_new (&priv->shared.mutex); + + priv->shared.name_changed = TRUE; + schedule_idle_update_unlocked (self); } static void @@ -125,7 +219,14 @@ on_client_readonly_changed_cb (EClient *client, GParamSpec *pspec, GcalCalendar *self) { - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_READ_ONLY]); + g_autoptr (GMutexLocker) locker = NULL; + GcalCalendarPrivate *priv; + + priv = gcal_calendar_get_instance_private (self); + locker = g_mutex_locker_new (&priv->shared.mutex); + + priv->shared.readonly_changed = TRUE; + schedule_idle_update_unlocked (self); } static void @@ -133,17 +234,14 @@ on_source_visible_changed_cb (ESourceSelectable *source, GParamSpec *pspec, GcalCalendar *self) { + g_autoptr (GMutexLocker) locker = NULL; GcalCalendarPrivate *priv; - gboolean visible; priv = gcal_calendar_get_instance_private (self); - visible = e_source_selectable_get_selected (source); + locker = g_mutex_locker_new (&priv->shared.mutex); - if (visible == priv->visible) - return; - - priv->visible = visible; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]); + priv->shared.visibility_changed = TRUE; + schedule_idle_update_unlocked (self); } @@ -246,6 +344,10 @@ gcal_calendar_finalize (GObject *object) gcal_clear_signal_handler (&priv->readonly_changed_handler_id, priv->client); gcal_clear_signal_handler (&priv->visible_changed_handler_id, selectable_extension); + g_mutex_lock (&priv->shared.mutex); + g_clear_handle_id (&priv->shared.update_idle_id, g_source_remove); + g_mutex_unlock (&priv->shared.mutex); + g_clear_object (&priv->client); g_clear_object (&priv->source);