FAQ Index - Search - Recent Changes - Everything - Add entry

<< Previous Entry | FAQ Entry 20.15 | Next Entry >>

20.15. I'm using threads and timeout or idle or input handlers and the app freezes. What's wrong?

From gtk.gdk.theads_init reference: Signal handlers are automatically invoked within a gdk_threads_enter() and gdk_threads_leave() function pair by GTK so the gtk.gdk.threads_enter() and gtk.gdk.threads_leave() functions should not be called within a Python signal handler or the application will deadlock. However, idle, timeout and input handlers are executed outside the GDK global lock (GGL) so these should use the gtk.gdk.threads_enter() and gtk.gdk.threads_leave() functions if PyGTK methods or functions are called.

An example to illustrate better the proposed solution follows:

    def run(self, called_by_timeout=False):
        if called_by_timeout:
           # if it was called outside of GDK global lock (eg. here a timeout) make sure we threas_enter
           gtk.gdk.threads_enter()

        ...code-here...

        if called_by_timeout:
           gtk.gdk.threads_leave()
more: [www.pygtk.org]

info provided by Víctor M. Hernández Rocamora

If your code contains thread_enter and thread_leave, and on win32 platform it working fine, but on linux it hangs, that because mutexes on win32 are recursive (you can obtain lock any times in thread), while on linux they not. So, I have found easy way to work with that:

 class CGtkLocker:
        def __init__(self):
                self.lock = threading.Lock()
                self.thread = None
                self.locked = 0

        def __enter__(self):
                with self.lock: DoLock = (thread.get_ident()!=self.thread)
                if DoLock:
                        gtk.gdk.threads_enter()
                        with self.lock: self.thread = thread.get_ident()
                self.locked += 1
                return None

        def __exit__(self, exc_type, exc_value, traceback):
                with self.lock:
                        self.locked -= 1
                        if self.thread!=thread.get_ident():
                                print "!ERROR! Thread free not locked lock!"
                                sys.exit(0)
                        else:
                                if self.locked == 0:
                                        self.thread = None
                                        gtk.gdk.threads_leave()
                return None
 GtkLocker = CGtkLocker()

 def GtkLocked(f):
        @wraps(f)
        def wrapper(*args, **kwds):
                with GtkLocker.lock:
                        if GtkLocker.thread == None or GtkLocker.thread==thread.get_ident():
                                GtkLocker.thread = thread.get_ident()
                                GtkLocker.locked += 1
                                WeHold = True
                        else:
                                print "!ERROR! GtkLocked for non-owned thread!"
                                WeHold = False
                try:
                        ret = f(*args, **kwds)
                        return ret
                finally:
                        if WeHold:
                                with GtkLocker.lock:
                                        GtkLocker.locked -= 1
                                        if GtkLocker.locked == 0: GtkLocker.thread = None
        return wrapper
that code put into global GUI unit (for example, called "gui"), and use
 with gui.GtkLocker:
   code-that-use-gtk()
where you need to get lock for GTK -- that works, that block can call GtkLocker any times it need without deadlock. But! Since all event handlers called with already gtk locked, you must bind wrapped handlers, so you must call instead of
  somewidget.connect('event-name', handler)
that way:
  somewidget.connect('event-name', gui.GtkLocked(handler))
that makes handler called in already-locked context, so your "with gui.GtkLocker"'s will still works without deadlock.

Info provided by Anton Fedorov (datacompboy)

PyGTK FAQ Wizard | PyGTK Homepage | Feedback to faq at pygtk.org