python - PyQt: segfaults ocasionales cuando se utiliza QApplication.quit
segmentation-fault qtnetwork (1)
He estado trabajando en la captura de segfaults en PyQt / PySide por bastante tiempo. Básicamente, descubrí que la mayoría de los segfaults se pueden atribuir a la naturaleza asincrónica de la biblioteca (así también a nosotros).
Para su ejemplo exacto, ha conectado la señal de timeout
al método de quit
. Lo que podría suceder aquí es que, en caso de un tiempo de espera , se llama a quit
y, cuando el proceso finaliza, de repente todas las referencias a los objetos de la aplicación no son válidas. Pero mientras estaba ocurriendo esta operación, el bucle de eventos de QT aún se estaba ejecutando e intentó acceder a QNetworkAccessManager
para enviar otra señal, pero la referencia a esa ubicación de memoria ya no era válida, por lo que se produjo el error de seg.
Lo que hay que hacer en estos casos es implementar un tipo de método de apagado que asegure que todas las operaciones se detengan, elimine los componentes QT que tiene en uso en el orden correcto y luego, en ese momento, llame a saliendo.
Escribí sobre el tema con más detalle aquí, incluido un método de apagado seguro para aplicaciones QtWebKit
: https://github.com/integricho/path-of-a-pyqter/tree/master/qttut08
Nota: También publiqué esto en la lista de correo de PyQt. Responderé a mi pregunta aquí si surge una buena respuesta.
Tengo problemas con segfaults ocasionales al ejecutar QApplication.quit()
, posiblemente relacionado con libQt5Network.so y / o QtWebkit.
En primer lugar, los 3 sistemas de prueba que estoy usando:
- Arch Linux, PyQt 5.2, Qt 5.2.0, Python 3.3.3
- Ubuntu 13.10, PyQt 5.0.1, Qt 5.0.2, Python 3.3.2 en una VM
- Windows 7, PyQt 5.2, Qt 5.2.0, Python 3.3.3
Estos bloqueos nunca ocurrieron en Arch hasta ahora, con bastante frecuencia en Ubuntu, y de vez en cuando en Windows. (Aunque Windows solo es una suposición, acabo de entender que python.exe ya no funciona ).
Choque original
Primero noté el problema en un gran proyecto (ger), qutebrowser , donde me dio esta stacktrace al escribir :quit
(en Ubuntu):
#0 0xb5c296fc in QMutex::lock() () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#1 0xb3bdd97d in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#2 0xb3bdf0d0 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#3 0xb3bd4418 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#4 0xb3bd8b1e in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#5 0xb5dedf10 in QMetaObject::activate(QObject*, int, int, void**) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#6 0xb5dee48b in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#7 0xb5e59155 in QIODevice::readyRead() ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#8 0xb3bb1f14 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#9 0xb3ba4d99 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#10 0xb3bc03bb in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#11 0xb6483a54 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#12 0xb6488e66 in QApplication::notify(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#13 0xb6bb7e80 in sipQApplication::notify(QObject*, QEvent*) ()
from /usr/lib/python3/dist-packages/PyQt5/QtWidgets.cpython-33m-i386-linux-gnu.so
#14 0xb5dc737a in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#15 0xb5e11f67 in ?? () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#16 0xb5aaf83e in g_main_context_dispatch ()
from /lib/i386-linux-gnu/libglib-2.0.so.0
#17 0xb5aafbe8 in ?? () from /lib/i386-linux-gnu/libglib-2.0.so.0
#18 0xb5aafca8 in g_main_context_iteration ()
from /lib/i386-linux-gnu/libglib-2.0.so.0
#19 0xb5e1138f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#20 0xb5dc5c06 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#21 0xb5dc6014 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#22 0xb5c2b90b in QThread::exec() ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#23 0xb5c2b99b in QThread::run() () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#24 0xb5c2fa08 in ?? () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#25 0xb7774d78 in start_thread (arg=0xa5314b40) at pthread_create.c:311
#26 0xb76ac01e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:131
Descarga de núcleo aquí (15MB, gzip).
Ejemplo mínimo
Luego intenté de nuevo con un ejemplo mínimo que se cierra automáticamente luego de un segundo con un QTimer. Tuve que ejecutarlo en un bucle durante aproximadamente un minuto antes de que sucediera:
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebKitWidgets import QWebView
app = QApplication([])
wv = QWebView()
wv.load(QUrl("http://www.heise.de/"))
t = QTimer()
t.timeout.connect(QApplication.quit)
t.start(1000)
wv.show()
app.exec_()
Esto me dio esta stacktrace muy similar (en Ubuntu):
#0 0xb6cfd8d2 in QCoreApplication::postEvent(QObject*, QEvent*, int) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#1 0xb6d21c83 in QMetaObject::activate(QObject*, int, int, void**) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#2 0xb6d2248b in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#3 0xb3e47935 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#4 0xb3dcf687 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#5 0xb3e483b3 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#6 0xb6d21f10 in QMetaObject::activate(QObject*, int, int, void**) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#7 0xb6d2248b in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#8 0xb3e43fe5 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#9 0xb3d93b1e in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#10 0xb3d94630 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#11 0xb3d9471b in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#12 0xb6d21f10 in QMetaObject::activate(QObject*, int, int, void**) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#13 0xb6d2248b in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#14 0xb6d8d155 in QIODevice::readyRead() ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#15 0xb3e09f14 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#16 0xb3dfcd99 in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#17 0xb3e183bb in ?? () from /usr/lib/i386-linux-gnu/libQt5Network.so.5
#18 0xb492ba54 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#19 0xb4930e66 in QApplication::notify(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Widgets.so.5
#20 0xb505fe80 in sipQApplication::notify(QObject*, QEvent*) ()
from /usr/lib/python3/dist-packages/PyQt5/QtWidgets.cpython-33m-i386-linux-gnu.so
#21 0xb6cfb37a in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#22 0xb6d45f67 in ?? () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#23 0xb65f483e in g_main_context_dispatch ()
from /lib/i386-linux-gnu/libglib-2.0.so.0
#24 0xb65f4be8 in ?? () from /lib/i386-linux-gnu/libglib-2.0.so.0
#25 0xb65f4ca8 in g_main_context_iteration ()
from /lib/i386-linux-gnu/libglib-2.0.so.0
#26 0xb6d4536d in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#27 0xb6cf9c06 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#28 0xb6cfa014 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#29 0xb6b5f90b in QThread::exec() ()
from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#30 0xb6b5f99b in QThread::run() () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#31 0xb6b63a08 in ?? () from /usr/lib/i386-linux-gnu/libQt5Core.so.5
#32 0xb7798d78 in start_thread (arg=0xa7812b40) at pthread_create.c:311
#33 0xb76d001e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:131
Coredump aquí (15MB, gzip).
¿Alguien tiene una idea de lo que está pasando allí? ¿Algo de magia con cosas recogidas de basura al revés? También probé algunas soluciones * para similitudes similares en PyQt4, pero eso tampoco ayudó.
* no se puede encontrar la respuesta de StackOverflow donde se describió, básicamente estableciendo QtWidgets.qApp
en la instancia de QApplication
antes de ejecutar exec_()
, y luego en None
.