| FAQ Index - Search - Recent Changes - Everything - Add 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)
