refcounting.txt 11.5 KB
Newer Older
Tim Janik's avatar
Tim Janik committed
1 2 3 4 5 6 7 8 9
The Reference Counting Scheme of GDK an GTK+
============================================

Each data structure that provides reference counting offers a bunch of
functions that follow these conventions:

  *_new:      Create a new structure with a reference count of 1.
  *_ref:      Increase ref count by one.
  *_unref:    Decrease ref count by one.  If the count drops to zero,
10 11 12 13
              run appropriate finalization code and free the memory.
	      For data structures with a _destroy function, it will be
	      invoked at this point, if the data structure is not
              already in a destroyed state.
Tim Janik's avatar
Tim Janik committed
14 15 16 17

GtkObjects also provide the following functions:

  *_destroy:  Render an object `unusable', but as long as there are
18
              references to it, its allocated memory will not be freed.
Tim Janik's avatar
Tim Janik committed
19 20 21 22 23 24
  *_sink:     Clear a GtkObjects `floating' state and decrement the
	      reference count by 1.

GdkWindow
---------

25
A GdkWindow has to be explicitly destroyed with gdk_window_destroy.
Tim Janik's avatar
Tim Janik committed
26 27
This will send out a request to destroy this window and all its
children, and will decrement the ref_count of the GdkWindow by one.
28
Thus, it releases the initial reference created by gdk_window_new.
Tim Janik's avatar
Tim Janik committed
29 30 31 32 33 34 35 36 37 38

All GdkWindows are kept in a hash table to translate from their XId to
the actual structure and the pointer in the hash table is reflected in
the reference count.  When a DestroyNotify event is received for a
particular GdkWindow, it is removed from the hash table and the
ref_count is updated accordingly.

You can call gdk_window_destroy more than once on a particular
GdkWindow, it will only be destroyed when it hasn't been yet.  The
ref_count is *always* decremented, tho. Be careful.
39 40 41 42

Remark: When writing NO_WINDOW widgets, care should be taken about
        proper referencing/unreferencing of the parent's GdkWindow
        that is used by the widget.
Tim Janik's avatar
Tim Janik committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
 
GdkPixmap
---------

There is no gdk_pixmap_destroy function.  The Pixmap is destroyed when
the last reference to it vanishes.

GdkPixmaps are kept in the same hash table as GdkWindows but the
pointer in the hash table is *not* reflected in the ref_count.

This works only when Pixmaps never get XEvents.  I'm not sure if this
is the case.

GdkBitmap
---------

A GdkBitmap is only another name for a special use of GdkPixmap.

GdkVisual
---------

There are no *_new or *_destroy functions and the *_ref and *_unref
65
functions are no-ops.  GdkVisuals are static structures and thus do not
Tim Janik's avatar
Tim Janik committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
need reference counting.  The ref counting functions are only there
for extra defensive programming.

GdkColormap
-----------

Nothing special.  There is no gdk_colormap_destroy function.

GdkFont / GdkFontSet
--------------------

GdkFont and GdkFontSet are equivalent as far as ref counting is
concerned.  Use gdk_font_ref and gdk_font_unref for both.

There is no gdk_font_free or gdk_fontset_free function.

GtkAcceleratorTable
-------------------

There is no gtk_accelerator_table_destroy function.

GtkTooltips
-----------

There is no gtk_tooltips_destroy function.

GtkStyle
--------

There is no gtk_style_destroy function.

GtkObject
---------

GtkObjects follow the usual ref_counting strategy, but with a twist.

They are created with a ref_count of 1.  GtkObjects are able to
run finalization code when the ref_count drops to zero but you cannot
register arbitrary signal handlers to run at finalization time.

There is also the old gtk_object_destroy function and the "destroy"
signal but they are somewhat independent from finalization.  Just as
stated at the top of this text, gtk_object_destroy merely renders an
object unusable.  When the object is a container widget for example,
it unrealizes that widget, removes all children and disconnects all
signal handlers.  The finalization code is different, it would for
example free associated memory for text strings and release the
attached style.

This is the biggest change.  Every widget must be revised to have a
proper "destroy" function, etc.  Such a destroy function will only
be called once and is expected to leave the widget in a minimal but
consistent state.  Widgets that have been "destroyed" but not yet
finalized are flagged with GTK_DESTROY.  The "finalization" function
is new and should perform last-minute cleanup actions, in contrast
to the destroy function it will not be emitted as signal though.
It can assume that the "destroy" function has been called as the
last function on this widget.

Essentially, the old "destroy" function has been split into a
"finalize" plus a "destroy" function.

It is not possible to create GtkObjects with a ref_count of 0
because the first ref/unref pair will destroy it unintentionally.

To be mostly backward compatible with existing practice, a GtkObject
leads a more complicated life than the other reference counted structures.

When a GtkObject is created, it starts out in a special state called
"floating" (this is the twist).  This means that it is alive and has a
reference to it, but the `owner' of this reference is not known.
There are certain `potential owners' that will adopt a floating
GtkObject.  For GtkWidgets the most common adopters are the parent
widget.

When you want to adopt a possibly floating GtkObject, you call
gtk_object_sink on it.  This clears the floating state of the
GtkObject and decrements the ref_count by one, if it has been floating
previously.  Once the floating state has been cleared, it will never
be set again.

All widgets that are part of the display are linked into a
parent/child tree.  The link from the parent to a child is reflected
in the ref_count of the child, but the link from the child to the
parent is not reflected in the ref_count of the parent.

Like a GtkObject, a GtkWidget is created with a ref_count of 1 and
initially flagged as `floating'.  As soon as it is added as a child to
a parent, the `floating' flag is cleared and never will be set again.
Not even when it is later unparented.  The act of clearing the
`floating' flag also decrements the ref_count of the widget by one.

When the widget is unparented, its underlying GdkWindow is destroyed
(when it has one), it loses its reference from the parent and
naturally the ref_count is decremented.

It is considered a bug if a widget still has a GdkWindow when it is
being freed.

Toplevel widgets, which don't have a `natural' parent, are adopted by
special registering functions.  Because the of the reference count that
is set by the registering functions, toplevel widgets will have to be
explicitly destroyed, with the exception of GtkMenus.  GtkMenus are a
special case of toplevel widgets in that they will be `attached' to and
`detached' from other widgets.  The act of attaching a GtkMenu to a
widget will be reflected in its reference count.  The act of detaching
a GtkMenu will revert that.  Therefore GtkMenus naturally get destroyed
and finalized once they are detached from their reference holder.

So, the typical career of a GtkWindow a GtMenu attached to a
GtkOptionMenu looks like this:

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  /* window is created with ref_count == 1.  It is not flagged as
   * `floating' because it has already been registered as a toplevel
   * widget.
   */

  option_menu = gtk_option_menu_new ();
  /* option_menu->ref_count == 1 and it is flagged as `floating'.
   */
  
  gtk_container_add (window, option_menu);
  /* option_menu->ref_count still == 1, but it is no longer `floating'.
   */
  
  menu = gtk_menu_new ();
  /* menu->ref_count == 1 and it is flagged as `floating'.
   */
  
  menu_item = gtk_menu_item_new_with_label ("Choose Me");
  /* menu_item->ref_count == 1 and it is flagged as `floating'.
   */

200
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
Tim Janik's avatar
Tim Janik committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
  /* menu_item->ref_count still == 1, but it is no longer `floating'.
   */
  
  gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  /* menu->ref_count still == 1, but it is no longer `floating'.
   */

  gtk_widget_show (menu_item);
  gtk_widget_show (option_menu);
  gtk_widget_show (window);

  /* The widgets get their GdkWindows, nothing significant happens to
   * the ref_counts.
   */

Then, when the user wants to get rid of the window:

  gtk_widget_destroy (window);

  /* The GdkWindow of `window' and all its child GdkWindows are
   * destroyed.
   *
223 224
   * window is unregistered from the toplevel list and its ref_count
   * drops to zero.  The destroy code of `window' destroys `option_menu'.
Tim Janik's avatar
Tim Janik committed
225 226 227 228
   *
   * The destroy code of `option_menu' causes the `menu' to be detached
   * from it and its reference count drops to zero.
   *
229
   * The destroy code of `menu' destroys `menu_item'.
Tim Janik's avatar
Tim Janik committed
230 231 232 233 234 235 236 237 238 239 240 241 242
   *
   * The destruction of `menu_item' removes it from its parent, the
   * menu_item->ref_count drops to zero and `menu_item' is finalized (freed).
   *
   * Now `menu', `option_menu' and `window' will be destroyed and finalized,
   * in this order, since the reference count of each is zero.
   */


Taking care of proper referencing
---------------------------------

There are some cases where referencing of widgets from outside the toolkit
243
(on the application side) is needed.
244
Once the application performs an operation on a widget that will cause
Tim Janik's avatar
Tim Janik committed
245 246 247 248 249 250
its reference count to drop, if it wants to take further actions on the
widget, it needs to hold a reference to it.

Example code sequences that require reference wraps:

   /* gtk_container_remove() will unparent the child and therefore
251
    * cause its reference count to be decremented by one.
Tim Janik's avatar
Tim Janik committed
252
    */
253
   g_object_ref (widget);
Tim Janik's avatar
Tim Janik committed
254 255 256 257
   gtk_container_remove (container, widget);
   /* without the reference count, the widget would have been destroyed here.
   */
   gtk_container_add (container, widget);
258
   g_object_unref (widget);
Tim Janik's avatar
Tim Janik committed
259 260 261 262 263 264 265 266 267 268 269


  /* all items in item_list need to be referenced
   * before gtk_list_remove_items() is invoked.
   * this is somewhat tricky as gtk_list_append_items/gtk_list_prepend_items/
   * gtk_list_insert_items will take over the lists nodes.
   * we therefore have an extra GSList `*slist' for later unreferencing.
   */
   slist = NULL;
   for (list = item_list; list; list = list->next)
   {
270
     g_object_ref (GTK_WIDGET (list->data));
Tim Janik's avatar
Tim Janik committed
271 272 273 274 275 276 277 278 279 280 281 282
     slist = g_slist_prepend (slist, list->data);
   }
   gtk_list_remove_items (list, item_list);
   gtk_list_append_items (other_list, item_list);
   /* gtk_list_prepend_items (other_list, item_list); */
   /* gtk_list_insert_items (other_list, item_list, 3); */
   while (slist)
   {
     GSList *tmp;
     
     tmp = slist;
     slist = slist->next;
283
     g_object_unref (GTK_WIDGET (tmp->data));
Tim Janik's avatar
Tim Janik committed
284 285
     g_slist_free_1 (tmp);
   }
286 287 288 289 290 291 292
   
   /* Alternatively to the removal above you could just use
    * gtk_list_remove_items_no_unref() which will add the additional
    * reference count to the widget.
    */
   gtk_list_remove_items_no_unref (list, item_list);
   gtk_list_prepend_items (other_list, item_list);
Tim Janik's avatar
Tim Janik committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312


Now a (hopefully) complete list of functions that require
wrappers similar to the examples above:

void       gtk_container_remove         (GtkContainer     *container,
                                         GtkWidget        *widget);
void       gtk_list_remove_items        (GtkList          *list,
                                         GList            *items);
void       gtk_tree_remove_items        (GtkTree          *tree,
                                         GList            *items);
void       gtk_tree_item_remove_subtree (GtkTreeItem      *tree_item);
void       gtk_menu_item_remove_submenu (GtkMenuItem      *menu_item);
void       gtk_option_menu_remove_menu  (GtkOptionMenu    *option_menu);



Initial proposal:
	- Marius Vollmer <mvo@zagadka.ping.de>

313 314
Some modifications/additions, "Taking care of proper referencing" and
reference counting solution for GtkMenus:
Tim Janik's avatar
Tim Janik committed
315
	- Tim Janik <timj@gimp.org>