g_main_context_depth()
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gnome org
- Cc: timj gtk org
- Subject: g_main_context_depth()
- Date: Mon, 05 Jan 2004 16:40:01 -0500
Tim asked me for something like the following.
int g_main_context_depth (GMainContext *context);
Returns the depth of the stack of calls to
g_main_context_dispatch() on @context 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. 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. There is the temptation
to 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:
1) Use gtk_widget_set_sensitive() or modal dialogs to prevent
the user from interacting with elements while the main
loop is recursing.
2) Avoid 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.
As can be seen from the above, I'm not crazy about adding the function
- I don't know any valid uses of it other than the one case described
in detail above, which seems like a lame replacement for alloca()
essentially. (The one advantage is that you can return the allocated
memory to a caller. The huge disadvantage is that you can get
into difficulties if you start using memory from within a loop.)
- It's very tempting to use it in situations that aren't
appropriate. (The equivalent Qt function that inspired the request
has been deprecated, though the docs don't elaborate on why.)
I'd be interested to hear if other people have uses for this function,
or if Tim wants to say a bit more on why main-looop-depth controlled
GC is cool enough to be worth special support in GObject.
Thanks,
Owen
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]