| 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, |
| 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. |
| |
| GtkObjects also provide the following functions: |
| |
| *_destroy: Render an object `unusable', but as long as there are |
| references to it, its allocated memory will not be freed. |
| *_sink: Clear a GtkObjects `floating' state and decrement the |
| reference count by 1. |
| |
| GdkWindow |
| --------- |
| |
| A GdkWindow has to be explicitly destroyed with gdk_window_destroy. |
| 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. |
| Thus, it releases the initial reference created by gdk_window_new. |
| |
| 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. |
| |
| 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. |
| |
| 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 |
| functions are no-ops. GdkVisuals are static structures and thus do not |
| 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'. |
| */ |
| |
| gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); |
| /* 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. |
| * |
| * window is unregistered from the toplevel list and its ref_count |
| * drops to zero. The destroy code of `window' destroys `option_menu'. |
| * |
| * The destroy code of `option_menu' causes the `menu' to be detached |
| * from it and its reference count drops to zero. |
| * |
| * The destroy code of `menu' destroys `menu_item'. |
| * |
| * 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 |
| (on the application side) is needed. |
| Once the application performs an operation on a widget that will cause |
| 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 |
| * cause its reference count to be decremented by one. |
| */ |
| g_object_ref (widget); |
| gtk_container_remove (container, widget); |
| /* without the reference count, the widget would have been destroyed here. |
| */ |
| gtk_container_add (container, widget); |
| g_object_unref (widget); |
| |
| |
| /* 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) |
| { |
| g_object_ref (GTK_WIDGET (list->data)); |
| 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; |
| g_object_unref (GTK_WIDGET (tmp->data)); |
| g_slist_free_1 (tmp); |
| } |
| |
| /* 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); |
| |
| |
| 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> |
| |
| Some modifications/additions, "Taking care of proper referencing" and |
| reference counting solution for GtkMenus: |
| - Tim Janik <timj@gimp.org> |