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 617036 - Scoping: Anonymous instances immediately unreffed
Scoping: Anonymous instances immediately unreffed
Status: RESOLVED NOTABUG
Product: vala
Classification: Core
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: Vala maintainers
Vala maintainers
Depends on:
Blocks:
 
 
Reported: 2010-04-28 06:38 UTC by Mikkel Kamstrup Erlandsen
Modified: 2010-06-16 08:53 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Mikkel Kamstrup Erlandsen 2010-04-28 06:38:26 UTC
If I have some pseudo-Vala code looking like:

main()
{
  new MyInstance();

  new MainLoop(null, false).run();
}

It generates C code like this:

main()
{
  tmp1 = my_instance_new();
  my_instance_unref(tmp1);

  g_main_main_loop_run (tmp2 = g_main_loop_new (NULL, FALSE));
  g_main_loop_unref (tmp2);
}

whereas I'd expect the anonumous MyInstance instance to be alive in the entire scope of the method. Ie:

main()
{
  tmp1 = my_instance_new();

  g_main_main_loop_run (tmp2 = g_main_loop_new (NULL, FALSE));
  g_main_loop_unref (tmp2);

  my_instance_unref(tmp1);
}

Indeed this is the case if I create the instance like 'var i = new MyInstance()'.
Comment 1 Jürg Billeter 2010-04-28 07:39:06 UTC
This is intentional. Objects created as part of an expression are unreferenced at the end of the statement when not assigned to a variable. You haven't mentioned an actual problem, so I'm resolving this as NOTABUG. Feel free to reopen it if there is an actual problem caused by this.
Comment 2 Mikkel Kamstrup Erlandsen 2010-05-07 19:06:08 UTC
Reopening - sorry for the fuzz :-)

Motivated by another user having the exact same problems as me let me elaborate a bit.

Creating an instance may have all sorts of unpredictable long term side effects - like setting up some signal monitoring, starting some async ops, or other that'll be triggered inside the mainloop (or other following construct).

The exact issue in question here (both for me and the other guy) was that the anonymous instance listened for some signals from DBus and would perform some actions based on that.
Comment 3 Mikkel Kamstrup Erlandsen 2010-06-08 13:46:01 UTC
Another similar example:

using GLib;
using Gtk;

class Test {
    public static int main (string[] args) {
        Gtk.init (ref args);

        var win = new Gtk.Window(Gtk.WindowType.TOPLEVEL);
        var a = new PtrArray ();
        a.add (new Gtk.Label("Hello"));
        win.add((Widget)a.index(0));
        win.show_all ();
        Gtk.main ();

        return 0;
    }
}

This one crashes because the PtrArray doesn't grab a ref to the anonymous label. Fixing the scoping of anonymous variables would actually not solve this if some external party held a ref on the PtrArray...
Comment 4 Jürg Billeter 2010-06-11 12:43:22 UTC
These two examples really sound like issues in other places that you want to work around using the proposed change. The issue in the second example is certainly that PtrArray is a very low level binding, which doesn't take care of memory management. We'll be adding an improved binding of GPtrArray (called GenericArray in vala) shortly, which will address this issue.

Regarding the first issue: It's actually arguable whether the instance should just be ref'd when connecting to the D-Bus signals, however, let's just ignore that part and assume that behavior is kept as it is. Let's take a look at how two examples would behave in case your proposal was implemented.

Example 1:
void main () {
    ...
    new DBusListenerObject ();
    ... main loop ...
}

Everything would work as expected.

Example 2:
void create_dbus_listener () {
    new DBusListenerObject ();
}

void main () {
    ...
    create_dbus_listener ();
    ... main loop ...
}

Same issue with and without your proposal.

In my opinion, it's really just a workaround that would help with buggy code in some special circumstances. It would also possibly increase memory usage and lead to inconsistency (temporary objects as an argument would still be freed and it doesn't fit the usual RAII semantics we have in Vala).

I hope this explains my reasoning a bit better. Feel free to ask again in case I've only caused more confusion ;)
Comment 5 Mikkel Kamstrup Erlandsen 2010-06-11 12:58:19 UTC
I am still not sure we are on the same page here... :-)

Example would clearly not work because the anonymous DBusListenerObject goes out of scope when the stack frame for create_dbus_listener() ends. I don't think anyone (except maybe a few Javascript hackers) would expect that.

A anonymous object passed as argument to a function would be scoped to live in the stack frame of the function call, not the call site.

I am quite confident that this is how it works in Java at least.
Comment 6 Jürg Billeter 2010-06-11 13:13:37 UTC
I'm actually quite confident that Java doesn't guarantee anything like that. If you have time you could try something like this:

class Foo {
    protected void finalize ()  {
        // print some message
    }
}

static void main () {
{
   new Foo ();
   String[] array = new String[SOME_LARGE_NUMBER];
   for (int i = 0; i < array.length; i++) {
        array[i] = i.toString ();
   }
   // print some other message
}

If the loop is large enough to trigger a garbage collection run, I'd expect that the finalizer gets run before the end of main. With a conservative garbage collector it might be tricky to trigger as the stack might still contain a reference to Foo even though it's not used anymore.
Comment 7 Mikkel Kamstrup Erlandsen 2010-06-16 08:53:50 UTC
Bugger! As always, you are right. This program:

class AnonymousScopes {

    protected void finalize ()  {
        System.out.println ("FINALIZED!");
    }
    
    public static void main (String[] args) {
        new AnonymousScopes ();
        String[] array = new String[100];
        for (int i = 0; i < array.length; i++) {
            array[i] = Integer.toString (i);
            System.gc();
	}
        System.out.println("DONE");
    }
}

Prints out:
$ java AnonymousScopes 
FINALIZED!
DONE