Re: deprecating gdk threads
- From: Pavel Holejsovsky <pavel holejsovsky gmail com>
- To: gtk-devel-list gnome org
- Subject: Re: deprecating gdk threads
- Date: Mon, 06 Aug 2012 17:08:00 +0200
Hi,
On 8/4/2012 3:52 PM, Emmanuele Bassi wrote:
Basically, the consensus from previous discussions even in language
bindings, it is up to application authors to handle callbacks coming in
another threads by wrapping code which needs to manipulate GTK into
function/closure and schedule its execution via GLib.idle_add(). While this
works generally pretty well (except that it puts the burden of knowing that
the callback might be invoked in non-main thread to the application
developer), there is unresolved issue with regard to destructors/finalizers.
the callback for idle_add() will be invoked in the main thread - if we
assume that the main thread is the one that called
gtk_init()/gtk_main(), which is usually the assumption under which we
generally operate. that's the whole point of using the idle_add()
function to schedule an UI update.
Sorry for not being clear. I wanted to say that previously, when higher
language registered callback or signal, it didn't have to bother whether
the callback or signal will be called with proper thread, because
gdk/clutter_thread_() magic worked automatically in the background. Now
the application writer has to care whether the signal (or callback) can
come in some other thread and whether GLib.idle_add() is needed or not.
It is just potential source of bugs when GLib.idle_add() is not used
when it should be.
AFAICS, this does not play nice with gdk threads deprecation, because one
way to solve the GC finalization problems was to gdk_threads_enter() before
entering g_object_unref() call.
that would have only worked for specific classes, namely the ones that
manage GDK resources; dispose and finalize should be thread safe in
GObject, as far as GObject is concerned. using
threads_enter()/unref()/leave() would also have worked only on X11,
and assuming that the support for threading in Xlib was enabled.
otherwise, it simply was working by sheer accident.
Understood and agreed.
One way to solve this would be to put the burden on the bindings
implementation, and force the bindings to queue g_object_unref() calls using
g_idle_add() to be executed in the main thread. This seems to be rather
ineffective though.
can you explain why would it be ineffective?
1) AFAIK it is not possible for binding implementation to find out
whether it is safe to invoke g_object_unref() directly or whether it has
to be invoked in main thread context. Simple heuristics like 'all
GTK/GDK/Clutter threads should use main thread destruction path' does
not work, e.g. Gtk.SourceView clearly needs it too. So bindings would
have to resort to perform all g_object_unref() dispatched to main thread
context.
2) There is a *lot* more of g_object_ref()/unref() calls going on when
using bindings than when using APIs 'manually' from C. In C, in
majority cases caller does not need to store object, so it can avoid
ref/unref on temporary returned objects. An automatic binding cannot
know for how long the object returned from any function call will be
needed, so it needs to ref() it and store, and when GC removes binding
proxy, unref() will happen. In most of the cases through, the unref()
will not lead to actual object destruction.
These 2 points combined lead me to belief that sending every unref() to
main thread using g_idle_add() by binding implementation might be
ineffective.
Another way to solve this problem might be inside GDK itself, which might
check whether native window disposal function needs to be transferred to the
main thread and if yes, do it internally and transparently. The advantages
are:
+ it is done only when needed (eg only on Win32/Quartz/whatever, only when
called from 'bad' thread, only when g_object_unref() actually results in
window destruction)
+ much more error prone
- the actual native window destroy function can be called 'asynchronously'
from the POV of the caller.
- someone has to implement this :-(
scheduling the destruction for the next main loop iteration could be
feasible, but it would need to be implemented as you correctly pointed
out.
Well, since I'm the author of one binding, I'll have to do the work
anyway, either in binding or in gtk/glib. So if we find a way which
could be beneficial for more users and avoids code duplication (i.e.
sticking common code somewhere to gobject/gtk), I'm volunteering to do it.
After giving it some more thought, I'd propose solving the problem even
at the gobject level. Warning: a lot of handwaving below :-)
Basically, we define well-known ID for storing MainContext into any
gobject's data slot (g_object_set_data). It is up to implementation
(i.e. GTK/GDK/Clutter) to store an appropriate GMainContext instance to
this slot, marking the object as belonging to this context.
Anyone (incl. bindings implementation) can then query object for the
associated context; bindings can e.g. check whether it is legal to call
this object from currently executed thread.
At the time of destruction, when g_object_unref() decides that it is
time do destroy the object, it checks whether object has associated
context and if yes, it uses g_main_context_invoke() to actually invoke
the destruction path.
Any comments are appreciated.
thanks
pavel
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]