Re: Proxy objects and circular garbage




Michael Beach <mbeach@zip.com.au> writes:

> Vollmer Marius wrote:
> > 
> > Michael Beach <mbeach@zip.com.au> writes:
> > 
> > > So, my question is, how do people get around/deal with this sort of
> > > thing in other language bindings, such as Perl, Python, Eiffel and
> > > Guile?
> > 
> > In the guile-gtk bindings, the gtk->proxy link is a weak one.  More
> > precisely, the pointer is stored in a place where the conservative
> > collector of Guile does not find it.  I think this works fine.
> > 
> 
> So you are ok about the proxy getting GCed, even if the gtk+ object it
> proxies for is still live?

In guile-gtk, this is OK, because the proxy is merely a proxy. It
contains no information beyond the GTK object. But if you want
to:
 
 - Store persisitant information in the LSO (language specific object)
 - Derive new widgets implemented in non-C languages (NCL's)

Then the proxy object needs to persist when it is only being
held onto by GTK.

There are quite a few techniques to handle this. Some of the better
ones:

 - Weaken one of the references when the object is destroyed.
   This has the disadvantage that any object which is _not_
   destroyed will never be freed. For instance, using Perl-GTK
   syntax:

   {
     my $button = new GTK::Button "Hello";
   }

   Since it is never added to a parent, it will never be destroyed,
   and used to leak.

 - Hook into reference counting in either GTK or the NCL so that
   you can detect when the reference count drops to 1 or is
   increased from 1, and weaken a reference count.
 
   A trivial case of this is when the reference counting in the
   NCL is manual, like in C++ - in that case the reference counting
   for the NCL can simply be the GTK reference counting.

   In general, hooking into the NCL reference counting/garbage
   collection isn't feasible, so you would need to add another
   hook into GTK when notify at a reference count of 1.

 - Periodically do a mini-GC which scans scans all the proxy objects
   and checks to see if:

      reference_count (LSO) == reference_count (GTKO) == 1

   In that case, the object is dead, and destruction can be
   triggered by dropping one of the references.

   This is the approach taken by the Perl bindings. It is however, 
   dependent on the fact that Perl does reference counting
   itself. It would not be feasible when the NCL is garbage collected
   with a more sophisticated scheme.
 
There are some forms of cycles that the third approach cannot
detect. For instance:

  GTKO1                   GTKO2
   | |                     | |
   ^ v                     ^ v
   | |                     | |
  LSO1 <=================> LSO2

But since the third approach is only a relevant techinque when the NCL
is reference counted, not GC'd, this example already leaks, and isn't
too much of a problem.

Both the second and third techniques fail in the absence of manual
destruction:
  
   LSO1 <=> GTKO1 (parent)
    |         |
    ^         v
    |         |
   LSO2 <=> GTKO2 (child)

Basically, there is no way to get everything right, without having
a sophisticated GC techinque that can grok both GTK and every
NCL. This isn't likely to happen soon...

But having manual destruction means that normal programs will
have no, or almost no leaks, and that is probably good enough.

> > A similar circle will arise when attaching Scheme values to an
> > GtkObject with gtk_object_set_data/gtk_object_get_data but I don't
> > think that it is always the right thing to detach these datas upon a
> > "destroy" request.  Fortunately, Guile has other means to attach
> > arbitrary key/value pairs to objects and we should use these instead.
> > 
> 
> So are you saying that the data attached in this fashion is dropped by
> the gtk+ object when it is destroyed? Lucky you mentioned that, as I
> hadn't considered it.

No, object data is not dropped when the object is destroyed. (Unless
you hook into the "destroy" signal and remove it yourself)

Regards,
                                        Owen



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