Introducing "toggle references"

[ A resend because of the wrong address on the first message. Sorry
  for the duplicate for those who get it ]

One of the most challenging parts of writing a language binding for
GTK+ is dealing with memory management of the combination of a GObject
and a language runtime proxy.

We want to both objects to be kept around in the case:

               Language GC      GObject refcounting
   Other object ---> Proxy ---> GObject

and in the case:
                    Proxy <--- GObject <--- Other GObject

But if the Proxy and the GObject mutually reference each other, then
the combination can never be collected by either garbage collection

                    Proxy <--- GObject 

In some cases, the language's garbage collector is flexible enough
to allow a resolution there. This is true for Perl, for Python,
and I think also for Ruby. (I've only taken a brief look at the
implementation of the Ruby bindings.) But it's not the case for
either Java or the CLR.

The basic way that I'd propose handling the above is similar to the
way that the Perl bindings work. (They weren't the first to invent the
scheme ... it's been around for quite a while.... I believe I first
saw it in the docs for the ILU interprocess communication system 6-7
years ago.)

The basic idea of the scheme is that when the reference count on the
GObject drops to 1, we know that the only reference left to the
GObject is held by the proxy. So, there is no reason that the GObject
should keep the proxy alive, and we convert the reference from
GObject to the proxy to a weak reference. 

We go from:
  Other object ---> Proxy <--- GObject <--- Other Gobject 

  Other object ---> Proxy <... GObject 

If all other references to the Proxy are dropped, then we end up 
                    Proxy <... GObject 

The Proxy is garbage collected, and at that point the reference count
on the GObject drops to zero and it is freed as well.

On the other hand, if a new reference is created to the GObject (say,
the language binding calls gtk_container_add() on it), then we
strengthen the GObject => proxy reference again and return to the
original state.

The API I want to propose for this is quite simple. 

  * GToggleNotify:
  * @data: Callback data passed to g_object_add_toggle_ref()
  * @object: The object on which g_object_add_toggle_ref() was
  *          called.
  * @is_last_ref: %TRUE if the toggle reference is now the
  *    last reference to the object. %FALSE if the toggle
  *    reference was the last reference and there are now other
  *    references.
  * A callback function used for notification when the state
  * of a toggle reference changes. See g_object_add_toggle_ref().
 typedef void (GToggleNotify) (gpointer      data,
			       GObject      *object,
                               gboolean      is_last_ref);

[ The reversed data/object is to match GWeakNotify ]

  * g_object_add_toggle_ref:
  * @object: a #GObject
  * @notify: a function to call when this reference is the
  *          last reference to the object, or is no longer
  *          the last reference.
  * @data:   data to pass to @notify
  * Increases the reference count of the object by one and
  * sets a callback to be called when all other references
  * to the object are dropped, or when this is already the
  * last reference to the object and another reference is
  * established.
  * Multiple toggle references may be added to the same
  * gobject, however if there are multiple toggle references
  * to an object, none of them will ever be notified until
  * all but one are removed.
 void g_object_add_toggle_ref (GObject       *object,
                               GToggleNotify  notify,
                               gpointer       data);
  * g_object_remove_toggle_ref:
  * @object: a #GObject
  * @notify: a function to call when this reference is the
  *          last reference to the object, or is no longer
  *          the last reference.
  * @data:   data to pass to @notify
  * Removes a reference added with g_object_add_toggle_ref
 void g_object_remove_toggle_ref (GObject       *object,
                                  GToggleNotify  notify,
                                  gpointer       data);

(Why not g_object_toggle_ref() and g_object_toggle_unref()
to match g_object_weak_ref/unref()? they sound too much like they are
toggle the reference rather than add a "toggle reference.")

The big question is how to implement this efficiently. If we
implemented it like g_object_weak_ref(), then we would have to do a
qdata lookup each time the reference count of any object went from 
1 => 2 or from 2 => 1. Which is prohibitive.

Other ideas:

 - We could squeeze two flag bits into object->qdata by
   using the 4-byte alignment. This would require modification
   of g_data_list_*() to mask out the bits.

   Then we could use one of the flag bits to indicate whether
   there were any toggle references present.

 - We could add an instance-private data block for GObject.
   Looking this up isn't completely free, but is constant
   time, unlike the qdata lookup. This would expand all
   GObjects by at least 4 bytes.

 - We could allocate a block of data ahead of the GObject.
   Because of alignment constraints, it would need to be
   8 bytes on 32-bit platforms and 16 bytes on 64-bit 

Not really happy with any of these. The first approach though
ugly, would have the highest efficiency. 

In terms of the overall scheme, the main problem that I know
with it is that it can't handle the case of two language 
bindings referring to the same object:

        Language Runtime A                Language Runtime B
                            |           |
                            |           |
                    Proxy <--- GObject ---> Proxy
                           --->        <---
                            |           |

Is still uncollectable.

It also can't handle cycles that go through multiple GObjects:

                  Proxy A  <--- GObject A
                     |      --->  /|
                     |       |     |
                     |/      |     |
                  Proxy B  <... GObject B

But I don't know any way of handling these situations, so I don't 
think they are worth worrying about. (The existence of an explicit
gtk_widget_destroy() is largely to handle the second situation.)

Attachment: signature.asc
Description: This is a digitally signed message part

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