Bug 623400 - acquire context before dispatching
authorRyan Lortie <desrt@desrt.ca>
Sun, 3 Oct 2010 21:30:10 +0000 (17:30 -0400)
committerRyan Lortie <desrt@desrt.ca>
Sun, 3 Oct 2010 21:34:16 +0000 (17:34 -0400)
For GSettings.

Use the functionality introduced in the last commit to simplify our
notify dispatching and increase the safety of doing so (by ensuring that
the context is acquired in the current thread for the duration of the
dispatch).

This closes bugs #623400 and #629849.

gio/gdelayedsettingsbackend.c
gio/gsettingsbackend.c
gio/gsettingsbackendinternal.h

index bb3195a..dce1d7d 100644 (file)
@@ -70,21 +70,7 @@ g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed)
   g_static_mutex_unlock (&delayed->priv->lock);
 
   if (target != NULL)
-    {
-      if (g_settings_backend_get_active_context () != target_context)
-        {
-          GSource *source;
-
-          source = g_idle_source_new ();
-          g_source_set_priority (source, G_PRIORITY_DEFAULT);
-          g_source_set_callback (source, invoke_notify_unapplied,
-                                 target, g_object_unref);
-          g_source_attach (source, target_context);
-          g_source_unref (source);
-        }
-      else
-        invoke_notify_unapplied (target);
-    }
+    g_main_context_invoke (target_context, invoke_notify_unapplied, target);
 }
 
 
index bde0336..475d055 100644 (file)
@@ -119,26 +119,6 @@ is_path (const gchar *path)
   return TRUE;
 }
 
-GMainContext *
-g_settings_backend_get_active_context (void)
-{
-  GMainContext *context;
-  GSource *source;
-
-  if ((source = g_main_current_source ()))
-    context = g_source_get_context (source);
-
-  else
-    {
-      context = g_main_context_get_thread_default ();
-
-      if (context == NULL)
-        context = g_main_context_default ();
-    }
-
-  return context;
-}
-
 struct _GSettingsBackendWatch
 {
   GObject                       *target;
@@ -316,20 +296,7 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
                                     GBoxedFreeFunc    data1_free,
                                     gpointer          data2)
 {
-  GMainContext *context, *here_and_now;
-  GSettingsBackendWatch *watch;
-
-  /* We need to hold the mutex here (to prevent a node from being
-   * deleted as we are traversing the list).  Since we should not
-   * re-enter user code while holding this mutex, we create a
-   * one-time-use GMainContext and populate it with the events that we
-   * would have called directly.  We dispatch these events after
-   * releasing the lock.  Note that the GObject reference is acquired on
-   * the target while holding the mutex and the mutex needs to be held
-   * as part of the destruction of any GSettings instance (via the weak
-   * reference handling).  This is the key to the safety of the whole
-   * setup.
-   */
+  GSettingsBackendWatch *suffix, *watch, *next;
 
   if (data1_copy == NULL)
     data1_copy = pointer_id;
@@ -337,19 +304,34 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
   if (data1_free == NULL)
     data1_free = pointer_ignore;
 
-  context = g_settings_backend_get_active_context ();
-  here_and_now = g_main_context_new ();
-
-  /* traverse the (immutable while holding lock) list */
+  /* We're in a little bit of a tricky situation here.  We need to hold
+   * a lock while traversing the list, but we don't want to hold the
+   * lock while calling back into user code.
+   *
+   * Since we're not holding the lock while we call user code, we can't
+   * render the list immutable.  We can, however, store a pointer to a
+   * given suffix of the list and render that suffix immutable.
+   *
+   * Adds will never modify the suffix since adds always come in the
+   * form of prepends.  We can also prevent removes from modifying the
+   * suffix since removes only happen in response to the last reference
+   * count dropping -- so just add a reference to everything in the
+   * suffix.
+   */
   g_static_mutex_lock (&backend->priv->lock);
-  for (watch = backend->priv->watches; watch; watch = watch->next)
+  suffix = backend->priv->watches;
+  for (watch = suffix; watch; watch = watch->next)
+    g_object_ref (watch->target);
+  g_static_mutex_unlock (&backend->priv->lock);
+
+  /* The suffix is now immutable, so this is safe. */
+  for (watch = suffix; watch; watch = next)
     {
       GSettingsBackendClosure *closure;
-      GSource *source;
 
       closure = g_slice_new (GSettingsBackendClosure);
       closure->backend = g_object_ref (backend);
-      closure->target = g_object_ref (watch->target);
+      closure->target = watch->target; /* we took our ref above */
       closure->function = G_STRUCT_MEMBER (void *, watch->vtable,
                                            function_offset);
       closure->name = g_strdup (name);
@@ -357,23 +339,18 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
       closure->data1_free = data1_free;
       closure->data2 = data2;
 
-      source = g_idle_source_new ();
-      g_source_set_priority (source, G_PRIORITY_DEFAULT);
-      g_source_set_callback (source,
-                             g_settings_backend_invoke_closure,
-                             closure, NULL);
+      /* we do this here because 'watch' may not live to the end of this
+       * iteration of the loop (since we may unref the target below).
+       */
+      next = watch->next;
 
-      if (watch->context && watch->context != context)
-        g_source_attach (source, watch->context);
+      if (watch->context)
+        g_main_context_invoke (watch->context,
+                               g_settings_backend_invoke_closure,
+                               closure);
       else
-        g_source_attach (source, here_and_now);
-
-      g_source_unref (source);
+        g_settings_backend_invoke_closure (closure);
     }
-  g_static_mutex_unlock (&backend->priv->lock);
-
-  while (g_main_context_iteration (here_and_now, FALSE));
-  g_main_context_unref (here_and_now);
 }
 
 /**
index 68a61a8..a6711cb 100644 (file)
@@ -92,8 +92,6 @@ G_GNUC_INTERNAL
 GPermission *           g_settings_backend_get_permission               (GSettingsBackend               *backend,
                                                                          const gchar                    *path);
 G_GNUC_INTERNAL
-GMainContext *          g_settings_backend_get_active_context           (void);
-G_GNUC_INTERNAL
 GSettingsBackend *      g_settings_backend_get_default                  (void);
 G_GNUC_INTERNAL
 void                    g_settings_backend_sync_default                 (void);