I tried to test for memory leaks of the high level GTK3 bindings.
import gtk, glib, gobject
proc initWithArgv*() =
var
cmdLine{.importc.}: cstringArray
cmdCount{.importc.}: cint
gtk.gtk_init(cmdCount, cmdLine)
proc xadd*(self: Container; widget: Widget) =
discard
proc main() =
initWithArgv()
var window: Window = newWindow()
var
box = newBox(Orientation.vertical, 0)
button2 = newButton("Wrapper")
window.add(box)
#box.xadd(button2)
window.showAll
button2 = nil
GC_fullCollect()
gtk.gtkMain()
main()
The surprising observation is: With line #box.xadd(button2) commented out I get message "finalizeGObject" immediately after program startup as expected. Expected because newButton() calls new() with a finalizer proc, and when I set the button2 ref to nil and call GC_fullCollect() I would expect that finalizer to be called. But when I uncomment #box.xadd(button2), so that call is performed, then the finalizer is not called! Diff for the c code is
diff /tmp/home/stefan/nimgir/nim_gi/t1.c t1.c
201c201,203
< WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw* T3_;
---
> ContainercolonObjectType__U9bNkp3ITqibAfZmdNgz6Fg* T3_;
> WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw* T4_;
> WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw* T5_;
216a219,224
> nimln_(20, "t1.nim");
> T3_ = (ContainercolonObjectType__U9bNkp3ITqibAfZmdNgz6Fg*)0;
> T3_ = &box->Sup;
> T4_ = (WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw*)0;
> T4_ = &button2->Sup.Sup.Sup;
> xadd_CSJDajTMVqA4FK5tE4Bzgg(T3_, T4_);
218,220c226,228
< T3_ = (WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw*)0;
< T3_ = &window->Sup.Sup.Sup;
< showAll_lXhLwQ78AtCsaeIEOagh9bA(T3_);
---
> T5_ = (WidgetcolonObjectType__lBhQvK9csISM5sR8bgaX2Yw*)0;
> T5_ = &window->Sup.Sup.Sup;
> showAll_lXhLwQ78AtCsaeIEOagh9bA(T5_);
Is that the desired behaviour? I don't know how GC is performed in detail, may the temporary variables in the C code prevent GC?
When I modify the xadd() proc to
proc xadd(self: Container; widget: Button) =
discard
then finalizer is called again!
So problem is, that Button is a Widget which is again a GObject...
Which results for the initial code to
> T4_ = &button2->Sup.Sup.Sup;
> xadd_CSJDajTMVqA4FK5tE4Bzgg(T3_, T4_);
So is blocking the finalizer call intended? If yes, that would mean that I can not proof for memory leaks in this way.
Well, here is a self contained example:
type
O1 = ref object of RootObj
i: int
O2 = ref object of O1
j: int
proc finalizeGObject*[T](o: T) =
echo "finalizeGObject"
#proc xadd(self: O1; widget: O2) =
# discard
proc xadd(self: O1; widget: O1) =
discard
proc main() =
var
o1: O1
o2: O2
new(o1, finalizeGObject)
new(o2, finalizeGObject)
o1.xadd(o2)
o2 = nil
GC_fullCollect()
main()
For this I get no output, so finalizer is not called.
But with this
proc xadd(self: O1; widget: O2) =
discard
#proc xadd(self: O1; widget: O1) =
# discard
we get
$ ./t1
finalizeGObject
OK, that is what I guessed more or less...
I think I will try to put all the relevant code into another proc and then call GC_fullCollect() when that proc is finished, then all stack of that proc should be released and finalizer should be called for that objects. Will continue testing GTK3 GC behaviour soon...