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 639770 - gtk_quit_add() removed in GTK 3 with no replacement
gtk_quit_add() removed in GTK 3 with no replacement
Status: RESOLVED OBSOLETE
Product: glib
Classification: Platform
Component: gio
unspecified
Other Linux
: Normal major
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2011-01-17 18:01 UTC by Lennart Poettering
Modified: 2018-05-24 12:57 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Lennart Poettering 2011-01-17 18:01:37 UTC
libcanberra relied on GTK2's gtk_quit_add() to flush its event sound queue before an application is going down. Since gtk_quit_add() got removed from GTK3 without replacement libcanberra currently cannot be compiled against it.

We need some kind of replacement for gtk_quit_add() in either gtk or glib, for cases like libcanberra where some (non-essential) work needs to be done at shutdown where the full Gtk/Glib stack is still up and all libraries still initialized. While application code probably won't need this, code that is either loaded as a dynamic library or as loadable module and hence cannot simply be called from the main() function might need functionality like this.

libcanberra queues sound events in a local queue before dispatching them to the sound server to ensure that normal GdkEvent handling is not unnecessarily delayed (e.g. by CPU intensive work such as to uncompressing vorbis files) and events can be coalesced. That queue must be dispatched before the app terminates so that no event sounds are lost (i.e. somebody presses a quit button and you want the button sound to be heard before the app is gone) -- however the sound events are not really essential so it is fine if they are lost if the app terminates abnormally due to crashing.

In some other cases quit hooks like this are very much necessary too. Example: in some cases GIO will buffer data that is written out in-process. To ensure that this is sync'ed out code must manually sync it. While this is easily done from apps, this is impossible to do from loadable modules/shared libraries if there is no way to hook into the application shutdown process.

g_atexit() or GCC destructors are no suitable option since the thread context and their execution order is not defined. The dispatching of the event sound queue (or gio buffer syncs) must take place at a time where all libraries are still initialized which basically means that this must be done from within the glib main loop code -- and not after main() has terminated as would happen with atexit().
Comment 1 Lennart Poettering 2011-01-17 18:02:17 UTC
This is also tracked for libcanberra as https://bugs.freedesktop.org/show_bug.cgi?id=32839
Comment 2 Travis Reitter 2011-01-24 20:59:21 UTC
The removal of gtk_quit_add() (without replacement) also wedges clients of libcanberra, such as Empathy.

Built against the latest GTK3 and libcanberra-gtk, Empathy fails to run due to the unresolved symbol:

empathy: symbol lookup error: /opt/gnome/lib64/gtk-3.0/modules/libcanberra-gtk-module.so: undefined symbol: gtk_quit_add
Comment 3 Christian Kirbach 2011-01-27 17:41:41 UTC
Confirming report, since the point raised looks valid to me. This has kept people in trouble testing development GNOME since Jan 4, including me.
Comment 4 Colin Walters 2011-02-11 00:55:13 UTC
(In reply to comment #0)
> libcanberra relied on GTK2's gtk_quit_add() to flush its event sound queue
> before an application is going down. Since gtk_quit_add() got removed from GTK3
> without replacement libcanberra currently cannot be compiled against it.

Well, you could just remove the call and accept the loss for now.

> We need some kind of replacement for gtk_quit_add() in either gtk or glib,

Probably, though we also discussed moving at least quit sounds

> In some other cases quit hooks like this are very much necessary too. Example:
> in some cases GIO will buffer data that is written out in-process. To ensure
> that this is sync'ed out code must manually sync it. While this is easily done
> from apps, this is impossible to do from loadable modules/shared libraries if
> there is no way to hook into the application shutdown process.

This one is certainly messy...for libc, it's guaranteed that FILE * streams are flushed on exit().  There's no such guarantee for GIO, and it's definitely not clear to me that some g_quit_add would be the right thing to do.  I think API consumers simply have to flush when it makes sense.

> g_atexit() or GCC destructors are no suitable option since the thread context
> and their execution order is not defined.

Hmm...I think in reality glibc is going to call atexit() from whatever thread called exit(), which is pretty much always going to be the main thread.  You could always save the current thread ID and just bail in your atexit() handler if it's not the right thing.

> The dispatching of the event sound
> queue (or gio buffer syncs) must take place at a time where all libraries are
> still initialized which basically means that this must be done from within the
> glib main loop code -- and not after main() has terminated as would happen with
> atexit().

But what library de-initialization happens after the glib mainloop is done?
Comment 5 Lennart Poettering 2011-02-17 23:55:45 UTC
(In reply to comment #4)
> (In reply to comment #0)
> > libcanberra relied on GTK2's gtk_quit_add() to flush its event sound queue
> > before an application is going down. Since gtk_quit_add() got removed from GTK3
> > without replacement libcanberra currently cannot be compiled against it.
> 
> Well, you could just remove the call and accept the loss for now.

Grrrrr. Then sounds will get lost.

My hope by not removing this was that this would put some pressure on the folks who removed gtk_quit_add() to feel responsible to actually add a replacement. Since that apparently didn't work and nobody cares enough I will now comment this in libcanberra, but really, gtk 3 needs to be fixed.

> > We need some kind of replacement for gtk_quit_add() in either gtk or glib,
> 
> Probably, though we also discussed moving at least quit sounds

Nah, that is a hack. And is actually quite a bit of work. If this work is done it should be to do things properly, not to just tape around it by moving the triggers elsewhere.

> > In some other cases quit hooks like this are very much necessary too. Example:
> > in some cases GIO will buffer data that is written out in-process. To ensure
> > that this is sync'ed out code must manually sync it. While this is easily done
> > from apps, this is impossible to do from loadable modules/shared libraries if
> > there is no way to hook into the application shutdown process.
> 
> This one is certainly messy...for libc, it's guaranteed that FILE * streams are
> flushed on exit().  There's no such guarantee for GIO, and it's definitely not
> clear to me that some g_quit_add would be the right thing to do.  I think API
> consumers simply have to flush when it makes sense.
> 
> > g_atexit() or GCC destructors are no suitable option since the thread context
> > and their execution order is not defined.
> 
> Hmm...I think in reality glibc is going to call atexit() from whatever thread
> called exit(), which is pretty much always going to be the main thread.  You
> could always save the current thread ID and just bail in your atexit() handler
> if it's not the right thing.

A lot of code calls exit() from the non-main thread. All this OOM-like code that just wants to quit if things are hosed don't send this back to the main thread. But even if they would this doesn't matter really.

> > The dispatching of the event sound
> > queue (or gio buffer syncs) must take place at a time where all libraries are
> > still initialized which basically means that this must be done from within the
> > glib main loop code -- and not after main() has terminated as would happen with
> > atexit().
> 
> But what library de-initialization happens after the glib mainloop is done?

Well, most programs probably do something like this:

int main() {
INITIALIZE_SOME_STUFF;
RUN_GLIB_MAIN_LOOP;
SHUTDOWN_SOME_STUFF;
}

We want out hook to run between RUN_GLIB_MAIN_LOOP and SHUTDOWN_SOME_STUFF so that whatever might get deinitialized is done so after us. That deinitialization could be everything, for example a simple snd_config_update_free_global() which is something you want to call in ALSA to make your stuff valgrind clean. (Well, and don't tell me that people shouldn't do this in non-debug software, it doesn't matter and libcanberra needs to be compatible with whatever it is loaded into).
Comment 6 Colin Walters 2011-02-18 18:38:18 UTC
(In reply to comment #5)
> 
> My hope by not removing this was that this would put some pressure on the folks
> who removed gtk_quit_add() to feel responsible to actually add a replacement.

I don't think it works that way really =)

> Since that apparently didn't work and nobody cares enough I will now comment
> this in libcanberra, but really, gtk 3 needs to be fixed.

Well, it's debatable, which is what we're doing:

> > Probably, though we also discussed moving at least quit sounds
> 
> Nah, that is a hack. And is actually quite a bit of work. If this work is done
> it should be to do things properly, not to just tape around it by moving the
> triggers elsewhere.

I think it ultimately would be best; we avoid problems with legacy X apps, etc. (though I guess those don't have sounds on buttons or whatever).

> A lot of code calls exit() from the non-main thread. All this OOM-like code
> that just wants to quit if things are hosed don't send this back to the main
> thread. But even if they would this doesn't matter really.

But if a thread called exit() from *any* thread, your old scheme of using gtk_quit_add wouldn't do anything either.  Right?

So using an atexit handler and checking if it's the main thread might actually do better, since you'd start working for things that didn't use gtk_main() before.

> Well, most programs probably do something like this:
> 
> int main() {
> INITIALIZE_SOME_STUFF;
> RUN_GLIB_MAIN_LOOP;
> SHUTDOWN_SOME_STUFF;
> }
> 
> We want out hook to run between RUN_GLIB_MAIN_LOOP and SHUTDOWN_SOME_STUFF so
> that whatever might get deinitialized is done so after us. That
> deinitialization could be everything, for example a simple
> snd_config_update_free_global() which is something you want to call in ALSA to
> make your stuff valgrind clean. (Well, and don't tell me that people shouldn't
> do this in non-debug software, it doesn't matter and libcanberra needs to be
> compatible with whatever it is loaded into).

I wouldn't argue you can't do it; however I would say that libcanberra, if it did use an atexit() handler, would then need to provide an API to disable it and immediately run the cleanup.
Comment 7 Lennart Poettering 2011-02-18 18:55:55 UTC
(In reply to comment #6)

> > Nah, that is a hack. And is actually quite a bit of work. If this work is done
> > it should be to do things properly, not to just tape around it by moving the
> > triggers elsewhere.
> 
> I think it ultimately would be best; we avoid problems with legacy X apps, etc.
> (though I guess those don't have sounds on buttons or whatever).

Well, I think it's a bad idea. We decided not to do it like this once, and the demise of gtk_quit_add() is a bad reason to now reconsider that.

If you generate those events out-of-process you get all those difficulties with acquiring enough context. And you open an entire new can of worms with trying to coalesce sounds that shall be played when a window is closed to due a button click. Because the button sounds are then generated in the toolkit and the window sounds in the WM, so suppressing one by the other is incredibly hard. And button clicks are probably more often used to close windows than non-button clicks (think the OK button in a dialog).

We really want to generate those sounds from the toolkit and nowhere else.

> 
> > A lot of code calls exit() from the non-main thread. All this OOM-like code
> > that just wants to quit if things are hosed don't send this back to the main
> > thread. But even if they would this doesn't matter really.
> 
> But if a thread called exit() from *any* thread, your old scheme of using
> gtk_quit_add wouldn't do anything either.  Right?

Wel, sure if people call exit() in the middle of everything then there is no normal clean shutdown. But I think it is expected then that no shutdown sounds will be played.

But this discussion is besides the point here. I was arguing that if we use atexit() the execution environment of our hook is completely undefined regarding initialization state of other libs and threads. However for gtk_quit_add() we knew exactly when we would be called and from which thread. This his nothing to do at all with the fact that calling exit() just like that does not trigger event sounds.

> So using an atexit handler and checking if it's the main thread might actually
> do better, since you'd start working for things that didn't use gtk_main()
> before.

Well, nice idea. But since we have no clue in which context we are being called and which libraries are already shut down this is nearly impossible. Also, you need to understand what dispatching these things actually means: we might end up creating an audio thread from it. This is really nothing you want to do in an atexit() handler.

Really, atexit() is not an option. It's a pointless discussion.

> 
> > Well, most programs probably do something like this:
> > 
> > int main() {
> > INITIALIZE_SOME_STUFF;
> > RUN_GLIB_MAIN_LOOP;
> > SHUTDOWN_SOME_STUFF;
> > }
> > 
> > We want out hook to run between RUN_GLIB_MAIN_LOOP and SHUTDOWN_SOME_STUFF so
> > that whatever might get deinitialized is done so after us. That
> > deinitialization could be everything, for example a simple
> > snd_config_update_free_global() which is something you want to call in ALSA to
> > make your stuff valgrind clean. (Well, and don't tell me that people shouldn't
> > do this in non-debug software, it doesn't matter and libcanberra needs to be
> > compatible with whatever it is loaded into).
> 
> I wouldn't argue you can't do it; however I would say that libcanberra, if it
> did use an atexit() handler, would then need to provide an API to disable it
> and immediately run the cleanup.

Well, there are very little actual direct users of libcanberra. Almost all gtk apps just end up using it via GTK_MODULES. That means the app developers don't even know that lc will be in their process and that means you cannot ask them to actually call any functions from it.

Really, stop thinking about atexit(). It doesn't work.
Comment 8 Matthias Clasen 2011-02-18 20:36:29 UTC
Atexit handlers from (un)oadable modules are a very obvious nonstarter.

If lifecycle hooks are necessary for the mainloop, they should be added where the mainloop lives: in glib. GTK+ should not have its own mainloop extension api.

Maybe it is time to reconsider the use of a module here, though...
Comment 9 Lennart Poettering 2011-02-18 20:42:32 UTC
Hmm, but if we get rid of the module and make gtk generate those sounds natively we'd still need some way to hook into the main loop finishing.
Comment 10 Allison Karlitskaya (desrt) 2011-03-14 17:52:51 UTC
Another argument for having it in glib:

GSettings has almost exactly the same issue: GDBus defers work for performance reasons, so we don't get the message sent until later.
Comment 11 Lennart Poettering 2011-08-30 15:12:57 UTC
As discussed with Owen on IRC, maybe something like this would be a workable solution:

Introduce a new call g_flush_all() or so, which people can hook their callbacks into. This call could be explicitly called by those who need gsettings, libcanberra or gvfs after the mainloop, to sync their buffers. It would also be implicitly called at the end of g_main_loop_run() if g_main_depth() reaches 0.
Comment 12 GNOME Infrastructure Team 2018-05-24 12:57:58 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/glib/issues/389.