enthought / envisage

Envisage is a Python-based framework for building applications whose functionalities can be extended by adding "plug-ins".
http://docs.enthought.com/envisage/
Other
82 stars 26 forks source link

Segfault on Linux at process exit under PySide 6.3.1 #476

Closed mdickinson closed 1 year ago

mdickinson commented 2 years ago

EDIT 2023-03-10: Upstream issue here - https://bugreports.qt.io/browse/PYSIDE-2254


While trying to add support for PySide 6, we're seeing a segfault at process exit time when running the test suite, both on CI and locally.

We've also seen similar segfaults on downstream applications using Envisage.

For me, on a Linux VM, the segfault is reliably reproducible from a given piece of code, but also very fragile: addition or removal of unrelated code lines or unrelated imports can cause the segfault to disappear or reappear. As such, it's very hard to reduce to a minimal failing example. It's almost certainly not Envisage that's the root cause of the problem, and this seems likely to be a bug in PySide 6.

Here's a minimal-ish reproducer script on Ubuntu Linux 20.04.1:

import gc; gc.disable()

import os
import shutil
import tempfile
import unittest

import pkg_resources

from envisage.ui.tasks.api import TasksApplication

class TestTasksApplication(unittest.TestCase):
    def random(self):
        pass

    def test_layout_load(self):
        app = TasksApplication()
        app.on_trait_change(app.exit, "application_initialized")
        app.run()

if __name__ == "__main__":
    unittest.main(exit=False)

Note that the os, shutil, tempfile and pkg_resources imports are completely unused. Nevertheless, I can reproduce the segfault reliably with the above script, but if I remove those unused imports, the script runs without error.

Detailed instructions to reproduce

On Ubuntu 20.04.1 / x86_64, running under VirtualBox on a macOS / Intel host:

The following sequence of instructions reproduces the segfault for me. Replacing the last line with python -m unittest also produces the same crash at the end of the test run.

$ python3 -m venv --clear ~/.venvs/envisage && source ~/.venvs/envisage/bin/activate
$ python -m pip install --upgrade pip setuptools wheel
$ python -m pip install PySide6
$ python -m pip install git+https://github.com/enthought/pyface.git
$ python -m pip install -e .
$ export ETS_TOOLKIT=qt
$ python crasher.py

Here crasher.py is the above script.

Stack trace

Here's the stack trace, obtained by setting ulimit -c unlimited and by killing the Ubuntu crash reporter which will otherwise intercept the core dump (sudo service apport stop):

Core was generated by `python crasher.py'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fb4f06fd9a2 in Shiboken::BindingManager::retrieveWrapper(void const*) () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/shiboken6/libshiboken6.abi3.so.6.3
[Current thread is 1 (Thread 0x7fb4f2123740 (LWP 14184))]
(gdb) bt
#0  0x00007fb4f06fd9a2 in Shiboken::BindingManager::retrieveWrapper(void const*) ()
   from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/shiboken6/libshiboken6.abi3.so.6.3
#1  0x00007fb4eff22185 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/libpyside6.abi3.so.6.3
#2  0x00007fb4eff2285a in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/libpyside6.abi3.so.6.3
#3  0x00007fb4efa3455e in QVariant::~QVariant() () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#4  0x00007fb4efa0f40c in QObjectPrivate::~QObjectPrivate() ()
   from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#5  0x00007fb4efad4379 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#6  0x00007fb4efad4033 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#7  0x00007fb4efad4187 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#8  0x00007fb4efad43b9 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#9  0x00007fb4efa0f2e6 in QObjectPrivate::~QObjectPrivate() ()
   from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#10 0x00007fb4efa1d5ac in QObject::~QObject() () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#11 0x00007fb4d9cf9f37 in ?? ()
   from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/plugins/xcbglintegrations/libqxcb-glx-integration.so
#12 0x00007fb4efc5e032 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#13 0x00007fb4efc606d3 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#14 0x00007fb4efc5e299 in ?? () from /home/mdickinson/.venvs/envisage/lib/python3.8/site-packages/PySide6/Qt/lib/libQt6Core.so.6
#15 0x00007fb4f23358a7 in __run_exit_handlers (status=0, listp=0x7fb4f24db718 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, 
    run_dtors=run_dtors@entry=true) at exit.c:108
#16 0x00007fb4f2335a60 in __GI_exit (status=<optimised out>) at exit.c:139
#17 0x00007fb4f231308a in __libc_start_main (main=0x4eead0 <main>, argc=2, argv=0x7ffcee6c57e8, init=<optimised out>, fini=<optimised out>, 
    rtld_fini=<optimised out>, stack_end=0x7ffcee6c57d8) at ../csu/libc-start.c:342
#18 0x00000000005fa5ce in _start ()
mdickinson commented 1 year ago

There's a PySide 6.3.2 release; we should test with that.

mdickinson commented 1 year ago

This issue should be re-evaluated once the IPython components have been removed.

mdickinson commented 1 year ago

I'm able to reproduce this end-of-process segfault somewhat reliably with:

Here's a single-script crasher:

import unittest

from pyface.gui import GUI
from pyface.tasks.api import TaskWindow
from traits.api import Instance

from envisage.api import Application

class MyTasksApplication(Application):
    window = Instance(TaskWindow)

    def run(self):
        gui = GUI()
        self.start()
        window = TaskWindow()
        window.open()
        self.window = window
        gui.invoke_later(self.exit)
        gui.start_event_loop()
        self.stop()

    def exit(self):
        window = self.window
        self.window = None
        window.destroy()
        window.closed = True

def main():
    app = MyTasksApplication()
    app.run()

if __name__ == "__main__":
    main()

It's still a bit Heisenbuggy: the segfault seems more likely to occur in the presence of the (unused) unittest import, for example.

mdickinson commented 1 year ago

I've now reduced to an example that doesn't involve Envisage at all; opening a Pyface issue shortly.

mdickinson commented 1 year ago

Opened enthought/pyface#1211.

mdickinson commented 1 year ago

The root cause appears to be this: https://bugreports.qt.io/browse/PYSIDE-2254

I'll edit the main issue description so that this is easier to find.

mdickinson commented 1 year ago

This is now fixed upstream: it's been both worked around in Pyface and fixed for real in PySide6.

Leaving open here as a reminder to remove the skips on tests as soon as we have either a new release of Pyface or a new release of PySide6.

mdickinson commented 1 year ago

Removing from the 7.0 release milestone. We're dependent on upstream releases (Pyface and/or PySide6), and neither of those should block the Envisage 7.0 release.

mdickinson commented 1 year ago

Leaving open here as a reminder [...]

On second thoughts, for cleanliness, I'll close this issue and open a new one for removing the test skips.