Sinkability considered harmful



Sinkability considered harmful
------------------------------

Why do we have sinkable objects, i.e. gtk_object_sink()?

It is a historical artifact.  Early versions of GTK+ did not have a
consistent or even correct policy for memory management.  

In 1997, Marius Vollmer (a Guile hacker with experience in garbage
collection and automatic memory management) revised most of the code
in GTK+ to use reference counting consistently:

	http://mail.gnome.org/archives/gtk-list/1997-November/msg00229.html

Owen merged some of these changes (gtk+/ChangeLog.pre-1.0):

Wed Dec 17 21:09:12 1997  Owen Taylor  <owt1 cornell edu>
  1997-10-13  Marius Vollmer  <mvo zagadka ping de>

    [...]

    Revamped reference counting, see the file REFCOUNTING.

Early in 1998, Tim Janik merged most of the changes into GTK+
(gtk+/ChangeLog.pre-1.0):

Fri Jan 30 23:55:03 1998  Tim Janik  <timj gimp org>

	* Incorporation of Marius Vollmer's reference counting revolution,
	  plus various fixups and additions from myself.

	[...]

	* gtk/gtkobject.c: new functions gtk_object_finalize,
 	  gtk_object_notify_weaks, gtk_object_debug, gtk_object_sink,
 	  gtk_object_weakref, gtk_object_weakunref. implementation of the new
	  reference counting scheme for gtkobjects (consult gtk+/REFCOUNTING).

See the last part of this mail: 
	http://mail.gnome.org/archives/gtk-list/1997-November/msg00245.html

Quote:

	The complicated rules about GtkWidgets and their `floating'
	flag are there to avoid breaking *all* existing code.

So, what happened?

We started using reference counting in a consistent fashion.  Even if
not all structures and objects derived from GtkObject, they provided
their own foo_ref() and foo_unref() functions.

Client code seldom used "internal" objects like GdkFont, so it didn't
need to ref/unref those objects.  For example, in an expose handler
you could simply access widget->style->font, as you would not be
storing that font for usage elsewhere.


Widgets are special
-------------------

GTK+ always had this usage convention for widgets:

  GtkWidget *the_widget;

  the_widget = gtk_some_widget_new ();
  gtk_container_add (some_container, the_widget);
  /* I no longer own the_widget; some_container owns the base reference */

The convention is that once you insert a widget in a container, you
no longer own a reference to the widget.

Contrast this with a "normal" reference-counted API, where you do this:

	Obj *obj;

	obj = some_object_new ();
	/* reference count of obj is now 1 */

	container_add (container, obj);
	/* reference count of obj is now 2 or more */

	some_object_unref (obj);
	/* This code no longer owns a reference to obj, but the
	 * container does.
	 */

... but old code that used GTK+ did *NOT* require you to unref() the
object after adding it to a container, and we did not want to break
that code.  WE HAVE LIVED WITH THAT CONVENTION EVER SINCE.

The "floating" flag was introduced in GtkObject to:

	1. keep this convention

	2. make things easier for language bindings.

So, to summarize: the floating flag was added to avoid changing GTK+
client code, and to make things friendly to language bindings which
perform their own memory management.  You do *not* need a floating
flag in a normal reference-counted API.

All GtkObjects start with the floating flag turned on.  Inside the
code for containers, you can see this:

	gtk_some_container_add (GtkSomeContainer *container, GtkWidget *child)
	{
		gtk_object_ref (child);
		gtk_object_sink (child);
		add_child (container->children, child);
	}

With this scheme:

1. Right after creation, the initial reference of an object is said to
   be floating.

2. When you put the object in a container, the container first refs
   the object, then sinks it.

	void
	gtk_object_sink (GtkObject *object)
	{
	  if (GTK_OBJECT_FLOATING (object))
	    {
	      GTK_OBJECT_UNSET_FLAGS (object, GTK_FLOATING);
	      gtk_object_unref (object);
	    }
	}

   From the viewpoint of the container, this means:  if no one had
   claimed ownership to the initial reference of the object, I'll
   claim it myself.  Otherwise, I'll simply acquire a new reference to
   the object.

3. The calling code no longer owns the initial reference.


Widgets are inconsistent
------------------------

If you had an API where everything was "purely" reference-counted, you
would always acquire a reference when you needed one, and release it
when you no longer need it.  This makes using the API reasonably
easy.  You have to type a bit more to make the call to unref(), but at
least your code is consistent.

When certain objects have a floating flag and a sink() method, you as
a programmer need to keep in mind a special case.  Users of GTK+ have
kept this in mind for GtkWidget and its descendants since 1998.

Many APIs have abused float/sink "to make things easier for the
programmer", so that he doesn't have to unref() an object after
passing it to a container-like object which acquires a reference.
Recent culprits include gtk_file_filter_new() and
gtk_file_chooser_add_filter(), even though filters are not widgets!


Good practices for new APIs
---------------------------

Just make them purely reference-counted as derived from GObject.  If
your API has container-like objects which hold other objects, you'll
need to unref() the children by hand after inserting them in the
container.

Using floating references and a sink() method just makes life harder
for everyone:

1. Users have to keep in mind that those objects are special.

2. Language bindings have to go through contortions to get the
   references right.


Summary
-------

Let's not have a floating flag in GObject, as it promotes bad API
practices.

Let's keep GtkObject and GtkWidget as they are, as a historical
artifact, and just let language bindings deal with them as they do
now.

APIs which want to reinvent float/sink do so at their own peril.

  Federico





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