[PyQt] Signals arriving after proxy slots deleted

Andrew Suffield asuffield at suffields.me.uk
Wed Apr 11 13:48:15 BST 2012

I've been having grief with a class of problems that looks like this
valgrind error:

==28760== Invalid write of size 4
==28760==    at 0x6C26DED: PyQtProxy::unislot(void**) (in /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so)
==28760==    by 0x6C26EC1: PyQtProxy::qt_metacall(QMetaObject::Call, int, void**) (in /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so)
==28760==    by 0x7083DF8: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.0)
==28760==    by 0xCD7D945: QNetworkReplyImplPrivate::finished() (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.0)
==28760==    by 0xCDF3C64: QNetworkAccessHttpBackend::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4.8.0)
==28760==    by 0x7088DB5: QObject::event(QEvent*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.0)
==28760==    by 0x8F14EC3: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.0)
==28760==    by 0x8F19D42: QApplication::notify(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.0)
==28760==    by 0x8900CB8: sipQApplication::notify(QObject*, QEvent*) (in /usr/lib/python2.7/dist-packages/PyQt4/QtGui.so)
==28760==    by 0x706F80B: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.0)
==28760==    by 0x70735D9: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.0)
==28760==    by 0x709E962: postEventSourceDispatch(_GSource*, int (*)(void*), void*) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.0)
==28760==  Address 0xed62f14 is 20 bytes inside a block of size 120 free'd
==28760==    at 0x402773C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28760==    by 0x6C276E8: PyQtProxy::deleteSlotProxies(void*, char const*) (in /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so)
==28760==    by 0x6C24B20: pyqtBoundSignal_disconnect (in /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so)
==28760==    by 0x4B9588: PyEval_EvalFrameEx (ceval.c:4021)

What's happened here is that a cross-thread connection from a
QNetworkReply.finished signal to some python code has been
.disconnect()ed, and then an event arrives from the thread where the
network stuff is happening.

Unfortunately, it seems that .disconnect() in pyqt is also a delete of
the receiving proxy object, which leads to memory corruption when the
signal magic happens.

I'm working around the issue in the short term by not using
disconnect, and making sure my QObjects don't get gced before the
event loop's had time to drain (by keeping references to them lying
around), but this is rather suboptimal in terms of code noise. What
I'd like is a safe way to use disconnect, but I don't see how - is
there actually a pyqt bug in here?

More information about the PyQt mailing list