Add two new functions, g_source_set_funcs and g_source_is_destroyed, that
authorMatthias Clasen <mclasen@redhat.com>
Fri, 2 Jun 2006 02:36:30 +0000 (02:36 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Fri, 2 Jun 2006 02:36:30 +0000 (02:36 +0000)
2006-06-01  Matthias Clasen  <mclasen@redhat.com>

* glib/glib.symbols:
* glib/gmain.h:
* glib/gmain.c: Add two new functions,
g_source_set_funcs and g_source_is_destroyed,
that will be necessary to solve thread-safety
issues with idles in GTK+.  (#321886, Chris Wilson)

ChangeLog
ChangeLog.pre-2-12
glib/glib.symbols
glib/gmain.c
glib/gmain.h

index 95a25090e271d1c21d5565e0da13da0ef5b9827f..1580606fcd6d0bd843c2648a4f2b7d4c9cb02da5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2006-06-01  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib.symbols:
+       * glib/gmain.h: 
+       * glib/gmain.c: Add two new functions, 
+       g_source_set_funcs and g_source_is_destroyed,
+       that will be necessary to solve thread-safety
+       issues with idles in GTK+.  (#321886, Chris Wilson)
+
 2006-06-01  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/giochannel.c (g_io_channel_write_chars): Avoid
index 95a25090e271d1c21d5565e0da13da0ef5b9827f..1580606fcd6d0bd843c2648a4f2b7d4c9cb02da5 100644 (file)
@@ -1,3 +1,12 @@
+2006-06-01  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib.symbols:
+       * glib/gmain.h: 
+       * glib/gmain.c: Add two new functions, 
+       g_source_set_funcs and g_source_is_destroyed,
+       that will be necessary to solve thread-safety
+       issues with idles in GTK+.  (#321886, Chris Wilson)
+
 2006-06-01  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/giochannel.c (g_io_channel_write_chars): Avoid
index 4c7b7c1bdc3479ee8f23455f0aacd9da9dfcf294..0058353a0447da7d82f4da9aaa464715b6c39fe6 100644 (file)
@@ -583,6 +583,7 @@ g_main_context_unref
 g_main_context_wait
 g_main_context_wakeup
 g_main_depth
+g_main_current_source
 g_main_loop_get_context
 g_main_loop_is_running
 g_main_loop_new
@@ -607,6 +608,8 @@ g_source_remove_poll
 g_source_set_callback
 g_source_set_callback_indirect
 g_source_set_can_recurse
+g_source_set_funcs
+g_source_is_destroyed
 g_source_set_priority
 g_source_unref
 g_idle_add
index 8a0cbb97ae0a65061c33a747a8799e67327686c9..de9cc222faa546ec57ba95232a740d14b91a6960 100644 (file)
@@ -102,6 +102,14 @@ struct _GMainWaiter
 };
 #endif  
 
+typedef struct _GMainDispatch GMainDispatch;
+
+struct _GMainDispatch
+{
+  gint depth;
+  GSList *source;
+};
+
 struct _GMainContext
 {
 #ifdef G_THREADS_ENABLED
@@ -1227,6 +1235,29 @@ g_source_set_callback (GSource        *source,
   g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs);
 }
 
+
+/**
+ * g_source_set_funcs:
+ * @source: a #GSource
+ * @funcs: the new #GSourceFuncs
+ * 
+ * Sets the source functions (can be used to override 
+ * default implementations) of an unattached source.
+ * 
+ * Since: 2.12
+ */
+void
+g_source_set_funcs (GSource     *source,
+                  GSourceFuncs *funcs)
+{
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (source->context == NULL);
+  g_return_if_fail (source->ref_count > 0);
+  g_return_if_fail (funcs != NULL);
+
+  source->source_funcs = funcs;
+}
+
 /**
  * g_source_set_priority:
  * @source: a #GSource
@@ -1690,19 +1721,18 @@ g_get_current_time (GTimeVal *result)
 
 /* Running the main loop */
 
-static gint *
-get_depth_pointer (void)
+static GMainDispatch *
+get_dispatch (void)
 {
   static GStaticPrivate depth_private = G_STATIC_PRIVATE_INIT;
-  gint *depth_pointer = g_static_private_get (&depth_private);
-  if (!depth_pointer)
+  GMainDispatch *dispatch = g_static_private_get (&depth_private);
+  if (!dispatch)
     {
-      depth_pointer = g_new (gint, 1);
-      *depth_pointer = 0;
-      g_static_private_set (&depth_private, depth_pointer, g_free);
+      dispatch = g_slice_new0 (GMainDispatch);
+      g_static_private_set (&depth_private, dispatch, NULL);
     }
 
-  return depth_pointer;
+  return dispatch;
 }
 
 /**
@@ -1824,10 +1854,96 @@ get_depth_pointer (void)
 int
 g_main_depth (void)
 {
-  gint *depth = get_depth_pointer ();
-  return *depth;
+  GMainDispatch *dispatch = get_dispatch ();
+  return dispatch->depth;
+}
+
+/**
+ * g_main_current_source:
+ *
+ * Return value: The currently firing source for this thread or NULL.
+ */
+GSource *
+g_main_current_source (void)
+{
+  GMainDispatch *dispatch = get_dispatch ();
+  return dispatch->source ? dispatch->source->data : NULL;
 }
 
+/**
+ * g_source_is_destroyed:
+ * @source: a #GSource
+ *
+ * Returns whether @source has been destroyed.
+ *
+ * This is important when you operate upon your objects 
+ * from within idle handlers, but may have freed the object 
+ * before the dispatch of your idle handler.
+ *
+ * <informalexample><programlisting>
+ * static gboolean 
+ * idle_callback (gpointer data)
+ * {
+ *   SomeWidget *self = data;
+ *    
+ *   GDK_THREADS_ENTER ();
+ *   /<!-- -->* do stuff with self *<!-- -->/
+ *   self->idle_id = 0;
+ *   GDK_THREADS_LEAVE ();
+ *    
+ *   return FALSE;
+ * }
+ *
+ * static void 
+ * some_widget_do_stuff_later (SomeWidget *self)
+ * {
+ *   self->idle_id = g_idle_add (idle_callback, self);
+ * }
+ *
+ * static void 
+ * some_widget_finalize (GObject *object)
+ * {
+ *   SomeWidget *self = SOME_WIDGET (object);
+ *   
+ *   if (self->idle_id)
+ *     g_source_remove (self->idle_id);
+ *   
+ *   G_OBJECT_CLASS (parent_class)->finalize (object);
+ * }
+ * </programlisting></informalexample>
+ *
+ * This will fail in a multi-threaded application if the 
+ * widget is destroyed before the idle handler fires due 
+ * to the use after free in the callback. A solution, to 
+ * this particular problem, is to check to if the source
+ * has already been destroy within the callback.
+ *
+ * <informalexample><programlisting>
+ * static gboolean 
+ * idle_callback (gpointer data)
+ * {
+ *   SomeWidget *self = data;
+ *   
+ *   GDK_THREADS_ENTER ();
+ *   if (!g_source_is_destroyed (g_main_get_current_source ()))
+ *     {
+ *       /<!-- -->* do stuff with self *<!-- -->/
+ *     }
+ *   GDK_THREADS_LEAVE ();
+ *   
+ *   return FALSE;
+ * }
+ * </programlisting></informalexample>
+ *
+ * Return value: %TRUE if the source has been destroyed
+ */
+gboolean
+g_source_is_destroyed (GSource *source)
+{
+  return SOURCE_DESTROYED (source);
+}
+
+
 /* Temporarily remove all this source's file descriptors from the
  * poll(), so that if data comes available for one of the file descriptors
  * we don't continually spin in the poll()
@@ -1869,7 +1985,7 @@ unblock_source (GSource *source)
 static void
 g_main_dispatch (GMainContext *context)
 {
-  gint *depth = get_depth_pointer ();
+  GMainDispatch *current = get_dispatch ();
   guint i;
 
   for (i = 0; i < context->pending_dispatches->len; i++)
@@ -1912,11 +2028,13 @@ g_main_dispatch (GMainContext *context)
 
          UNLOCK_CONTEXT (context);
 
-         (*depth)++;
+         current->depth++;
+         current->source = g_slist_prepend (current->source, source);
          need_destroy = ! dispatch (source,
                                     callback,
                                     user_data);
-         (*depth)--;
+         current->source = g_slist_remove (current->source, source);
+         current->depth--;
          
          if (cb_funcs)
            cb_funcs->unref (cb_data);
index b064e64b1874df35ff5d646913b87ec359be1b07..035b61524ffb468bf16a074121977f58ac0eca5a 100644 (file)
@@ -177,19 +177,21 @@ gint     g_main_context_check    (GMainContext *context,
                                  gint          n_fds);
 void     g_main_context_dispatch (GMainContext *context);
 
-void      g_main_context_set_poll_func (GMainContext *context,
-                                       GPollFunc     func);
+void     g_main_context_set_poll_func (GMainContext *context,
+                                      GPollFunc     func);
 GPollFunc g_main_context_get_poll_func (GMainContext *context);
 
 /* Low level functions for use by source implementations
  */
-void g_main_context_add_poll      (GMainContext *context,
-                                  GPollFD      *fd,
-                                  gint          priority);
-void g_main_context_remove_poll   (GMainContext *context,
-                                  GPollFD      *fd);
+void     g_main_context_add_poll    (GMainContext *context,
+                                    GPollFD      *fd,
+                                    gint          priority);
+void     g_main_context_remove_poll (GMainContext *context,
+                                    GPollFD      *fd);
+
+gint     g_main_depth               (void);
+GSource *g_main_current_source      (void);
 
-int g_main_depth (void);
 
 /* GMainLoop: */
 
@@ -223,11 +225,14 @@ guint    g_source_get_id          (GSource        *source);
 
 GMainContext *g_source_get_context (GSource       *source);
 
-void g_source_set_callback          (GSource              *source,
-                                    GSourceFunc           func,
-                                    gpointer              data,
-                                    GDestroyNotify        notify);
+void     g_source_set_callback    (GSource        *source,
+                                  GSourceFunc     func,
+                                  gpointer        data,
+                                  GDestroyNotify  notify);
 
+void     g_source_set_funcs       (GSource        *source,
+                                   GSourceFuncs   *funcs);
+gboolean g_source_is_destroyed    (GSource        *source);
 
 /* Used to implement g_source_connect_closure and internally*/
 void g_source_set_callback_indirect (GSource              *source,