-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Refactor Gtk callback setters and types #685
Comments
I can probably work on a PR for this if the idea sounds good. |
It seems like the memory leak bug that I struggled to fix for more than a year actually has to do with this issue itself. The irony is that it took me a few weeks to even realize that this is the actual reason. I'll probably get this done within this (and next) week. |
While working on the upcoming pull request, I have noticed a problem that I think might be affecting the entire library rather than just what I am patching up in the library. Prior to my discovery, the type The incorrect referencing has led me to the second issue of Gtk complaining about unreferencing an invalid object. Although this may seem like Gtk is complaining about Because of this, in the upcoming pull request, I will be clarifying the // Take wraps a unsafe.Pointer as a glib.Object, taking ownership of it.
// This function is exported for visibility in other gotk3 packages and
// is not meant to be used by applications.
//
// To be clear, this should mostly be used when Gtk says "transfer none". Refer
// to AssumeOwnership for more details.
func Take(ptr unsafe.Pointer) *Object {
obj := newObject(ToGObject(ptr))
obj.RefSink()
runtime.SetFinalizer(obj, (*Object).Unref)
return obj
}
// AssumeOwnership is similar to Take, except the function does not take a
// reference. This is usually used for newly constructed objects that for some
// reason does not have an initial floating reference.
//
// To be clear, this should often be used when Gtk says "transfer full", as it
// means the ownership is transferred to the caller, so we can assume that much.
// This is in contrary to Take, which is used when Gtk says "transfer none", as
// we're now referencing to an object that might possibly be kept, so we should
// mark as such.
func AssumeOwnership(ptr unsafe.Pointer) *Object {
obj := newObject(ToGObject(ptr))
runtime.SetFinalizer(obj, (*Object).Unref)
return obj
} The new implementation of As a side note, this discovery may be related to PR #507. |
This PR also helped me find out another issue: an object that is being signal-connected to can be referenced from inside the signal callback, sometimes creating a circular reference in some cases. In these cases, neither the object nor the callback will ever be freed, as nothing would call the destructors needed to unregister the callback in the internal registry. Specifically with l, _ := gdk.PixbufLoaderNew()
l.Connect("area-prepared", func() {
p, _ := l.GetPixbuf()
image.SetFromPixbuf(p)
})
io.Copy(l, file)
l.Close() This code has a memory leak: the There are a few ways to solve this problem. One way is to spend a lot of effort correcting the callback behaviors. For example, The other way is a lot more obvious: don't reference the object it is being connected to. The fix to the above code (in -l.Connect("area-prepared", func() {
+l.Connect("area-prepared", func(l *gdk.PixbufLoader) {
p, _ := l.GetPixbuf() This, combined with the new changes commented above, completely fixed issues with the After working out such a simple fix to the above problem, I am now considering the choice of forcing As a side note, I'm unsure how well this will pan out if said object needs to be in a structure, perhaps in a situation like this: type ChatBox struct {
gtk.TextView
Loader *gdk.PixbufLoader
State State
}
func (box *ChatBox) Method() {
box.Loader.Connect("size-allocate", func(loader *gdk.PixbufLoader) {
// Here, we're dereferencing box to get the state, which might keep box
// alive along with the PixbufLoader, causing a circular reference.
loader.SetSize(box.State.Width, box.State.Height)
})
} |
Is your feature request related to a problem? Please describe.
Kind of. While working on a pull request to add
TreeViewSearchEqualFunc
, I've noticed 3 (three) uglies:All callback types right now take in a
...interface{}
(variadic empty interface) argument. This is superfluous; Go closures can reference values outside.There is no clean up of callbacks (
GDestroyNotify
is not used).Each
Set.*Func
method and/or type seems to require its own map with a mutex and serial incrementing ID, which is redundant.Describe the solution you'd like
I propose we address this problem in 3 simple steps:
Remove all
...interface{}
arguments from the callback types. This allows us to store the Go callback function value cleanly without requiring astruct
to wrap the interfaces.Move the callback containers off of the package and into an internal package like so.
Use the created abstraction to clean up callbacks using the
GDestroyNotify
callback:Describe alternatives you've considered
Since this change will break existing code, I have considered to just go along with adding some instead of all the solutions above. Despite that, I still think the current code is worth refactoring and breaking.
Additional Information
My generated libhandy binding implements something similar to this.
The text was updated successfully, but these errors were encountered: