4

Is there any unambiguous documentation stating whether locks (of any kind) are needed or not for idle_add() / timeout_add() and/or the actual callbacks installed by them?

def work(*args):
  # (1) gtk.gdk.threads_enter() #needed?
  self.ui.change_some_label()
  # (2) gtk.gdk.threads_leave() #?

# (3) gtk.gdk.threads_enter()   #?
gobject.idle_add (work)
# (4) gtk.gdk.threads_leave()   #?

def main():
  gtk.gdk.threads_init()
  #...

Are 1+2 and/or 3+4 necessary? For which pygtk versions does this apply? I am targetting separately 2.12 (on an embedded platform) and 2.24 (on the desktop). Threads are due to gstreamer.

For the underlying C functions g_idle_add(), g_timeout_add() I found a gtk-app-devel discussion that states

If you have called gdk_threads_init then idle and timeout handlers will be run without the gdk thread lock and you will have to add gdk_threads_enter/leave calls yourself if you are doing gui stuff.

... though this is from 2004. I find it amazingly hard to locate clear, specific documentation for either GTK+-2 or for PyGTK.

Many answers on SO endorse scheduling GUI work via idle_add, with no locks / critical sections whatsoever (e.g. GUI not updated from another thread when using PyGtk)

Community
  • 1
  • 1
dan3
  • 2,528
  • 22
  • 20

1 Answers1

5

calling g_idle_add() and g_timeout_add() does not need locking: they are thread safe operations, and guarantee that the callback will be called in the GMainContext that is currently spinning the main loop.

the documentation you linked says that the callback will need to acquire the GDK master lock; the callback is invoked by GLib, but the lock is provided by GDK, hence you will need to acquire it explicitly to avoid threads interrupting during the callback emission.

for this reason, the C API provides the gdk_threads_add_idle() and gdk_threads_add_timeout() functions (and their full() variants), which guarantee to invoke your callback with the GDK lock held. PyGTK does not wrap those functions, because it would need to also hold the Python interpreter lock; this means that you will need to remember to call gdk_threads_enter()/gdk_threads_leave() yourself in the callback.

ebassi
  • 8,648
  • 27
  • 29
  • I did not link to documentation, I linked to a mailing list discussion from 2004. The problem, as stated in the original OP, is that I find no unambiguous documentation clearing this up. OTOH there are many answers (even here on SO) that ignore this problem. Now what if one doesn't actually do GUI stuff from multiple threads (but schedules instead everything to the main loop via idle_add()? I believe there's no need for locks/critical sections in that case, even if gtk.gdk.threads_init() was called in the beginning (e.g. by some overzealous framework writer) – dan3 Nov 30 '13 at 09:52
  • if you feel that the documentation is not clear, feel free to file a bug on bugzilla.gnome.org, product gtk+. a patch would also be welcome. as for the other issue: even if the callback you scheduled through idle_add() is executed in the main thread context that is spinning the main loop, you have to mark it as a critical section, to avoid other code taking the lock and calling GDK API from different threads. not that it should ever happen (it's not portable) but lots of people think that taking the GDK lock absolves them of thinking about threading. – ebassi Dec 01 '13 at 10:04
  • The PyGTK docs, http://www.pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--idle-add say **nothing at all** about these issues. Is there any other (semi-)official documentation that addresses these issues clearly (e.g. recent mailing list replies from lead developers)? That was the original question. Nevertheless I will accept your answer and edit the Q to make a note of the problems. – dan3 Dec 01 '13 at 10:19
  • Hey, are you saying that even the idiom < threads_enter() / do GUI work / threads_leave() > from a non-main thread is not portable? Could you point me to some docs? – dan3 Dec 01 '13 at 10:24
  • the official documentation about GDK and threads has all the information: https://developer.gnome.org/gdk/stable/gdk-Threads.html including the non-portability of threads_enter/leave. those functions have also been deprecated in GDK 3.x for that reason. – ebassi Dec 07 '13 at 21:08