Reference counting



Hi,

I'm still tinkering with the reference counting of Gtk.  Below is a
text that describes how I think it should work.  Everything except the
GtkObjects stuff has been implemented and is available as a patch from
ftp.gimp.org as gtk-mvo-971013-0.patch.gz.  I hope to have something
for the GtkObjects soon.

The changes are not backward compatible.  It is possible to install
some support until all major Gtk apps have been converted, I think.

So, what do you think?


How ref counting works within Gdk and Gtk
=========================================

Each data structure that provides ref 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 aprropriate finalization code and free the memory.
              No user visible actions should take place, like
              destryoing windows, etc.

Some structures also provide a *_destroy function, but it is generally
unrelated to freeing the memory.  `Destroying' merely renders an
object `unusable'.  But as long as there are references to it, it will
stick around.

GdkWindow
---------

A GdkWindow has to be explicitely 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 inital 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.
 
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 that are not GtkWidgets implement the usual ref_counting
strategy.  They are created with a ref_count of 1 and there is no
gtk_object_destroy function and no "destroy" signal.  GtkObjects still
are able to run finalization code but you cannot register arbitrary
handlers to run at finalization time.

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

To be mostly backward compatible with existing practice, a GtkWidget
leads a more complicated life.

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.

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.

When the ref_count drops to zero, a widget unparents all its children.
The children will most likely have already destroyed their GdkWindows
at this point because their parent does not have a parent.

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
a special widget, maybe a GtkDisplay or GtkScreen.  This special
parent of all toplevel widgets is never freed.  The toplevel widgets
are added to this parent as soon as they are created.

The action that is currently performed by gtk_widget_destroy is
therefore identical to gtk_widget_unparent.  Both basically mean "take
this widget off the screen".  This does not directly and invariably
cause the widget to be freed and having its finalization code run.  It
only happens when the former parent was the last reference to the
widget.

So, the typical career of a GtkWindow and the GtkButton that sits in
it 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 added to the special
  // parent of all toplevel widgets.

  button = gtk_button_new_with_label ("Yo!");

  // button->ref_count == 1 and it is flagged as `floating'.

  gtk_container_add (window, button);

  // button->ref_count still == 1, but it is no longer `floating'.

  gtk_widget_show (button);
  gtk_widget_show (window);

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

Then, when the user wants to destroy the window:

  gtk_widget_unparent (window);

  // The GdkWindow of `window' and all its child GdkWindows are
  // destroyed.

  // window is removed from its parent and its ref_count drops to
  // zero.  The finalization code of `window' unparents `button'.

  // button->ref_count drops to zero and the button is freed, too.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]