[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