[ 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 scheme. | 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 ---> | To: | Other object ---> Proxy <... GObject ---> | If all other references to the Proxy are dropped, then we end up with: | 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 platforms. 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