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