Details
Description
I have found that I can reliably make a PySide6 or PyQt6 script hang at exit (I assume since the problem is identical regardless of the python wrpper I am using, this is likely a Qt issue rather than a PySide issue). The minimum needed to reprodce is:
import zmq import PyQt6.QtWidgets app = PyQt6.QtWidgets.QApplication([])
This command should exit immediately, but deadlocks instead. If I attach to the process with GDB, I can get the following backtrace (which is identical regardless of the python wrapper):
#0 __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x374c9ca0) at ./nptl/futex-internal.c:57 #1 __futex_abstimed_wait_common (cancel=true, private=0, abstime=0x0, clockid=0, expected=0, futex_word=0x374c9ca0) at ./nptl/futex-internal.c:87 #2 __GI___futex_abstimed_wait_cancelable64 (futex_word=futex_word@entry=0x374c9ca0, expected=expected@entry=0, clockid=clockid@entry=0, abstime=abstime@entry=0x0, private=private@entry=0) at ./nptl/futex-internal.c:139 #3 0x00007f1c9c5dfa41 in __pthread_cond_wait_common (abstime=0x0, clockid=0, mutex=0x374c9c50, cond=0x374c9c78) at ./nptl/pthread_cond_wait.c:503 #4 ___pthread_cond_wait (cond=0x374c9c78, mutex=0x374c9c50) at ./nptl/pthread_cond_wait.c:627 #5 0x00007f1c999ffa9b in QWaitCondition::wait(QMutex*, QDeadlineTimer) () from /home/luke/miniconda3/envs/teleprox/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6 #6 0x00007f1c999f2cd9 in QThread::wait(QDeadlineTimer) () from /home/luke/miniconda3/envs/teleprox/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6 #7 0x00007f1c99268980 in ?? () from /home/luke/miniconda3/envs/teleprox/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6DBus.so.6 #8 0x00007f1c992687be in ?? () from /home/luke/miniconda3/envs/teleprox/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6DBus.so.6 #9 0x00007f1c9c591495 in __run_exit_handlers (status=0, listp=0x7f1c9c766838 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:113 #10 0x00007f1c9c591610 in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:143 #11 0x00007f1c9c575d97 in __libc_start_call_main (main=main@entry=0x5f90a0 <main>, argc=argc@entry=3, argv=argv@entry=0x7fff61a00198) at ../sysdeps/nptl/libc_start_call_main.h:74 #12 0x00007f1c9c575e40 in __libc_start_main_impl (main=0x5f90a0 <main>, argc=3, argv=0x7fff61a00198, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff61a00188) at ../csu/libc-start.c:392 #13 0x00000000005f84bd in _start ()
So it appears that libQt6DBus has an exit handler that waits on a QThread, which is where we deadlock.
The strange thing about this deadlock is that it only happens when I import zmq, and only if it is imported before Qt. I got as far as finding that when zmq is imported, it selects and imports its cython backend (instead of cffi), and this step is necessary to reproduce the deadlock.