GNOME Bugzilla – Bug 617036
Scoping: Anonymous instances immediately unreffed
Last modified: 2010-06-16 08:53:50 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()'.
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.
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.
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...
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 ;)
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.
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.
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