I have a subclass of Gtk.Box that contains a GLib.Timer that fires a notification after a given interval. I have method in this class that calls this.destroy() on the Gtk.Box. The timer continues to run and fires the notification even after its parent instance has been destroyed. All instances of this class that have been destroyed exhibit this behaviour and continue to use CPU and memory untill the process is killed.
How do I fix this? How can I kill instances effectively and how do I manually free memory instead of relying on vala's garbage collection.
edit: here's an (embarrassing) mvce
// mvce_deletable
// nine
// 2017.01.11
// valac --pkg gtk+-3.0 --pkg glib-2.0 deletablebox.vala
using Gtk;
using GLib;
class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private GLib.Timer timer;
private Gtk.Label label;
public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
Timeout.add (50, update);
this.show_all ();
}
private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}
int main ( string [] args ) {
Gtk.init(ref args);
var window = new Gtk.Window ();
window.destroy.connect (Gtk.main_quit);
var delete_me = new RemovableBox ();
window.add ( delete_me );
window.show_all();
Gtk.main();
return 0;
}
I added a timer_id the the RemovableBox class but it still doesn't work as desired.
class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private uint timeout_id;
private GLib.Timer timer;
private Gtk.Label label;
public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
timeout_id = Timeout.add (40, update);
this.show_all ();
}
~ RemovableBox () {
Source.remove (timeout_id);
}
private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}
You are confusing automatic reference couting with full garbage collection.
There is no garbage collector in GLib, but classes have a reference count instead that is increased when a GObject is used in multiple places and decreased when it is no longer used in those places, until it reaches zero. The object is then freed.
In fact in C code the reference counting is manual:
Vala adds the
autoto reference counting, which makes it "automatic reference counting" (ARC). That is you don't have to worry about the reference count in Vala, it will add the appropriaterefandunrefoperations for you.In full garbage collection (like in C#, Java, ...) memory deallocation is not deterministic, the object can be kept alive even if it isn't used anymore. This is done using something called a "managed heap" and a garbage collector is run in the background (i.e. as a GC thread).
Now that we have the background stuff covered to your actual problem:
You have to remove the Gtk.Box from it's parent container and also set any references you might still have in your Vala code to
nullin order to get the reference count to 0. It will then beunrefed.There are of course other options, like disabling the timer, etc. You should really add an MVCE to your question for us to be able to give you some design advice on your code.
PS: Reference counting is often considered as a simple method of garbage collection. That's why I write full garbage collection (also called
tracing garbage collection) in order to not confuse the two terms. See the Wikipedia article on garbage collection.