Blame docs/refcounting.txt

Packit Service fb6fa5
The Reference Counting Scheme of GDK an GTK+
Packit Service fb6fa5
============================================
Packit Service fb6fa5
Packit Service fb6fa5
Each data structure that provides reference counting offers a bunch of
Packit Service fb6fa5
functions that follow these conventions:
Packit Service fb6fa5
Packit Service fb6fa5
  *_new:      Create a new structure with a reference count of 1.
Packit Service fb6fa5
  *_ref:      Increase ref count by one.
Packit Service fb6fa5
  *_unref:    Decrease ref count by one.  If the count drops to zero,
Packit Service fb6fa5
              run appropriate finalization code and free the memory.
Packit Service fb6fa5
	      For data structures with a _destroy function, it will be
Packit Service fb6fa5
	      invoked at this point, if the data structure is not
Packit Service fb6fa5
              already in a destroyed state.
Packit Service fb6fa5
Packit Service fb6fa5
GtkObjects also provide the following functions:
Packit Service fb6fa5
Packit Service fb6fa5
  *_destroy:  Render an object `unusable', but as long as there are
Packit Service fb6fa5
              references to it, its allocated memory will not be freed.
Packit Service fb6fa5
  *_sink:     Clear a GtkObjects `floating' state and decrement the
Packit Service fb6fa5
	      reference count by 1.
Packit Service fb6fa5
Packit Service fb6fa5
GdkWindow
Packit Service fb6fa5
---------
Packit Service fb6fa5
Packit Service fb6fa5
A GdkWindow has to be explicitly destroyed with gdk_window_destroy.
Packit Service fb6fa5
This will send out a request to destroy this window and all its
Packit Service fb6fa5
children, and will decrement the ref_count of the GdkWindow by one.
Packit Service fb6fa5
Thus, it releases the initial reference created by gdk_window_new.
Packit Service fb6fa5
Packit Service fb6fa5
All GdkWindows are kept in a hash table to translate from their XId to
Packit Service fb6fa5
the actual structure and the pointer in the hash table is reflected in
Packit Service fb6fa5
the reference count.  When a DestroyNotify event is received for a
Packit Service fb6fa5
particular GdkWindow, it is removed from the hash table and the
Packit Service fb6fa5
ref_count is updated accordingly.
Packit Service fb6fa5
Packit Service fb6fa5
You can call gdk_window_destroy more than once on a particular
Packit Service fb6fa5
GdkWindow, it will only be destroyed when it hasn't been yet.  The
Packit Service fb6fa5
ref_count is *always* decremented, tho. Be careful.
Packit Service fb6fa5
Packit Service fb6fa5
Remark: When writing NO_WINDOW widgets, care should be taken about
Packit Service fb6fa5
        proper referencing/unreferencing of the parent's GdkWindow
Packit Service fb6fa5
        that is used by the widget.
Packit Service fb6fa5
 
Packit Service fb6fa5
GdkPixmap
Packit Service fb6fa5
---------
Packit Service fb6fa5
Packit Service fb6fa5
There is no gdk_pixmap_destroy function.  The Pixmap is destroyed when
Packit Service fb6fa5
the last reference to it vanishes.
Packit Service fb6fa5
Packit Service fb6fa5
GdkPixmaps are kept in the same hash table as GdkWindows but the
Packit Service fb6fa5
pointer in the hash table is *not* reflected in the ref_count.
Packit Service fb6fa5
Packit Service fb6fa5
This works only when Pixmaps never get XEvents.  I'm not sure if this
Packit Service fb6fa5
is the case.
Packit Service fb6fa5
Packit Service fb6fa5
GdkBitmap
Packit Service fb6fa5
---------
Packit Service fb6fa5
Packit Service fb6fa5
A GdkBitmap is only another name for a special use of GdkPixmap.
Packit Service fb6fa5
Packit Service fb6fa5
GdkVisual
Packit Service fb6fa5
---------
Packit Service fb6fa5
Packit Service fb6fa5
There are no *_new or *_destroy functions and the *_ref and *_unref
Packit Service fb6fa5
functions are no-ops.  GdkVisuals are static structures and thus do not
Packit Service fb6fa5
need reference counting.  The ref counting functions are only there
Packit Service fb6fa5
for extra defensive programming.
Packit Service fb6fa5
Packit Service fb6fa5
GdkColormap
Packit Service fb6fa5
-----------
Packit Service fb6fa5
Packit Service fb6fa5
Nothing special.  There is no gdk_colormap_destroy function.
Packit Service fb6fa5
Packit Service fb6fa5
GdkFont / GdkFontSet
Packit Service fb6fa5
--------------------
Packit Service fb6fa5
Packit Service fb6fa5
GdkFont and GdkFontSet are equivalent as far as ref counting is
Packit Service fb6fa5
concerned.  Use gdk_font_ref and gdk_font_unref for both.
Packit Service fb6fa5
Packit Service fb6fa5
There is no gdk_font_free or gdk_fontset_free function.
Packit Service fb6fa5
Packit Service fb6fa5
GtkAcceleratorTable
Packit Service fb6fa5
-------------------
Packit Service fb6fa5
Packit Service fb6fa5
There is no gtk_accelerator_table_destroy function.
Packit Service fb6fa5
Packit Service fb6fa5
GtkTooltips
Packit Service fb6fa5
-----------
Packit Service fb6fa5
Packit Service fb6fa5
There is no gtk_tooltips_destroy function.
Packit Service fb6fa5
Packit Service fb6fa5
GtkStyle
Packit Service fb6fa5
--------
Packit Service fb6fa5
Packit Service fb6fa5
There is no gtk_style_destroy function.
Packit Service fb6fa5
Packit Service fb6fa5
GtkObject
Packit Service fb6fa5
---------
Packit Service fb6fa5
Packit Service fb6fa5
GtkObjects follow the usual ref_counting strategy, but with a twist.
Packit Service fb6fa5
Packit Service fb6fa5
They are created with a ref_count of 1.  GtkObjects are able to
Packit Service fb6fa5
run finalization code when the ref_count drops to zero but you cannot
Packit Service fb6fa5
register arbitrary signal handlers to run at finalization time.
Packit Service fb6fa5
Packit Service fb6fa5
There is also the old gtk_object_destroy function and the "destroy"
Packit Service fb6fa5
signal but they are somewhat independent from finalization.  Just as
Packit Service fb6fa5
stated at the top of this text, gtk_object_destroy merely renders an
Packit Service fb6fa5
object unusable.  When the object is a container widget for example,
Packit Service fb6fa5
it unrealizes that widget, removes all children and disconnects all
Packit Service fb6fa5
signal handlers.  The finalization code is different, it would for
Packit Service fb6fa5
example free associated memory for text strings and release the
Packit Service fb6fa5
attached style.
Packit Service fb6fa5
Packit Service fb6fa5
This is the biggest change.  Every widget must be revised to have a
Packit Service fb6fa5
proper "destroy" function, etc.  Such a destroy function will only
Packit Service fb6fa5
be called once and is expected to leave the widget in a minimal but
Packit Service fb6fa5
consistent state.  Widgets that have been "destroyed" but not yet
Packit Service fb6fa5
finalized are flagged with GTK_DESTROY.  The "finalization" function
Packit Service fb6fa5
is new and should perform last-minute cleanup actions, in contrast
Packit Service fb6fa5
to the destroy function it will not be emitted as signal though.
Packit Service fb6fa5
It can assume that the "destroy" function has been called as the
Packit Service fb6fa5
last function on this widget.
Packit Service fb6fa5
Packit Service fb6fa5
Essentially, the old "destroy" function has been split into a
Packit Service fb6fa5
"finalize" plus a "destroy" function.
Packit Service fb6fa5
Packit Service fb6fa5
It is not possible to create GtkObjects with a ref_count of 0
Packit Service fb6fa5
because the first ref/unref pair will destroy it unintentionally.
Packit Service fb6fa5
Packit Service fb6fa5
To be mostly backward compatible with existing practice, a GtkObject
Packit Service fb6fa5
leads a more complicated life than the other reference counted structures.
Packit Service fb6fa5
Packit Service fb6fa5
When a GtkObject is created, it starts out in a special state called
Packit Service fb6fa5
"floating" (this is the twist).  This means that it is alive and has a
Packit Service fb6fa5
reference to it, but the `owner' of this reference is not known.
Packit Service fb6fa5
There are certain `potential owners' that will adopt a floating
Packit Service fb6fa5
GtkObject.  For GtkWidgets the most common adopters are the parent
Packit Service fb6fa5
widget.
Packit Service fb6fa5
Packit Service fb6fa5
When you want to adopt a possibly floating GtkObject, you call
Packit Service fb6fa5
gtk_object_sink on it.  This clears the floating state of the
Packit Service fb6fa5
GtkObject and decrements the ref_count by one, if it has been floating
Packit Service fb6fa5
previously.  Once the floating state has been cleared, it will never
Packit Service fb6fa5
be set again.
Packit Service fb6fa5
Packit Service fb6fa5
All widgets that are part of the display are linked into a
Packit Service fb6fa5
parent/child tree.  The link from the parent to a child is reflected
Packit Service fb6fa5
in the ref_count of the child, but the link from the child to the
Packit Service fb6fa5
parent is not reflected in the ref_count of the parent.
Packit Service fb6fa5
Packit Service fb6fa5
Like a GtkObject, a GtkWidget is created with a ref_count of 1 and
Packit Service fb6fa5
initially flagged as `floating'.  As soon as it is added as a child to
Packit Service fb6fa5
a parent, the `floating' flag is cleared and never will be set again.
Packit Service fb6fa5
Not even when it is later unparented.  The act of clearing the
Packit Service fb6fa5
`floating' flag also decrements the ref_count of the widget by one.
Packit Service fb6fa5
Packit Service fb6fa5
When the widget is unparented, its underlying GdkWindow is destroyed
Packit Service fb6fa5
(when it has one), it loses its reference from the parent and
Packit Service fb6fa5
naturally the ref_count is decremented.
Packit Service fb6fa5
Packit Service fb6fa5
It is considered a bug if a widget still has a GdkWindow when it is
Packit Service fb6fa5
being freed.
Packit Service fb6fa5
Packit Service fb6fa5
Toplevel widgets, which don't have a `natural' parent, are adopted by
Packit Service fb6fa5
special registering functions.  Because the of the reference count that
Packit Service fb6fa5
is set by the registering functions, toplevel widgets will have to be
Packit Service fb6fa5
explicitly destroyed, with the exception of GtkMenus.  GtkMenus are a
Packit Service fb6fa5
special case of toplevel widgets in that they will be `attached' to and
Packit Service fb6fa5
`detached' from other widgets.  The act of attaching a GtkMenu to a
Packit Service fb6fa5
widget will be reflected in its reference count.  The act of detaching
Packit Service fb6fa5
a GtkMenu will revert that.  Therefore GtkMenus naturally get destroyed
Packit Service fb6fa5
and finalized once they are detached from their reference holder.
Packit Service fb6fa5
Packit Service fb6fa5
So, the typical career of a GtkWindow a GtMenu attached to a
Packit Service fb6fa5
GtkOptionMenu looks like this:
Packit Service fb6fa5
Packit Service fb6fa5
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Packit Service fb6fa5
  /* window is created with ref_count == 1.  It is not flagged as
Packit Service fb6fa5
   * `floating' because it has already been registered as a toplevel
Packit Service fb6fa5
   * widget.
Packit Service fb6fa5
   */
Packit Service fb6fa5
Packit Service fb6fa5
  option_menu = gtk_option_menu_new ();
Packit Service fb6fa5
  /* option_menu->ref_count == 1 and it is flagged as `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
  
Packit Service fb6fa5
  gtk_container_add (window, option_menu);
Packit Service fb6fa5
  /* option_menu->ref_count still == 1, but it is no longer `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
  
Packit Service fb6fa5
  menu = gtk_menu_new ();
Packit Service fb6fa5
  /* menu->ref_count == 1 and it is flagged as `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
  
Packit Service fb6fa5
  menu_item = gtk_menu_item_new_with_label ("Choose Me");
Packit Service fb6fa5
  /* menu_item->ref_count == 1 and it is flagged as `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
Packit Service fb6fa5
  gtk_menu_append (GTK_MENU (menu), menu_item);
Packit Service fb6fa5
  /* menu_item->ref_count still == 1, but it is no longer `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
  
Packit Service fb6fa5
  gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
Packit Service fb6fa5
  /* menu->ref_count still == 1, but it is no longer `floating'.
Packit Service fb6fa5
   */
Packit Service fb6fa5
Packit Service fb6fa5
  gtk_widget_show (menu_item);
Packit Service fb6fa5
  gtk_widget_show (option_menu);
Packit Service fb6fa5
  gtk_widget_show (window);
Packit Service fb6fa5
Packit Service fb6fa5
  /* The widgets get their GdkWindows, nothing significant happens to
Packit Service fb6fa5
   * the ref_counts.
Packit Service fb6fa5
   */
Packit Service fb6fa5
Packit Service fb6fa5
Then, when the user wants to get rid of the window:
Packit Service fb6fa5
Packit Service fb6fa5
  gtk_widget_destroy (window);
Packit Service fb6fa5
Packit Service fb6fa5
  /* The GdkWindow of `window' and all its child GdkWindows are
Packit Service fb6fa5
   * destroyed.
Packit Service fb6fa5
   *
Packit Service fb6fa5
   * window is unregistered from the toplevel list and its ref_count
Packit Service fb6fa5
   * drops to zero.  The destroy code of `window' destroys `option_menu'.
Packit Service fb6fa5
   *
Packit Service fb6fa5
   * The destroy code of `option_menu' causes the `menu' to be detached
Packit Service fb6fa5
   * from it and its reference count drops to zero.
Packit Service fb6fa5
   *
Packit Service fb6fa5
   * The destroy code of `menu' destroys `menu_item'.
Packit Service fb6fa5
   *
Packit Service fb6fa5
   * The destruction of `menu_item' removes it from its parent, the
Packit Service fb6fa5
   * menu_item->ref_count drops to zero and `menu_item' is finalized (freed).
Packit Service fb6fa5
   *
Packit Service fb6fa5
   * Now `menu', `option_menu' and `window' will be destroyed and finalized,
Packit Service fb6fa5
   * in this order, since the reference count of each is zero.
Packit Service fb6fa5
   */
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
Taking care of proper referencing
Packit Service fb6fa5
---------------------------------
Packit Service fb6fa5
Packit Service fb6fa5
There are some cases where referencing of widgets from outside the toolkit
Packit Service fb6fa5
(on the application side) is needed.
Packit Service fb6fa5
Once the application performs an operation on a widget that will cause
Packit Service fb6fa5
its reference count to drop, if it wants to take further actions on the
Packit Service fb6fa5
widget, it needs to hold a reference to it.
Packit Service fb6fa5
Packit Service fb6fa5
Example code sequences that require reference wraps:
Packit Service fb6fa5
Packit Service fb6fa5
   /* gtk_container_remove() will unparent the child and therefore
Packit Service fb6fa5
    * cause its reference count to be decremented by one.
Packit Service fb6fa5
    */
Packit Service fb6fa5
   g_object_ref (widget);
Packit Service fb6fa5
   gtk_container_remove (container, widget);
Packit Service fb6fa5
   /* without the reference count, the widget would have been destroyed here.
Packit Service fb6fa5
   */
Packit Service fb6fa5
   gtk_container_add (container, widget);
Packit Service fb6fa5
   g_object_unref (widget);
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
  /* all items in item_list need to be referenced
Packit Service fb6fa5
   * before gtk_list_remove_items() is invoked.
Packit Service fb6fa5
   * this is somewhat tricky as gtk_list_append_items/gtk_list_prepend_items/
Packit Service fb6fa5
   * gtk_list_insert_items will take over the lists nodes.
Packit Service fb6fa5
   * we therefore have an extra GSList `*slist' for later unreferencing.
Packit Service fb6fa5
   */
Packit Service fb6fa5
   slist = NULL;
Packit Service fb6fa5
   for (list = item_list; list; list = list->next)
Packit Service fb6fa5
   {
Packit Service fb6fa5
     g_object_ref (GTK_WIDGET (list->data));
Packit Service fb6fa5
     slist = g_slist_prepend (slist, list->data);
Packit Service fb6fa5
   }
Packit Service fb6fa5
   gtk_list_remove_items (list, item_list);
Packit Service fb6fa5
   gtk_list_append_items (other_list, item_list);
Packit Service fb6fa5
   /* gtk_list_prepend_items (other_list, item_list); */
Packit Service fb6fa5
   /* gtk_list_insert_items (other_list, item_list, 3); */
Packit Service fb6fa5
   while (slist)
Packit Service fb6fa5
   {
Packit Service fb6fa5
     GSList *tmp;
Packit Service fb6fa5
     
Packit Service fb6fa5
     tmp = slist;
Packit Service fb6fa5
     slist = slist->next;
Packit Service fb6fa5
     g_object_unref (GTK_WIDGET (tmp->data));
Packit Service fb6fa5
     g_slist_free_1 (tmp);
Packit Service fb6fa5
   }
Packit Service fb6fa5
   
Packit Service fb6fa5
   /* Alternatively to the removal above you could just use
Packit Service fb6fa5
    * gtk_list_remove_items_no_unref() which will add the additional
Packit Service fb6fa5
    * reference count to the widget.
Packit Service fb6fa5
    */
Packit Service fb6fa5
   gtk_list_remove_items_no_unref (list, item_list);
Packit Service fb6fa5
   gtk_list_prepend_items (other_list, item_list);
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
Now a (hopefully) complete list of functions that require
Packit Service fb6fa5
wrappers similar to the examples above:
Packit Service fb6fa5
Packit Service fb6fa5
void       gtk_container_remove         (GtkContainer     *container,
Packit Service fb6fa5
                                         GtkWidget        *widget);
Packit Service fb6fa5
void       gtk_list_remove_items        (GtkList          *list,
Packit Service fb6fa5
                                         GList            *items);
Packit Service fb6fa5
void       gtk_tree_remove_items        (GtkTree          *tree,
Packit Service fb6fa5
                                         GList            *items);
Packit Service fb6fa5
void       gtk_tree_item_remove_subtree (GtkTreeItem      *tree_item);
Packit Service fb6fa5
void       gtk_menu_item_remove_submenu (GtkMenuItem      *menu_item);
Packit Service fb6fa5
void       gtk_option_menu_remove_menu  (GtkOptionMenu    *option_menu);
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
Initial proposal:
Packit Service fb6fa5
	- Marius Vollmer <mvo@zagadka.ping.de>
Packit Service fb6fa5
Packit Service fb6fa5
Some modifications/additions, "Taking care of proper referencing" and
Packit Service fb6fa5
reference counting solution for GtkMenus:
Packit Service fb6fa5
	- Tim Janik <timj@gimp.org>