[pygtk] Help: Problems with threads and pygtk

Dave Aitel dave at immunitysec.com
Sat Oct 7 05:02:31 WST 2006


Also strace it and see if a buggy linux kernel has you stuck in fmutex()...
-dave

Brian wrote:
> On Wed, 2006-04-10 at 18:58 -0700, David Hirschfield wrote:
>   
>> I've run into a serious problem using threads with pygtk, and I was 
>> hoping someone could give me suggestions.
>>
>> My program is a pygtk interface with a "processor" thread that pulls 
>> from a safely synchronized queue of "requests" and processes each one.
>> Seems simple, and it should have been...but it freezes solid when I run 
>> it. Not a traceback of any kind, the interface and the python 
>> interpreter freeze.
>>
>> I've checked all the things that should be obvious problems:
>>
>> I'm calling:
>> gobject.threads_init()
>> gtk.gdk.threads_init()
>> before gtk.main() is called
>>
>> There is no race condition or blocking call causing everything to get stuck.
>> The thread's only interaction with the rest of the program is via the  
>> Queue.Queue object that keeps the requests to be processed.
>>
>> I've stepped through with pdb and it freezes in a completely innocuous 
>> place in the code (during a loop where I append to a list).
>> The freeze occurs in slightly different places depending on what kind of 
>> debugging code I put in, changes to the order of things, etc...so it 
>> feels like it's some kind of corruption in the interpreter stack.
>>
>> Running the processor thread in a dummy app without the pygtk part never 
>> freezes up.
>> Running the pygtk app with a non-threaded processor, which just 
>> sequentially runs the requests and waits till they complete, never 
>> freezes up.
>>
>> I'm at my wit's end here...I know threads are just asking for trouble, 
>> and usually I avoid them, but circumstances here basically require 
>> threading (all the request time is spent waiting on different kinds of 
>> IO). The setup was so simple, I figured I couldn't possibly run into 
>> trouble...yet here I am.
>>
>> So, anyone have any idea what could cause the entire python interpreter 
>> to freeze solid? Are there known issues with pygtk and python threads 
>> that I should be aware of? Is there some way to verify that the 
>> interpreter stack is not getting screwed up somehow?
>>
>> Any help at this point would really be useful,
>> -Dave
>>
>>     
>
> Try this dispatcher.py module for ideas of where you may be going wrong.
> Use the dispatcher-example for how to use it for inter-thread
> communication.
>
> It has worked very well for our app and eliminated a number of problems.
>
>
>   
> ------------------------------------------------------------------------
>
> #! /usr/bin/env python
> # Fredrik Arnerup <foo at stacken.kth.se>, 2004-12-19
> # Brian Dolbec<dol-sen at telus.net>,2005-3-30
>
> import gobject, os, Queue
> from select import select
>
> class Dispatcher:
>     """Send signals from a thread to another thread through a pipe
>     in a thread-safe manner"""
>     def __init__(self, callback_func, *args, **kwargs):
>         self.callback = callback_func
>         self.callback_args = args
>         self.callback_kwargs = kwargs
>         self.continue_io_watch = True
>         self.queue = Queue.Queue(0) # thread safe queue
>         self.pipe_r, self.pipe_w = os.pipe()
>         gobject.io_add_watch(self.pipe_r, gobject.IO_IN, self.on_data)
>         
>     def __call__(self, *args):
>         """Emit signal from thread"""
>         self.queue.put(args)
>         # write to pipe afterwards
>         os.write(self.pipe_w, "X")
>     
>     def on_data(self, source, cb_condition):
>         if select([self.pipe_r],[],[], 0)[0] and os.read(self.pipe_r,1):
>             if self.callback_args:
>                 args = self.callback_args + self.queue.get()
>                 self.callback(*args, **self.callback_kwargs)
>             else:
>                 self.callback(*self.queue.get(), **self.callback_kwargs)
>         return self.continue_io_watch
>
>   
> ------------------------------------------------------------------------
>
> #! /usr/bin/env python
>
> # Fredrik Arnerup <foo at stacken.kth.se>, 2004-12-19
> # Brian Dolbec<dol-sen at telus.net>,2005-3-30
>
> import pygtk; pygtk.require("2.0")
> import gtk
> from time import sleep
> import threading, gobject, os
> from dispatcher import Dispatcher
>
> # ####################################
> # dispatcher
> # example code:
> #
> #
> # ####################################
>
> class Thread(threading.Thread):
>
>     def __init__(self, dispatcher, thread_num, length):
>         threading.Thread.__init__(self)
>         self.setDaemon(1)  # quit even if this thread is still running
>         self.dispatcher = dispatcher
>         self.thread_num = thread_num
>         self.sleep_length = length
>
>     def run(self):
>         done = False
>         print("thread_num = %s; process id = %d ****************" %(self.thread_num,os.getpid()))
>         pid_func(self.thread_num)
>         for num in range(250):
>             #print self.thread_num, " num = ",num
>             sleep(self.sleep_length)
>             data = [ self.thread_num, (": time is slipping away: %d\n" %num), num, done]
>             self.dispatcher(data) # signal main thread
>         done = True
>         data = [ self.thread_num, (": Time slipped away: I'm done"), num, done]
>         self.dispatcher(data) # signal main thread
>
>
> def pid_func(threadnum):
>     print("pid_func: called from thread_num = %s; process id = %d ****************" %(threadnum,os.getpid()))
>
> def message_fun(buffer, message):
>     #print ("got a message : %s" %(message[0] + str(message[1])))
>     if message[3]:
>         thread_finished[message[0]] = True
>         buffer.insert(buffer.get_end_iter(), message[0] + str(message[1]) + "\n\n")
>     else:
>         #message2 = ("%d x 3 = %d\n" %(message[2],message[2]*3))
>         buffer.insert(buffer.get_end_iter(), message[0] + str(message[1])) # + message2)
>     return
>
> def timerfunc():
>     if (not thread_finished["thread1"]) or (not thread_finished["thread2"]) \
>                 or (not thread_finished["thread3"]) or (not thread_finished["thread4"]):
>         pbar.pulse()
>         #print 'Plusing ProgressBar, since a thread is not finished'
>         return True
>     else:
>         pbar.set_fraction(0)
>         pbar.set_text("Done")
>         return False
>
> def on_window_map_event(event, param):
>     print 'Window mapped'
>     thread1 = Thread(Dispatcher(message_fun, buffer), "thread1", 0.9)
>     thread2 = Thread(Dispatcher(message_fun, buffer), "thread2", 0.9)
>     thread3 = Thread(Dispatcher(message_fun, buffer), "thread3", 0.9)
>     thread4 = Thread(Dispatcher(message_fun, buffer), "thread4", 0.5)
>     gobject.timeout_add(100, timerfunc)
>     thread1.start()
>     thread2.start()
>     thread3.start()
>     thread4.start()
>
>
> if __name__ == "__main__":
>        
>     gtk.threads_init()
>     window = gtk.Window(gtk.WINDOW_TOPLEVEL)
>     textview = gtk.TextView()
>     buffer = textview.get_buffer()
>     sw = gtk.ScrolledWindow()
>     sw.add(textview)
>     pbar = gtk.ProgressBar()
>     vbox = gtk.VBox()
>     vbox.pack_start(sw)
>     vbox.pack_start(pbar, False)
>     window.add(vbox)
>     #gui_dispatcher = Dispatcher(message_fun, buffer)
>     window.connect('map_event', on_window_map_event)
>     window.connect("destroy", gtk.main_quit)
>     window.resize(400, 600)
>     window.show_all()
>     thread_finished = {"thread1":False, "thread2":False, "thread3":False, "thread4":False}
>     gtk.threads_enter()
>     gtk.main()
>     gtk.threads_leave()
>   
> ------------------------------------------------------------------------
>
> _______________________________________________
> pygtk mailing list   pygtk at daa.com.au
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
>   



More information about the pygtk mailing list