GNOME Bugzilla – Bug 138161
Return value from gtk_main_iteration is not valid
Last modified: 2011-02-04 16:16:20 UTC
Compile and run the attached program. The print statements track what the correct value should be and show the incorrect value being returned.
Created attachment 25956 [details] gtk_main_iteration test program
This bug prevent people from manually taking control of the gtk event loop. How do projects work around this?
Docs bug - the return value is whether any events are pending.
Didn't actually mean to close, since the docs are wrong.
Created attachment 25994 [details] New test case demonstrating further problems
Well, if that's what the docs are supposed to say, that makes the problem *worse*, not better. There is no way to determine if gtk_main_quit() was called without explicity setting a global variable on every exit point in user code. Even this solution may not be possible if someone is using a builder like Glade. In addition, there is a mismatch between the return code and gtk_events_pending. The attached second test case demonstrates the problem. The docs currently explain what is actually the most useful behavior; the flag should reflect whether gtk_main_quit() was called. That is the only piece of information which it is not possible to get using gtk_XXX functions (if this is an incorrect statement, please feel free to correct me).
Okay, I figured this would be an easy patch, then I looked at the Gtk code for gtk_main(). Grumble. Since gtk_main() is not implemented in terms of gtk_main_iteration() there is a lot of extra state floating around that gtk_main() adds. It also explains why gtk_loop_level returns an incorrect answer when using iterations. So much for an easy patch. The correct solution is probably to split gtk_main() apart and add two helper functions: gtk_main_iteration_enter() and gtk_main_iteration_leave(). gtk_main_iteration_enter() would establish all of the state which now sits at the beginning of gtk_main(); gtk_main_iteration_leave() would handle all of the cleanup which now sits at the end of gtk_main() and gtk_main_iteration() would handle the main loop in the center. Consequently, gtk_main() would remain unchanged. However, programs could get at the main loop correctly from their own source code by explicitly calling the gtk_main_iteration_(enter,do,leave) trio. At this point, all of the loop level and event pending functions would be correct as well as the state which gtk_main() adds (which is currently unavailable when using gtk_main_iteration())
gtk_main_quit() is *purely* convenience functionality; no GTK+ function ever calls it. If you want to write your own main loop, just keep your own quit variable. (Note also that gtk_main_iteration() is obsolete; see the mainloop functionality in GLib for the current interfaces) Chnaging the return value of the gtk_main_iteration() function to mean something different isn't an option.
This is not true. Please look at the code. gtktreeview.c and gtkwidget.c both call gtk_main_iteration in 2.4.0. Possibly those particular functions might also be convenience, but it is there. In fact, since the return value is useless, both of these functions throw it away. As I explained earlier, the issue is with builders like Glade which are wired to use gtk_main_quit(). I *cannot* just "write my own main loop" without access to the gtk state. I'm also becoming convinced that there are some threading issues that are the fault of not having the initialized state activated. Finally, if the system gets split into gtk_main_iteration_{enter,do,leave}, then the return value of gtk_main_iteration can remain exactly the same because gtk_main_level() will now return a useful value. Clearly we're talking past one another, I'll try to submit a patch later to clarify.
Created attachment 26016 [details] [review] Patch to isolate mainloop pieces This patch creates three new functions: gtk_main_loop_enter() gtk_main_loop_iterate() gtk_main_loop_leave() and then implements the gtk_main() function in terms of them. The only question is whether the gtk_main_loop_iterate function is equivalent to the g_main_loop_run() function. Someone with far more knowledge than I have about gtk internals should looks at that. However, the preliminary tests I have run all seem to behave equivalently.
Created attachment 26017 [details] New test case showing how to implement a user code main loop correctly
The original docs appear to be correct in intent about gtk_main_iteration(). The relevant code is from gtkmain.c: > if (main_loops) > return !g_main_loop_is_running (main_loops->data); > else > return TRUE; The intent is that if a main_loop is still active, return the complement of its state. The primary means of killing an active main loop is gtk_main_quit(), so this code matches the docs. I might argue that returning TRUE when no loop exists is probably not the best choice, but it is now standard and probably cannot be fixed while maintaining binary compatibility.
Hmm, you seem to be right about gtk_main_iteration() return value; because it's an obsolete useless function, I had forgotten what it returned; the correct resolution is probably to add "or if gtk_main_run() h has not been called" to the docs. Let me give you a very brief overview of the big picture to show you why you are really looking in the wrong places for writing a custom main looop: The basic unit of running the main loop in GLib/GTK+ g_main_context_iteration() which checks for pending events, possibly waits for new events, and then dispatches the events found. (You can actually break it into individual pieces, but I'll ignore that for now.) This operation has no concept of quitting. The concept of quiting is introduced with GMainLoop; g_main_loop_run() conceptually does while (loop->running) g_main_context_iteration (loop->context, ...) while g_main_loop_quit() does loop->running = FALSE; gtk_main_run() and gtk_main_quit() are convenience functions that create a stack of GMainLoop objects; gtk_main_run() adds a new loop to the stack and calls g_main_loop_run() on it, gtk_main_quit() quites the topmost loop. No GTK+ function ever calls either of these two functions; they are purely convenience. You can write a perfectly correct GTK+ program using only the GLib functionality. While some GTK+ functions may run a recursive main loop (gtk_widget_show_now(), gtk_dialog_run(), gtk_clipboard_wait...()) the main loop they run always terminates based only on conditions documented for these functions; if gtk_main_quit() caused these functions to terminate, that would be a serious bug in GTK+. These functions thus don't use gtk_main_run(), they use GMainLoop or just do iterations directly and check their own quit variable. (The main thing that GMainLoop gives you is that it works correctly with multiple threads; the fact that gtk_widget_show_now() doesn't use it is a minor bug.)
The context that this came up in was a PyGtk app that needed to make the Gtk loop inferior to another framework event loop. By opening the Gtk loop, I can make both systems intermix. Unfortunately, PyGtk (and other wrappers) very rarely wrap glib since the languages natively have most of its functionality. Consequently, glib calls are really out of the question. Gtk needs to provide the hooks to get at the glib calls directly (even if they are just pass through functions). As for gtk_main_quit(), the fact that a Gtk function may not work if I take over the event loop is a bug in my book. There is no indication that gtk_main_quit() is merely convenience or that it may not work. I think most developers would be stunned to have several of the gtk functions simply quit working because an app took manual control of the event loop. At this point, this is clearly an architectural discussion. I'd like to take this to one of the mailing lists. Which mailing list should I take it to? gtk-devel or gtk-app-devel?
gtk-devel is the right list
Mass changing gtk+ bugs with target milestone of 2.4.2 to target 2.4.4, as Matthias said he was trying to do himself on IRC and was asking for help with. If you see this message, it means I was successful at fixing the borken-ness in bugzilla :) Sorry for the spam; just query on this message and delete all emails you get with this message, since there will probably be a lot.
Closing as NOTABUG because the discussion has moved to the mailing lists. If this bug report remains relevant, please reopen and add a summary of developments.
Reopening, closed inappropriuately.