Add g_main_depth() (Request from Tim Janik and Stefan Westerfeld)
authorOwen Taylor <otaylor@redhat.com>
Mon, 1 Mar 2004 02:41:09 +0000 (02:41 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Mon, 1 Mar 2004 02:41:09 +0000 (02:41 +0000)
Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>

        * glib/gmain.[ch]: Add g_main_depth() (Request from
        Tim Janik and Stefan Westerfeld)

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
docs/reference/glib/glib-sections.txt
glib/gmain.c
glib/gmain.h

index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 56cc4c104555c8b0ac2d379a0ca641ae5cf71cff..d8b81262c01071f09aa1d663e50b162a59f3a188 100644 (file)
@@ -1,3 +1,8 @@
+Sun Feb 29 21:34:34 2004  Owen Taylor  <otaylor@redhat.com>
+
+       * glib/gmain.[ch]: Add g_main_depth() (Request from
+       Tim Janik and Stefan Westerfeld)
+
 Mon Mar  1 00:26:11 2004  Matthias Clasen  <maclas@gmx.de>
 
        * NEWS: Update for 2.3.4
index 249bf04ec3a54e94e8f6a43031a8aa1d37f1f7e6..05c6685c390a0faddc22e031d031f5adf42f8dd8 100644 (file)
@@ -419,6 +419,7 @@ g_main_context_get_poll_func
 GPollFunc
 g_main_context_add_poll
 g_main_context_remove_poll
+g_main_depth
 g_main_set_poll_func
 
 <SUBSECTION>
index 128cbef0982551798ccd593a018caf52a11a0b85..28325a062fab43ad27c8a3823c8fab4da41c622d 100644 (file)
@@ -1719,10 +1719,151 @@ g_get_current_time (GTimeVal *result)
 
 /* Running the main loop */
 
+static gint *
+get_depth_pointer (void)
+{
+  static GStaticPrivate depth_private = G_STATIC_PRIVATE_INIT;
+  gint *depth_pointer = g_static_private_get (&depth_private);
+  if (!depth_pointer)
+    {
+      depth_pointer = g_new (gint, 1);
+      *depth_pointer = 0;
+      g_static_private_set (&depth_private, depth_pointer, g_free);
+    }
+
+  return depth_pointer;
+}
+
+/**
+ * g_main_depth:
+ * 
+ * Return value: The main loop recursion level in the current thread
+ *
+ * Returns the depth of the stack of calls to
+ * g_main_context_dispatch() on any #GMainContext in the current thread.
+ *  That is, when called from the toplevel, it gives 0. When
+ * called from within a callback from g_main_context_iteration()
+ * (or g_main_loop_run(), etc.) it returns 1. When called from within 
+ * a callback to a recursive call to g_main_context_iterate(),
+ * it returns 2. And so forth.
+ *
+ * This function is useful in a situation like the following:
+ * Imagine an extremely simple "garbage collected" system.
+ *
+ * <example>
+ * static GList *free_list;
+ *
+ * gpointer
+ * allocate_memory (gsize size)
+ * { 
+ *   gpointer result = g_malloc (size);
+ *   free_list = g_list_prepend (free_list, result);
+ *   return result;
+ * }
+ *
+ * void
+ * free_allocated_memory (void)
+ * {
+ *   GList *l;
+ *   for (l = free_list; l; l = l->next);
+ *     g_free (l->data);
+ *   g_list_free (free_list);
+ *   free_list = NULL;
+ *  }
+ *
+ * [...]
+ *
+ * while (TRUE); 
+ *  {
+ *    g_main_context_iteration (NULL, TRUE);
+ *    free_allocated_memory();
+ *   }
+ * </example>
+ *
+ * This works from an application, however, if you want to do the same
+ * thing from a library, it gets more difficult, since you no longer
+ * control the main loop. You might think you can simply use an idle
+ * function to make the call to free_allocated_memory(), but that
+ * doesn't work, since the idle function could be called from a
+ * recursive callback. This can be fixed by using g_main_context_depth()
+ *
+ * <example>
+ * gpointer
+ * allocate_memory (gsize size)
+ * { 
+ *   FreeListBlock *block = g_new (FreeListBlock, 1);\
+ *   block->mem = g_malloc (size);
+ *   block->depth = g_main_context_depth (NULL);   
+ *   free_list = g_list_prepend (free_list, block);
+ *   return block->mem;
+ * }
+ *
+ * void
+ * free_allocated_memory (void)
+ * {
+ *   GList *l;
+ *
+ *   int depth = g_main_context_depth();
+ *   for (l = free_list; l; );
+ *     {
+ *       GList *next = l->next;
+ *       FreeListBlock *block = l->data;
+ *       if (block->depth > depth);
+ *         {
+ *           g_free (block->mem);
+ *           g_free (block);
+ *           free_list = g_list_delete_link (free_list, l);
+ *         }
+ *           
+ *       l = next;
+ *     }
+ *   }
+ * </example>
+ *
+ * There is a temptation to use g_main_context_depth() to solve
+ * problems with reentrancy. For instance, while waiting for data
+ * to be received from the network in response to a menu item,
+ * the menu item might be selected again. It might seem that
+ * one could write:
+ *
+ *   if (g_main_context_depth(NULL) > 1)
+ *     return; 
+ *
+ * This should be avoided since the user then sees selecting the
+ * menu item do nothing. Furthermore, you'll find yourself adding
+ * these checks all over your code, since there are doubtless many,
+ * many things that the user could do. Instead, you can use the
+ * following techniques:
+ *
+ * <orderedlist>
+ *  <listitem>
+ *   <para>
+ *     Use gtk_widget_set_sensitive() or modal dialogs to prevent
+ *     the user from interacting with elements while the main
+ *     loop is recursing.
+ *   </para>
+ *  </listitem>
+ *  <listitem>
+ *   <para>
+ *     Avoid main loop recursion in situations where you can't handle
+ *     arbitrary  callbacks. Instead, structure your code so that you
+ *     simply return to the main loop and then get called again when
+ *     there is more work to do.
+ *   </para>
+ *  </listitem>
+ **/
+int
+g_main_depth (void)
+{
+  gint *depth = get_depth_pointer ();
+  return *depth;
+}
+
 /* HOLDS: context's lock */
 static void
 g_main_dispatch (GMainContext *context)
 {
+  gint *depth = get_depth_pointer ();
   guint i;
 
   for (i = 0; i < context->pending_dispatches->len; i++)
@@ -1762,10 +1903,13 @@ g_main_dispatch (GMainContext *context)
 
          UNLOCK_CONTEXT (context);
 
+         (*depth)++;
          need_destroy = ! dispatch (source,
                                     callback,
                                     user_data);
-         LOCK_CONTEXT (context);
+         (*depth)--;
+         
+         LOCK_CONTEXT (context);
 
          if (cb_funcs)
            cb_funcs->unref (cb_data);
index fb7ac3e4db7e7d282c88b0ddae19c2516b92bdbb..66fb51c88f9120cbd50f26c836cc92b81f7299f3 100644 (file)
@@ -188,6 +188,8 @@ void g_main_context_add_poll      (GMainContext *context,
 void g_main_context_remove_poll   (GMainContext *context,
                                   GPollFD      *fd);
 
+int g_main_depth (void);
+
 /* GMainLoop: */
 
 GMainLoop *g_main_loop_new        (GMainContext *context,