Re: deprecating gdk threads



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]