g_main_context_depth()



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]