Skip to content

Memory leak when using events from subclass #165

@gnunn1

Description

@gnunn1

Below is a simple reproducer for a problem I'm having in Terminix (gnunn1/tilix#589) where objects don't seem to be released properly when using eventHandlers. I'm not sure if this is a GtkD problem, an issue with D's GC not handling chained dependencies or a coding practice I shouldn't be doing.

When you run the reproducer, it has an Add and Delete button. Clicking the Add Button creates a new Box with an Entry, clicking the Delete button removes and calls the D GC to collect (just to force things for testing purposes). The CustomVox and CustomEntry which are created have destructors that simply write a line out to stdout.

If you run the code and press Add then Delete and repeat several times you will see that the destructors for CustomBox and CustomEntry are not being executed. However, if you comment out the line:

entry.addOnChanged(&onChanged);

and repeat the test you will see that the destructors are being called properly.

My expectation is that because the CustomBox is not being referenced in D anywhere that it should be GC'ed, however something with the event handler between the CustomBox and CustomEvent is stopping it from working. Is this incrementing the reference count and preventing the GC from removing the struct from root maybe?

import std.stdio;

import gio.Application : GioApplication = Application;
import gtk.Application;
import gtk.ApplicationWindow;
import gtk.Box;
import gtk.Button;
import gtk.EditableIF;
import gtk.Entry;
import gtk.Label;

class HelloWorld : ApplicationWindow
{

	Box bContent;
	CustomBox bEntry;

	this(Application application)import std.stdio;

import gio.Application : GioApplication = Application;
import gtk.Application;
import gtk.ApplicationWindow;
import gtk.Box;
import gtk.Button;
import gtk.EditableIF;
import gtk.Entry;
import gtk.Label;

class HelloWorld : ApplicationWindow
{

	Box bContent;
	CustomBox bEntry;

	this(Application application)
	{
		super(application);
		setTitle("GtkD");
		setBorderWidth(10);

		Box bButtons = new Box(Orientation.HORIZONTAL,6);
		Button btnAdd = new Button("Add");
		btnAdd.addOnClicked(delegate(Button b) {
			if (bEntry is null) {
				bEntry = new CustomBox();
				bEntry.showAll();
				bContent.add(bEntry);
			}
		});
		bButtons.add(btnAdd);
		
		Button btnDelete = new Button("Delete");
		btnDelete.addOnClicked(delegate(Button b) {
			if (bEntry !is null) {
				bContent.remove(bEntry);
				bEntry = null;

				import core.memory;
				GC.collect();
			}
		});
		bButtons.add(btnDelete);

		bContent = new Box(Orientation.VERTICAL, 6);
		bContent.add(bButtons);

		add(bContent);

		showAll();
	}
}

class CustomBox: Box {
private:
	CustomEntry entry;

	void onChanged(EditableIF editable) {
		writeln("Entry changed");
	}

public:
	this() {
		super(Orientation.HORIZONTAL,0);
		entry = new CustomEntry();
		// Comment out the addOnChanged and destructors start triggering properly
		entry.addOnChanged(&onChanged);
		add(entry);
	}

	~this() {
		writeln("Box destructor destroyed");
	}
}

class CustomEntry: Entry {

	~this() {
		writeln("Entry destructor called");
	}
}

int main(string[] args)
{
    auto application = new Application("org.gtkd.demo.helloworld", GApplicationFlags.FLAGS_NONE);
    application.addOnActivate(delegate void(GioApplication app) { new HelloWorld(application); });
    return application.run(args);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions