From 0875017ad13ba352b488b0116eab1fa1a12d34e5 Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Mon, 1 Mar 2004 02:41:09 +0000 Subject: [PATCH] Add g_main_depth() (Request from Tim Janik and Stefan Westerfeld) Sun Feb 29 21:34:34 2004 Owen Taylor * glib/gmain.[ch]: Add g_main_depth() (Request from Tim Janik and Stefan Westerfeld) --- ChangeLog | 5 ++ ChangeLog.pre-2-10 | 5 ++ ChangeLog.pre-2-12 | 5 ++ ChangeLog.pre-2-4 | 5 ++ ChangeLog.pre-2-6 | 5 ++ ChangeLog.pre-2-8 | 5 ++ docs/reference/glib/glib-sections.txt | 1 + glib/gmain.c | 146 +++++++++++++++++++++++++++++++++- glib/gmain.h | 2 + 9 files changed, 178 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 56cc4c1..d8b8126 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 56cc4c1..d8b8126 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 56cc4c1..d8b8126 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 56cc4c1..d8b8126 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 56cc4c1..d8b8126 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 56cc4c1..d8b8126 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,8 @@ +Sun Feb 29 21:34:34 2004 Owen Taylor + + * glib/gmain.[ch]: Add g_main_depth() (Request from + Tim Janik and Stefan Westerfeld) + Mon Mar 1 00:26:11 2004 Matthias Clasen * NEWS: Update for 2.3.4 diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 249bf04..05c6685 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -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 diff --git a/glib/gmain.c b/glib/gmain.c index 128cbef..28325a0 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -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. + * + * + * 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(); + * } + * + * + * 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() + * + * + * 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; + * } + * } + * + * + * 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: + * + * + * + * + * Use gtk_widget_set_sensitive() or modal dialogs to prevent + * the user from interacting with elements while the main + * loop is recursing. + * + * + * + * + * 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. + * + * + **/ +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); diff --git a/glib/gmain.h b/glib/gmain.h index fb7ac3e..66fb51c 100644 --- a/glib/gmain.h +++ b/glib/gmain.h @@ -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, -- 2.7.4