After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 586577 - Reference counting for GLib.SList
Reference counting for GLib.SList
Status: RESOLVED NOTABUG
Product: vala
Classification: Core
Component: Code Generator
unspecified
Other All
: Urgent blocker
: ---
Assigned To: Vala maintainers
Vala maintainers
wrong-code
: 624249 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2009-06-21 22:19 UTC by Kuteynikov
Modified: 2017-02-20 18:07 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Sample code which triggers the bug. (464 bytes, text/plain)
2009-08-02 14:05 UTC, raster
Details
the same test with Gee and ArrayList (385 bytes, text/plain)
2009-08-02 14:35 UTC, raster
Details

Description Kuteynikov 2009-06-21 22:19:52 UTC
Please describe the problem:
When object is being added to list, Vala generates code that increases it's reference count:

self->objlist = g_slist_prepend (self->objlist, (_tmp5 = obj, (_tmp5 == NULL) ? NULL : g_object_ref (_tmp5)));

but when I remove object, no decrease of ref.count happens:

self->objlist = g_slist_remove (self->objlist, obj);

It causes memory leaks.

Steps to reproduce:
1. objlist.prepend(obj);
2. objlist.remove(obj);
3. see generated code


Actual results:
memory leaks

Expected results:
g_object_unref(obj);

Does this happen every time?
yes

Other information:
Comment 1 raster 2009-08-02 14:03:09 UTC
I can confirm it. It happens in Vala 0.5.4 (the one shipped with Ubuntu) and Vala 0.7.4.

I attach a sample code. The output of this code is:

References: 1d
References: 2d
References: 3d
References: 3d

But it should be

References: 1d
References: 2d
References: 3d
References: 2d
Comment 2 raster 2009-08-02 14:05:43 UTC
Created attachment 139734 [details]
Sample code which triggers the bug.

This code contains the sample commented in comment #1
Comment 3 raster 2009-08-02 14:35:02 UTC
Created attachment 139738 [details]
the same test with Gee and ArrayList

This is the same test but using Gee and ArrayList. This works as expected, so there is really a bug in Glib's List.
Comment 4 raster 2009-08-05 08:22:21 UTC
I tested the test code from #2 in Vala 0.7.5 and still fails.
Comment 5 Marc-Andre Lureau 2009-11-05 23:56:06 UTC
afaik, there is nothing that can be done, because List doesn't do refcounting itself. As you said, you should use Gee.
Comment 6 Marc-Andre Lureau 2009-11-06 00:31:20 UTC
perhaps reimplementing functions that remove elements from the list could be a solution though.
Comment 7 raster 2009-11-06 00:33:22 UTC
Marc-Andre: but, as you can see in the examples I uploaded, it is doing refcounting, but only when adding an element, not when overwritting. That's the bug. It should do it in both cases.
Comment 8 Marc-Andre Lureau 2009-11-06 09:47:07 UTC
(In reply to comment #7)
> Marc-Andre: but, as you can see in the examples I uploaded, it is doing
> refcounting, but only when adding an element, not when overwritting. That's the
> bug. It should do it in both cases.

Adding a ref is easy, that's the way you pass refcounted references to functions that have "owned". On the other hand, remove() is different, because you don't know if the reference is actually removed.

Also, remove_all() is even worst. You don't know how many times the reference has been removed.

Well, I am not sure, but it's interesting to discuss it, as I have been bitten by this in the past.
Comment 9 carlo.teubner 2010-08-01 17:08:03 UTC
(In reply to comment #6)
> perhaps reimplementing functions that remove elements from the list could be a
> solution though.

Yes; the following modification to the List binding fixes this example:


		[ReturnsModifiedPointer ()]
		[CCode (cname = "g_list_remove")]
		public unowned G _remove (G data);

		[ReturnsModifiedPointer ()]
		[CCode (cname = "_vala_list_remove")]
		public unowned G remove (G data)
		{
			foreach (var v in this) {
				if (v == data) {
					Object vo = v as Object;
					if (vo != null) {
						vo.unref ();
					}
					break;
				}
			}

			return _remove (data);
		}

Limitations:
1. Only works for contained Objects, not types with custom unref functions. 
2. Generates compiler warnings, since I had to change the return type of both functions from void to unowned G.

Similar things could be done with the other removal functions.

Regarding limitation 1, it seems to me that we should be able to simply write "var mine = (owned) v;" inside the loop so that it gets destroyed when it goes out of scope; however, that does not seem to happen (the generated C code deals with gpointers and seems unaware of the true type of the elements).

Regarding limitation 2, it should be possible to fix this by allowing (requiring) a void method to return a value of the appropriate type if it is marked as ReturnsModifiedPointer ().

An alternative approach might be to let the compiler handle all this, by introducing various new attributes:

		[ReturnsModifiedPointer ()]
		[RemovesFirstMatch ()]
		public void remove (G data);

The only problem is, how does the compiler now how to iterate over the elements? Well, the best way might be to require the type (i.e. GLib.List) to have an iterator() function (which also supports foreach). Currently foreach support for List is hardcoded, but it should be possible to implement this in vapi instead, if we allow custom class/struct definitions inside vapi (see my comment 3 in bug 622261).
Comment 10 carlo.teubner 2010-08-01 17:33:21 UTC
Note also that List and SList are already special to automatically unref their contents when they get destroyed. I've entered bug 625772 for all the glib containers that don't even do this.
Comment 11 Philip Withnall 2010-08-04 08:05:44 UTC
*** Bug 624249 has been marked as a duplicate of this bug. ***
Comment 12 Marc-Andre Lureau 2010-08-04 08:41:30 UTC
(In reply to comment #9)
> Yes; the following modification to the List binding fixes this example:
>         [ReturnsModifiedPointer ()]
>         [CCode (cname = "g_list_remove")]
>         public unowned G _remove (G data);
> 
>         [ReturnsModifiedPointer ()]
>         [CCode (cname = "_vala_list_remove")]
>         public unowned G remove (G data)
>         {
>             foreach (var v in this) {
>                 if (v == data) {
>                     Object vo = v as Object;
>                     if (vo != null) {
>                         vo.unref ();
>                     }
>                     break;
>                 }
>             }
> 
>             return _remove (data);
>         }

That looks similar to binding enhancement I proposed in https://bugzilla.gnome.org/review?bug=624249&attachment=166328
Comment 13 Jürg Billeter 2010-12-22 08:23:33 UTC
(In reply to comment #12)
> That looks similar to binding enhancement I proposed in
> https://bugzilla.gnome.org/review?bug=624249&attachment=166328

We should really handle this correctly without requiring calls to separate methods. Probably requires special handling in code generator to handle this for all cases.
Comment 14 Daniel Espinosa 2017-02-20 18:07:18 UTC
This is not a Vala bug but a GLib bug.

If you check at GList, it hasn't a GDestroyNotify at initialization, like GHashTable, no g_list_remove() returns data to handle by you automatically, then is your responsability as a programmer to know how to handle data, in this case each time you call GLib.List.remove() on ref counted objects, you should decrease it by calling unref() explicitly in Object classes or the one provided by your bindings.

So if GLib adds a new GLib.List<Object>() specialized handler for GObject classes, taking care about ref/unref, we just take care on using GLib.List<G> on Objects and use Gee.ArrayList<G> instead.ç

Use GLib.List<G> as returning value where your class takes care about your objects not returned GLib.List<G> one.

If you create a GLib.List<G> as return value, creating one using append(), as described, advice your user to unref all returned objects using GLib.List<G>.foreach() before get out of context.

For public API you may want to use GLib.Queue(), it returns removed objects, then it can be unref by Vala automatically.