Charles Wei
Charles Wei

Reputation: 571

QT raise segmentation fault (core dumped) when QApplication exit

Description

I'm using Python & QT to do some experiment.
I use Python to create a GUI application by calling QT's functions.
But, when I close a simple GUI application, it raised a segmentation fault (core dumped).

Sample code

C++:

// This is python C++ extension
#ifndef QT_API
#define QT_API

#include "Python.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QPushButton>

#endif

static QSharedPointer<QApplication> pglobal_app;

// api: init
static PyObject* init_wrapper(PyObject* self, PyObject* args)
{
    int argc = 1;
    char *argv[] = {""};
    pglobal_app = QSharedPointer<QApplication>(new QApplication(argc, argv));
    Py_RETURN_NONE;
}

// api: launch
static PyObject* launch_wrapper(PyObject* self, PyObject* args)
{
    QPushButton button("Exit");
    QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
    button.show();
    pglobal_app->exec();
    Py_RETURN_NONE;
}

// methods table
static PyMethodDef qt_api_methods[] = {
    { "init", init_wrapper, METH_VARARGS, "init qt_api"},
    { "launch", launch_wrapper, METH_VARARGS, "launch qt_api"},
    { NULL, NULL, 0, NULL }
};

// init qt_api module
PyMODINIT_FUNC initqt_api(void)
{
    (void) Py_InitModule("qt_api", qt_api_methods);
}

Python:

#!/usr/bin/env python
import sys, os
sys.path.append("build/lib.linux-x86_64-2.7")
import qt_api

qt_api.init()
qt_api.launch()
print "BYE"

Symptoms

When I click the 'Exit' button, the program printed BYE and then raise a segmentation fault.

GDB glance

I use GDB to check what's going on. Below is the output.

#0  0x00007fd9872ed869 in QWindow::destroy() () from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
#1  0x00007fd988b8e1ad in QWidgetPrivate::deleteTLSysExtra() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#2  0x00007fd988b6278d in QWidgetPrivate::deleteExtra() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#3  0x00007fd988b68cf8 in QWidgetPrivate::~QWidgetPrivate() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#4  0x00007fd988b68f39 in QWidgetPrivate::~QWidgetPrivate() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#5  0x00007fd9894be666 in QObject::~QObject() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#6  0x00007fd988b71c7a in QWidget::~QWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#7  0x00007fd988ea5e0e in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#8  0x00007fd988b8d48d in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#9  0x00007fd9894be666 in QObject::~QObject() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007fd988b71c7a in QWidget::~QWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#11 0x00007fd988b8c299 in QDesktopWidget::~QDesktopWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#12 0x00007fd988b38a5c in QApplication::~QApplication() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#13 0x00007fd988b38e69 in QApplication::~QApplication() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#14 0x00007fd9898cf77e in destroy (this=0x233d220)
    at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:151
#15 deref (d=0x233d220) at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:469
#16 deref (this=<optimized out>) at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:464
#17 QSharedPointer<QApplication>::~QSharedPointer (this=<optimized out>, __in_chrg=<optimized out>)
    at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:305
#18 0x00007fd98ab15259 in __run_exit_handlers (status=0, listp=0x7fd98ae986c8 <__exit_funcs>, 
    run_list_atexit=run_list_atexit@entry=true) at exit.c:82
#19 0x00007fd98ab152a5 in __GI_exit (status=<optimized out>) at exit.c:104

Question

  1. Why this happening and how to avoid this issue?
  2. If I want to invoke QApplication::quit() from Python in another thread, what should I do?

Thank you so much.

Upvotes: 1

Views: 3049

Answers (1)

tux3
tux3

Reputation: 7320

Let's look at your launch_wrapper:

// api: launch
static PyObject* launch_wrapper(PyObject* self, PyObject* args)
{
    QPushButton button("Exit");
    QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
    button.show();
    pglobal_app->exec();
    Py_RETURN_NONE;
}

Here you create a QPushButton, connect it, show it, and enter the main Qt event loop. But you create it on the stack, so when you click that button, the main event loop returns, your launch_wrapper returns, and the QPushButton is destroyed on the way out.

Now your pglobal_app is static, so it's destroyed after all used code has returned, so long after your QPushButton is gone.

But internally Qt keeps a hierarchy of parents and childs for pretty much all of its objects, and the parent owns the childs. So when the QApplication is destroyed, it also destroys all of its childs, including your QPushButton.

So you have an object being destroyed twice. That's a double-free and undefined behavior. Hence the crash.

To fix this, create all your GUI elements on the heap (i.e. QPushButton* button = new QPushButton("Exit"); and let Qt clean it up with the parent/child hierarchy.

Upvotes: 2

Related Questions