Skip to content
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

MemChecker: attempt to release non-allocated block #146

Closed
gnunn1 opened this issue Feb 23, 2016 · 17 comments
Closed

MemChecker: attempt to release non-allocated block #146

gnunn1 opened this issue Feb 23, 2016 · 17 comments
Assignees
Labels

Comments

@gnunn1
Copy link
Contributor

gnunn1 commented Feb 23, 2016

I think my application may be experiencing a similar memory issue that we thought we resolved in this thread http://forum.gtkd.org/groups/GtkD/thread/336/

I have a user that is complaining about infrequent segmentation faults in Terminix (gnunn1/tilix#101) that are difficult to reproduce. He's posted a few stack traces in that issue, however I've managed to reproduce it once myself with the G_SLICE=debug-blocks in gdb. As shown below, the stack trace I get looks pretty similar to that previous issue, a failure in the destructor for the RGBA class.

One question, looking at the code of the RGBA destructor, I see the following:

    ~this ()
    {
        if ( Linker.isLoaded(LIBRARY.GDK) && gdkRGBA !is null )
        {
            gdk_rgba_free(gdkRGBA);
        }
    }

Would it help at all for this code to explicitly null the gdkRGBA pointer after the free call?

GSlice: MemChecker: attempt to release non-allocated block: 0x1d2ff30 size=32

Program received signal SIGABRT, Aborted.
0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff6f2872a in abort () from /usr/lib/libc.so.6
#2  0x00007ffff1b5ae18 in g_slice_free1 () from /usr/lib/libglib-2.0.so.0
#3  0x00000000008a9288 in gdk.RGBA.RGBA.~this() (this=0x7ffff7f13700) at ../../../.dub/packages/gtk-d-3.2.2/src/gdk/RGBA.d:91
#4  0x0000000000af2903 in rt_finalize2 ()
#5  0x0000000000af2a02 in rt_finalizeFromGC ()
#6  0x0000000000aee42e in gc.gc.Gcx.sweep() ()
#7  0x0000000000aeebdd in gc.gc.Gcx.fullcollect(bool) ()
#8  0x0000000000aecc73 in gc.gc.Gcx.smallAlloc(ubyte, ref ulong, uint) ()
#9  0x0000000000aea761 in gc.gc.GC.malloc(ulong, uint, ulong*, const(TypeInfo)) ()
#10 0x0000000000ac222b in gc_qalloc ()
#11 0x0000000000af1c29 in rt.lifetime.__arrayAlloc(ulong, const(TypeInfo), const(TypeInfo)) ()
#12 0x0000000000ac745b in _d_arraycatT ()
#13 0x00000000008e5af9 in glib.Str.Str.toStringzArray(immutable(char)[][]) (args=...) at ../../../.dub/packages/gtk-d-3.2.2/src/glib/Str.d:86
#14 0x00000000008d6092 in gio.Settings.Settings.setStrv(immutable(char)[], immutable(char)[][]) (this=0x7ffff7f5c900, value=..., key=...)
    at ../../../.dub/packages/gtk-d-3.2.2/src/gio/Settings.d:1412
#15 0x00000000008829e4 in gx.terminix.profilewindow.ColorPage.setColorScheme(gx.terminix.colorschemes.ColorScheme) (this=0x7ffff7f7a800, scheme=0x7ffff7f7b500)
    at source/gx/terminix/profilewindow.d:467
#16 0x00000000008819eb in gx.terminix.profilewindow.ColorPage.createUI().__dgliteral1(gtk.ComboBoxText.ComboBoxText) (this=0x7ffff7f7f6b0, cb=0x7ffff7f82000)
    at source/gx/terminix/profilewindow.d:300
#17 0x00000000009090f1 in gtk.ComboBoxText.ComboBoxText.callBackChanged(gtkc.gtktypes.GtkComboBoxText*, gtk.ComboBoxText.ComboBoxText) (ComboBoxTextStruct=0x1d90fb0, 
    _ComboBoxText=0x7ffff7f82000) at ../../../.dub/packages/gtk-d-3.2.2/src/gtk/ComboBoxText.d:229
#18 0x00007ffff100a015 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
#19 0x00007ffff101c061 in ?? () from /usr/lib/libgobject-2.0.so.0
#20 0x00007ffff1024dfc in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#21 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#22 0x00007fffed97d8db in ?? () from /usr/lib/libgtk-3.so.0
#23 0x00007fffed97ffb8 in gtk_combo_box_set_active_iter () from /usr/lib/libgtk-3.so.0
#24 0x00007fffed9801ea in ?? () from /usr/lib/libgtk-3.so.0
#25 0x00007ffff100caca in g_cclosure_marshal_VOID__STRINGv () from /usr/lib/libgobject-2.0.so.0
#26 0x00007ffff100a244 in ?? () from /usr/lib/libgobject-2.0.so.0
#27 0x00007ffff1024a46 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#28 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#29 0x00007fffedb42103 in ?? () from /usr/lib/libgtk-3.so.0
#30 0x00007ffff100a015 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
#31 0x00007ffff101c061 in ?? () from /usr/lib/libgobject-2.0.so.0
#32 0x00007ffff1024dfc in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#33 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#34 0x00007fffedb806ae in gtk_widget_activate () from /usr/lib/libgtk-3.so.0
@MikeWey
Copy link
Member

MikeWey commented Feb 23, 2016

It can't hurt, but i may need to try and use the ownership information (if available) from the GIR files, and only free the handle in the destructor if we own de handle.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 23, 2016

Thanks, I'm going to spend some time tonight trying to reproduce it and generate additional stack traces just for additional data points. The previous issue was very easy to reproduce, this one is much more difficult so maybe the previous fix covered the majority of cases but something is still missing.

As well, if there is anything in my code I should check in terms of things that could be causing this feel free to let me know, I don't think I'm doing anything weird but who knows.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 24, 2016

So I was able to reproduce a few more times tonite, all stack traces (appended below) point to the RGBA destructor. I tried adding the code to null the reference but it didn't make any difference. I also tried my first fix from the original thread and it still happened.

If you need to use Terminix to reproduce the problem, editing the profile and moving the transparency slider back and forth repeatably causes the crash to happen. Before your original fix it was almost immediate, now you have to move it back and forth quite a bit to get it to happen.

2016-02-23T19:21:08.524:appwindow.d:updateVisual:365 Setting rgba visual
GSlice: MemChecker: attempt to release non-allocated block: 0x1d1ec00 size=32

Program received signal SIGABRT, Aborted.
0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff6f2872a in abort () from /usr/lib/libc.so.6
#2  0x00007ffff1b5ae18 in g_slice_free1 () from /usr/lib/libglib-2.0.so.0
#3  0x00000000008a9288 in gdk.RGBA.RGBA.~this() (this=0x7ffff7f136a0) at ../../../.dub/packages/gtk-d-3.2.2/src/gdk/RGBA.d:91
#4  0x0000000000af2903 in rt_finalize2 ()
#5  0x0000000000af2a02 in rt_finalizeFromGC ()
#6  0x0000000000aee42e in gc.gc.Gcx.sweep() ()
#7  0x0000000000aeebdd in gc.gc.Gcx.fullcollect(bool) ()
#8  0x0000000000aecc73 in gc.gc.Gcx.smallAlloc(ubyte, ref ulong, uint) ()
#9  0x0000000000aea761 in gc.gc.GC.malloc(ulong, uint, ulong*, const(TypeInfo)) ()
#10 0x0000000000ac21c8 in gc_malloc ()
#11 0x0000000000ac468b in _d_newclass ()
#12 0x000000000088a015 in gx.terminix.terminal.search.SearchRevealer.createPopover() (this=0x7ffff7fac000) at source/gx/terminix/terminal/search.d:169
#13 0x000000000088959b in gx.terminix.terminal.search.SearchRevealer.createUI() (this=0x7ffff7fac000) at source/gx/terminix/terminal/search.d:102
#14 0x000000000088a188 in gx.terminix.terminal.search.SearchRevealer.this(vte.Terminal.Terminal) (this=0x7ffff7fac000, vte=0x7fffe31fe000)
    at source/gx/terminix/terminal/search.d:194
#15 0x000000000088d58a in gx.terminix.terminal.terminal.Terminal.createVTE() (this=0x7fffe31fd000) at source/gx/terminix/terminal/terminal.d:623
#16 0x000000000088a836 in gx.terminix.terminal.terminal.Terminal.createUI() (this=0x7fffe31fd000) at source/gx/terminix/terminal/terminal.d:257
#17 0x0000000000891469 in gx.terminix.terminal.terminal.Terminal.this(immutable(char)[]) (this=0x7fffe31fd000, profileUUID=...) at source/gx/terminix/terminal/terminal.d:1352
#18 0x000000000088461a in gx.terminix.session.Session.createTerminal(immutable(char)[]) (this=0x7ffff7fa0800, profileUUID=...) at source/gx/terminix/session.d:184
#19 0x0000000000884179 in gx.terminix.session.Session.createUI(immutable(char)[], immutable(char)[], bool) (this=0x7ffff7fa0800, firstRun=true, workingDir=..., profileUUID=...)
    at source/gx/terminix/session.d:118
#20 0x0000000000886887 in gx.terminix.session.Session.this(immutable(char)[], immutable(char)[], immutable(char)[], bool) (this=0x7ffff7fa0800, firstRun=true, workingDir=..., 
    profileUUID=..., name=...) at source/gx/terminix/session.d:761
#21 0x00000000008763ae in gx.terminix.appwindow.AppWindow.createNewSession(immutable(char)[], immutable(char)[], immutable(char)[]) (this=0x7ffff7f95000, workingDir=..., 
    profileUUID=..., name=...) at source/gx/terminix/appwindow.d:376
#22 0x0000000000877c21 in gx.terminix.appwindow.AppWindow.createSession(immutable(char)[], immutable(char)[]) (this=0x7ffff7f95000, profileUUID=..., name=...)
    at source/gx/terminix/appwindow.d:656
#23 0x0000000000877e81 in gx.terminix.appwindow.AppWindow.initialize() (this=0x7ffff7f95000) at source/gx/terminix/appwindow.d:684
#24 0x000000000087262f in gx.terminix.application.Terminix.createAppWindow() (this=0x7ffff7edf400) at source/gx/terminix/application.d:199
#25 0x000000000087237d in gx.terminix.application.Terminix.onCreateNewWindow() (this=0x7ffff7edf400) at source/gx/terminix/application.d:154
#26 0x00000000008722a0 in gx.terminix.application.Terminix.installAppMenu().__dgliteral3!(glib.Variant.Variant, gio.SimpleAction.SimpleAction).__dgliteral3(glib.Variant.Variant, gio.SimpleAction.SimpleAction) (this=0x7ffff7eb5c20, SimpleAction=0x7ffff7eb2f00, GVariant=0x7ffff7f92a20) at source/gx/terminix/application.d:122
#27 0x00000000008d8234 in gio.SimpleAction.SimpleAction.callBackActivate(gtkc.giotypes.GSimpleAction*, gtkc.glibtypes.GVariant*, gio.SimpleAction.SimpleAction) (
    simpleactionStruct=0x7fffdc0062d0, parameter=0x0, _simpleaction=0x7ffff7eb2f00) at ../../../.dub/packages/gtk-d-3.2.2/src/gio/SimpleAction.d:243
#28 0x00007ffff100a015 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
#29 0x00007ffff101c061 in ?? () from /usr/lib/libgobject-2.0.so.0
#30 0x00007ffff1024dfc in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#31 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
GSlice: MemChecker: attempt to release non-allocated block: 0x18b9e50 size=32

Program received signal SIGABRT, Aborted.
0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6f272a8 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff6f2872a in abort () from /usr/lib/libc.so.6
#2  0x00007ffff1b5ae18 in g_slice_free1 () from /usr/lib/libglib-2.0.so.0
#3  0x00000000008a9288 in gdk.RGBA.RGBA.~this() (this=0x7ffff7f13740) at ../../../.dub/packages/gtk-d-3.2.2/src/gdk/RGBA.d:91
#4  0x0000000000af2903 in rt_finalize2 ()
#5  0x0000000000af2a02 in rt_finalizeFromGC ()
#6  0x0000000000aee42e in gc.gc.Gcx.sweep() ()
#7  0x0000000000aeebdd in gc.gc.Gcx.fullcollect(bool) ()
#8  0x0000000000aecc73 in gc.gc.Gcx.smallAlloc(ubyte, ref ulong, uint) ()
#9  0x0000000000aea761 in gc.gc.GC.malloc(ulong, uint, ulong*, const(TypeInfo)) ()
#10 0x0000000000ac222b in gc_qalloc ()
#11 0x0000000000af1c29 in rt.lifetime.__arrayAlloc(ulong, const(TypeInfo), const(TypeInfo)) ()
#12 0x0000000000ac488a in _d_newarrayU ()
#13 0x0000000000ac4974 in _d_newarrayiT ()
#14 0x00000000008e59a9 in glib.Str.Str.toStringz(immutable(char)[]) (s=...) at ../../../.dub/packages/gtk-d-3.2.2/src/glib/Str.d:69
#15 0x00000000008a97fa in gdk.RGBA.RGBA.parse(immutable(char)[]) (this=0x7ffff7eb3260, spec=...) at ../../../.dub/packages/gtk-d-3.2.2/src/gdk/RGBA.d:240
#16 0x000000000088ee25 in gx.terminix.terminal.terminal.Terminal.applyPreference(immutable(char)[]) (this=0x7fffe31f8000, key=...) at source/gx/terminix/terminal/terminal.d:870
#17 0x0000000000891694 in gx.terminix.terminal.terminal.Terminal.this(immutable(char)[]).__dgliteral8!(gio.Settings.Settings).__dgliteral8(immutable(char)[], gio.Settings.Settings) (this=0x7ffff7eb5000, Settings=0x7ffff7eb1900, key=...) at source/gx/terminix/terminal/terminal.d:1356
#18 0x00000000008d6623 in gio.Settings.Settings.callBackChanged(gtkc.giotypes.GSettings*, char*, gio.Settings.Settings) (settingsStruct=0x10f4430, 
    key=0x1336c43 "background-transparency-percent", _settings=0x7ffff7eb1900) at ../../../.dub/packages/gtk-d-3.2.2/src/gio/Settings.d:1546
#19 0x00007ffff100caca in g_cclosure_marshal_VOID__STRINGv () from /usr/lib/libgobject-2.0.so.0
#20 0x00007ffff100a244 in ?? () from /usr/lib/libgobject-2.0.so.0
#21 0x00007ffff1024a46 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#22 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#23 0x00007ffff0b13c2c in ?? () from /usr/lib/libgio-2.0.so.0
#24 0x00007ffff16c61f0 in ffi_call_unix64 () from /usr/lib/libffi.so.6
#25 0x00007ffff16c5c58 in ffi_call () from /usr/lib/libffi.so.6
#26 0x00007ffff100ad65 in g_cclosure_marshal_generic_va () from /usr/lib/libgobject-2.0.so.0
#27 0x00007ffff100a244 in ?? () from /usr/lib/libgobject-2.0.so.0
#28 0x00007ffff1024558 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#29 0x00007ffff102512f in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#30 0x00007ffff0b1386c in ?? () from /usr/lib/libgio-2.0.so.0
#31 0x00007ffff0b0ef9a in ?? () from /usr/lib/libgio-2.0.so.0
#32 0x00007ffff1b3ecf5 in g_main_context_invoke_full () from /usr/lib/libglib-2.0.so.0
#33 0x00007ffff0b0f0a4 in ?? () from /usr/lib/libgio-2.0.so.0
#34 0x00007fffe924a414 in ?? () from /usr/lib/gio/modules/libdconfsettings.so
#35 0x00007fffe924b519 in ?? () from /usr/lib/gio/modules/libdconfsettings.so
#36 0x00007fffe9249edc in ?? () from /usr/lib/gio/modules/libdconfsettings.so
---Type <return> to continue, or q <return> to quit---

@phw
Copy link

phw commented Feb 24, 2016

Just to have the info here: I commented on gnunn1/tilix#101 (comment) , the stack trace I posted there also refers to the RGBA destructor.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 24, 2016

I've been thinking about this a bit and one thing I'm wondering about is when I add code to set the gdkRGBA reference to null I still get the error "attempt to release non-allocated block", I'm having a hard time understanding how this could be happening since there is a null check in the destructor before calling free. I'm not going to claim to have a deep understanding of C style memory management since I come from a Java background but I can only see a few scenarios for this:

a. Something is calling the RGBA class free method explicitly since I didn't null the reference there, seems very unlikely though

b. Something in GTK is freeing the RGBA struct, i.e. a widget that is assuming ownership of the struct contrary to convention

c. The D GC is freeing the struct memory, but since this struct is allocated using either gmallloc or rgba_copy it should be outside the scope of D's GC correct? This is what the D GC page seems to state

d. The gtkdRGBA struct was never allocated to begin or it is using a D allocated struct, looking at the code I don't see a path for this as I can't find any obvious cases where the D allocated struct is used by the class, it's always copied. Only exception would be if someone called the this(GdkRGBA* gdkRGBA) constructor with the D allocated struct and I'm not doing that anywhere.

The other thing I wanted to ask about, purely as a curiousity question, is whether it makes sense for gdkRGBA to be managed by GLib at all. You could convert the gdkRGBA to use the same semantics as ObjectG where it is a D allocated struct and use addRoot to prevent it from being GC'ed. It looks like the GTK widget methods that work with RGBA either copy the struct and manage the copy internally or fill in an existing struct. Obviously the copy method would need to change and not call the underlying rgba_copy method but instead copy the struct using the D method of doing so.

Note I'm not saying that the RGBA memory management should be changed, just an interesting thought as I was looking at things.

@MikeWey
Copy link
Member

MikeWey commented Feb 24, 2016

It's option B.

Something is returning a RGBA pointer but isn't transferring the ownership, so it still frees the object itself. And since it doesn't know about GtkD the handle used by GtkD isn't set to null, and the GC then later on calls free om a dangling pointer.

ObjectG can use addRoot because it's ref counted, and we remove the root when the ref count is one. RGBA isn't ref counted so that solution doesn't work there.

I possibly need to "temporarily" remove the RGBA destructor until the ownership is sorted out.

@MikeWey MikeWey added the Bug label Feb 24, 2016
@MikeWey MikeWey self-assigned this Feb 24, 2016
MikeWey added a commit that referenced this issue Feb 24, 2016
The next step is to also do this in the cases where ownership is
transfered to us. See #146
@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 24, 2016

Thanks Mike, there are very few spots where I'm dealing with RGBA, I'll check the GTK source code for those spots and see if something pops up.

I'll test that commit you just made, I assume the downside of it is leaking gdkRGBA in cases where we get it from a widget and are supposed to free it but don't. In my case since I deal with this struct very infrequently, mostly when users edit the profile, the downside of a small leak is better then a crash IMHO.

Also, if that commit becomes a longer term solution, would it make sense to tweak code like ColorChooserT.getRgba from the current gmalloc to code that would set the ownership of the RGBA? Alternatively, change the parameter from an out to a ref and force the developer to create a RGBA class and use it's gdkRGBA when calling the gtk function.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 25, 2016

Using the updated RGBA.d it appears with initial testing I no longer get the allocation issue, however as expected it leaks a bit of memory. I spent about fifteen minutes moving the transparency slider back and forth in my app which generates a lot of RGBA from ColorChooser and went from 18 MB to 30 MB in size. However, given it's unlikely users will do this I'll take the memory leak over the crash as a temporary workaround so thanks for the quick reply Mike.

I'll do some more testing over the next few days to confirm the workaround, sometimes it can be stubborn to reproduce.

@MikeWey
Copy link
Member

MikeWey commented Feb 27, 2016

The returned structs either directly or via out params now pass on the ownership information, this should reduce the memory leak.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 27, 2016

I've tested out this new version but I still seem to be leaking memory. Let me step back a bit and describe more what I'm seeing and the code I'm using to make sure what I'm seeing is really a leak and not normal D behavior. In my app Terminix, a terminal emulator, there is a profile editor similar to the one in Gnome Terminal where you can configure the profile. An option I have here is to control the transparency via a slider which is binded to a GSettings option. As the user waves the slider back and forth, GSettings gets updated and generates change messages which is sent to my code in the terminal view to update the colors.

When I use the Gnome System Monitor and wave the slider back and forth I can see the process size growing from 14.8 MB up to 30 MB in a period of 5 to 10 minutes and it never seems to drop. My expectation is that since the D GC kicks in on new allocations, as we see with the original RGBA object, the process size would drop. This drop probably would not get you back to the original heap size but it should level off at some point. Would this be your expectation as well?

Originally I was thinking that moving this slider generates a lot of RGBA because the original I code constantly created new RGBAs, however I forgot I optimized this code in the terminal after the first issue to use a fixed set of RGBA classes. The only exception is if you have an option turned on to use the system theme which does a getColor/getBackgroundColor against the style context but I don't use this in testing.

The new code when it gets the slider message simply calls the parse method on each RGBA since it is reading the colors from GSettings. Looking at the source code for RGBA, parse simply re-uses the existing RGBA struct so that should not be causing the leak.

Here's my code in snippets:

private:
    RGBA fg;
    RGBA bg;
    RGBA[16] palette;

    void initColors() {
        fg = new RGBA();
        bg = new RGBA();
        palette = new RGBA[16];
        for (int i = 0; i < 16; i++) {
            palette[i] = new RGBA();
        }
    }

    void applyPreference(string key) {
        switch (key) {
        case SETTINGS_PROFILE_FG_COLOR_KEY, SETTINGS_PROFILE_BG_COLOR_KEY, SETTINGS_PROFILE_PALETTE_COLOR_KEY, SETTINGS_PROFILE_USE_THEME_COLORS_KEY,
        SETTINGS_PROFILE_BG_TRANSPARENCY_KEY:
                if (gsProfile.getBoolean(SETTINGS_PROFILE_USE_THEME_COLORS_KEY)) {
                    vte.getStyleContext().getColor(StateFlags.ACTIVE, fg);
                    vte.getStyleContext().getBackgroundColor(StateFlags.ACTIVE, bg);
                } else {
                    if (!fg.parse(gsProfile.getString(SETTINGS_PROFILE_FG_COLOR_KEY)))
                        trace("Parsing foreground color failed");
                    if (!bg.parse(gsProfile.getString(SETTINGS_PROFILE_BG_COLOR_KEY)))
                        trace("Parsing background color failed");
                }
            bg.alpha = to!double(100 - gsProfile.getInt(SETTINGS_PROFILE_BG_TRANSPARENCY_KEY)) / 100.0;
            string[] colors = gsProfile.getStrv(SETTINGS_PROFILE_PALETTE_COLOR_KEY);
            foreach (i, color; colors) {
                if (!palette[i].parse(color))
                    trace("Parsing color failed " ~ colors[i]);
            }
            vte.setColors(fg, bg, palette);
            break;

So here is it where gets a bit fuzzy and my lack of background in C hurts. I was looking at the other methods in play here and started looking at gio.Settings.getStrv(). It's code in GtkD appears as follows:

    public string[] getStrv(string key)
    {
        return Str.toStringArray(g_settings_get_strv(gSettings, Str.toStringz(key)));
    }

Looking at the code for Str.toStringArray it looks like it is making a copy of the array and strings which is then presumably managed by D from then on. However, I was wondering who is responsible for freeing the C array we just got from g_settings_get_strv. I asked about it on the GTK IRC and one of the people their mentioned that arrays are never allocated on the stack so the caller is responsible for freeing it which is why the Gtk documentation has it annotated as transfer: full (https://developer.gnome.org/gio/stable/GSettings.html#g-settings-get-strv).

Based on that, I tried changing this code to a quick and dirty version as follows to explicitly free the memory:

    public string[] getStrv(string key)
    {
        //return Str.toStringArray(g_settings_get_strv(gSettings, Str.toStringz(key)));
        char** args = g_settings_get_strv(gSettings, Str.toStringz(key));
        string [] result = Str.toStringArray(args);
        gtkc.glib.g_strfreev(args);
        return result;
    }

The code runs fine with no complaints, the memory still grows albeit it feels slower to grow but that could be wishful thinking on my part. Does this make any sense at all?

@MikeWey
Copy link
Member

MikeWey commented Feb 27, 2016

The code runs fine with no complaints, the memory still grows albeit it feels slower to grow but that could be wishful thinking on my part. Does this make any sense at all?

Yes that makes sense, strings currently aren't freed.
It's been a while since my last attempt, but the last time i tried freeing based on the ownership information in the gir files, this resulted in segfaults.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 27, 2016

I'm just going by the GIO Settings API, but it seems like there none of the functions that return strings (gchar*) specify transfer at least in the documentation. Does that mean they are allocated on the stack and freed automatically?

If you want to make another attempt, I'm happy to work with you on it at least with regards to testing and debugging anyway.

@MikeWey
Copy link
Member

MikeWey commented Feb 27, 2016

In the GIO gir file the return types of g_settings_get_string and g_settings_get_strv are annotated with transfer-ownership="full".

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 27, 2016

That sucks that the docs are not correct, must be tough as a C programmer having to manually check GIR files if you can't rely on the docs. I updated my test code for gio.Settings to also handle getString, seems to have slowed down the rate of memory growth quite a bit but not eliminated it completely.

    public string getString(string key)
    {
        //return Str.toString(g_settings_get_string(gSettings, Str.toStringz(key)));
        char* str = g_settings_get_string(gSettings, Str.toStringz(key));
        scope(exit) {gtkc.glib.g_free(str);}
        return Str.toString(str);
    }

    public string[] getStrv(string key)
    {
        //return Str.toStringArray(g_settings_get_strv(gSettings, Str.toStringz(key)));
        char** args = g_settings_get_strv(gSettings, Str.toStringz(key));
        scope(exit) {gtkc.glib.g_strfreev(args);}
        return Str.toStringArray(args);
    }

Like I said, I'm happy to help if you want to take a run at this however if you don't have time at this moment no worries.

@MikeWey
Copy link
Member

MikeWey commented Feb 28, 2016

I've added the freeing of C strings for functions that return them.

No problems sofar running the demos.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Feb 28, 2016

I've re-built Terminix using this change and upon initial testing it looks like everything works fine. I assume the change is looking at the transfer attribute since I noticed that methods that have Transfer: None in the docs like gio.Settings.listRelocatableSchemas are not freeing the string which is awesome.

Thanks for then effort on this Mike, really appreciate it.

@gnunn1
Copy link
Contributor Author

gnunn1 commented Mar 7, 2016

Closing this issue as I haven't had any complaints of this from users for over a week now. Any chance of getting a 3.23 release of GtkD with these changes?

@gnunn1 gnunn1 closed this as completed Mar 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants