[PyQt] subtle bug in PyQt in combination with Python garbage collector

Phil Thompson phil at riverbankcomputing.com
Fri Aug 26 10:44:34 BST 2011


On Thu, 25 Aug 2011 18:08:17 +0100, Phil Thompson
<phil at riverbankcomputing.com> wrote:
> On Sat, 13 Aug 2011 10:05:14 -0600, Kovid Goyal <kovid at kovidgoyal.net>
> wrote:
>> This bug has been present for a very long time. As a workaround in my
>> projects, 
>> I disable the automatic garbage collector and run garbage collection
>> manually
>> in the GUI thread via QTimer. Here's the code to do that:
>> 
>> class GarbageCollector(QObject):
>> 
>>     '''
>>     Disable automatic garbage collection and instead collect manually
>>     every INTERVAL milliseconds.
>> 
>>     This is done to ensure that garbage collection only happens in the
> GUI
>>     thread, as otherwise Qt can crash.
>>     '''
>> 
>>     INTERVAL = 5000
>> 
>>     def __init__(self, parent, debug=False):
>>         QObject.__init__(self, parent)
>>         self.debug = debug
>> 
>>         self.timer = QTimer(self)
>>         self.timer.timeout.connect(self.check)
>> 
>>         self.threshold = gc.get_threshold()
>>         gc.disable()
>>         self.timer.start(self.INTERVAL)
>>         #gc.set_debug(gc.DEBUG_SAVEALL)
>> 
>>     def check(self):
>>         #return self.debug_cycles()
>>         l0, l1, l2 = gc.get_count()
>>         if self.debug:
>>             print ('gc_check called:', l0, l1, l2)
>>         if l0 > self.threshold[0]:
>>             num = gc.collect(0)
>>             if self.debug:
>>                 print ('collecting gen 0, found:', num, 'unreachable')
>>             if l1 > self.threshold[1]:
>>                 num = gc.collect(1)
>>                 if self.debug:
>>                     print ('collecting gen 1, found:', num,
> 'unreachable')
>>                 if l2 > self.threshold[2]:
>>                     num = gc.collect(2)
>>                     if self.debug:
>>                         print ('collecting gen 2, found:', num,
>>                         'unreachable')
>> 
>>     def debug_cycles(self):
>>         gc.collect()
>>         for obj in gc.garbage:
>>             print (obj, repr(obj), type(obj))
>> 
>> Kovid.
> 
> Thanks for the insight.
> 
> Two solutions spring to mind...
> 
> 1. Implement the above so that it is created when QThread.start() is
> called for the first time.
> 
> 2. Change the SIP generated dtor code so that it calls deleteLater() if
> the object's thread is not the current one...
> 
>     if (QThread::currentThread() == obj->thread())
>         delete obj;
>     else
>         obj->deleteLater();
> 
> I'd prefer the latter (assuming it works).
> 
> Thoughts?

Tonight's SIP snapshot will implement the second solution. Let me know the
results.

Phil


More information about the PyQt mailing list