[PyQt] Segfault when QObject is 'shared' between threads

Ales Erjavec ales.erjavec324 at gmail.com
Fri Oct 26 11:03:09 BST 2018


Hi,

The following test fails (eventually) with a segmentation fault.
```
from concurrent.futures import ThreadPoolExecutor

from PyQt5.QtCore import Qt, QObject, QCoreApplication
from PyQt5.QtCore import QMetaObject


class TestObject(QObject):
    def event(self, event):
        return super().event(event)

executor = ThreadPoolExecutor()
app = QCoreApplication([])

def test(parent=None):
    obj = TestObject(parent)

    def run():
        nonlocal obj  # capture obj in the run's closure
        pass

    f = executor.submit(run)

    # run the event loop until future's completion
    @f.add_done_callback
    def quit(f):
        QMetaObject.invokeMethod(app, "quit", Qt.QueuedConnection)
    app.exec()

    obj.deleteLater()  # delete the object from the event queue

iter_n = 0

while True:
    test(parent=app)
    iter_n += 1
    if iter_n % 10 == 0:
        print("iter:", iter_n)

```

The test object is created in the main thread and is parented. It is
passed in a closure to a thread.
The run function does not even touch it. The only interaction that
should happen is an eventual
Py_DECREF once the worker thread is done. Note that this can happen
after the obj.deleteLater()
is called on the main thread and the test function exists.

I suspect that the problem is double deletion. The event loop picks up
the DeferredDelete event and
starts to delete the object (without the GIL held), at the same time
the closure reference is decrefed
while the state (parent ownership) is inconsistent.

Note that `event` method on TestObject must be overridden in python
for the test to fail.

Tested on:
* macOS 10.11.6, Python 3.5 PyQt 5.9.2, and Python 3.6 PyQt 5.11.3
* KDE Neon (Ubuntu 16.04) Python 3.5 PyQt 5.11.2


More information about the PyQt mailing list