Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit()



  • I encountered a strange problem. Here is code:
    In main.qml there is just a button, which emits signal buttonClicked() when it was pressed:

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        Foo fooObject;
    
        QObject* rootObject = engine.rootObjects().first();
        QObject::connect(rootObject, SIGNAL(buttonClicked()), 
                         &fooObject, SLOT(start_process()));
    
        return app.exec();
    }
    

    In Foo header:

    QProcess *process;
    

    And here is Foo source:

    void Foo::start_process()
    {
        process = new QProcess;
        QObject::connect(process, SIGNAL(readyRead()), 
                         this,    SLOT(wait_test()));
        QObject::connect(process, SIGNAL(finished(int)), 
                         process, SLOT(deleteLater()));
        process->start("uname -a"); //just for exaple
        qDebug() << "Process started";
    }
    
    void Foo::wait_test()
    {
        QEventLoop localLoop;
        QTimer::singleShot(5000, &localLoop, SLOT(quit()));
        localLoop.exec();
        qDebug() << "Time is up";
    }
    

    After wait_test() completed SIGSEGV occurs in QQuiApplication::exec() .
    I am sure that it is my mistake, but i don't know where...
    I'm using Qt 5.7 under Ubuntu with g++ compiler



  • This is likely the reason for the crash:

        QEventLoop localLoop;
        QTimer::singleShot(5000, &localLoop, SLOT(quit()));
    

    Class 'localLoop' is not on the heap so it would be destroyed when this member function wait_test() returns. After 5 seconds the timer tries to access this class which no longer exists and you have a segfault.

    I am not entirely sure what your goal is with this. If you want handle a case for something that doesn't respond (therefore finished() is never called) maybe have the timer call finished() with an -1 for an integer value (something you can use to detect an error).


  • Qt Champions 2016

    I was wondering that too
    but wont
    localLoop.exec(); // block
    so it wont run out of scope?



  • @MatrixSan What's the backtrace from the crash?



  • I wrote the same application on Windows Qt 5.7.1 and it is working as expected. Do you see the "Time is up" message on "Application Output"? Can you post your "main.qml" file? Maybe it is related to it.



  • This program runs in QEventLoop::exec() 5 seconds and after that, crash in QGuiApplication::exec(). It crashes frequently but not always. And I noticed the dependence of the frequency of the program falls on the delay time. The more delay the more likely program crashes



  • @MatrixSan Throw it in gdb and give us a backtrace it will probably be pretty obvious from that what the issue is.



  • @ambershark Is it it?
    Thread 1 "QtBug" received signal SIGSEGV, Segmentation fault.
    0x00007ffff6bb3d56 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    (gdb) bt full
    #0 0x00007ffff6bb3d56 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #1 0x00007ffff6c25d11 in QIODevice::channelReadyRead(int) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #2 0x00007ffff6ad1506 in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #3 0x00007ffff6ad18b0 in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #4 0x00007ffff6bb4056 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #5 0x00007ffff6c29bfe in QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #6 0x00007ffff6bc0f2b in QSocketNotifier::event(QEvent*) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #7 0x00007ffff6b8c98a in QCoreApplication::notify(QObject*, QEvent*) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    ---Type <return> to continue, or q <return> to quit---
    No symbol table info available.
    #8 0x00007ffff6b8cae0 in QCoreApplication::notifyInternal2(QObject*, QEvent*)
    () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #9 0x00007ffff6bda53e in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #10 0x00007ffff27a11a7 in g_main_context_dispatch ()
    from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #11 0x00007ffff27a1400 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #12 0x00007ffff27a14ac in g_main_context_iteration ()
    from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #13 0x00007ffff6bda05c in QEventDispatcherGlib::processEvents(QFlagsQEventLoop::ProcessEventsFlag) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #14 0x00007ffff6b8adca in QEventLoop::exec(QFlagsQEventLoop::ProcessEventsFlag) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #15 0x00007ffff6b92bad in QCoreApplication::exec() ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    ---Type <return> to continue, or q <return> to quit---
    No symbol table info available.
    #16 0x0000000000401e5f in main (argc=1, argv=0x7fffffffde58)
    at ../QtBug/main.cpp:16
    app = <incomplete type>
    engine = <incomplete type>
    fooObject = {<QObject> = {<No data fields>},
    static staticMetaObject = {d = {
    superdata = 0x7ffff70251c0 QObject::staticMetaObject,
    stringdata = 0x4033e0 <qt_meta_stringdata_Foo>,
    data = 0x403460 <qt_meta_data_Foo>,
    static_metacall = 0x402c7a <Foo::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0, extradata = 0x0}},
    process = 0x812fc0}
    rootObject = 0x6b44a0
    (gdb)



  • @MatrixSan Thanks, yea from that it looks like QEventLoop is out of scope and cleaned up. Try allocating it on the heap (without worrying bout clean up for now) just to test and see if that goes away. It should.

    Basically that backtrace is saying it is trying to call activate on a pointer that is invalid (dangling). So the memory was already cleaned up. This is why you rarely see Qt use connect on anything on the stack. It is almost guaranteed to crash at some point.

    Edit: Here is the code that should work:

    void Foo::wait_test()
    {
        QEventLoop *localLoop = new QEventLoop();
        QTimer::singleShot(5000, localLoop, SLOT(quit()));
        localLoop->exec();
        qDebug() << "Time is up";
        localLoop->deleteLater();
    }
    


  • @ambershark thank you for your reply, but program crashes anyway...
    Thread 1 "QtBug" received signal SIGSEGV, Segmentation fault.
    0x00007ffff6bb3d56 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    (gdb) bt full
    #0 0x00007ffff6bb3d56 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #1 0x00007ffff6c25d11 in QIODevice::channelReadyRead(int) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #2 0x00007ffff6ad1506 in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #3 0x00007ffff6ad18b0 in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #4 0x00007ffff6bb4056 in QMetaObject::activate(QObject*, int, int, void**) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #5 0x00007ffff6c29bfe in QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #6 0x00007ffff6bc0f2b in QSocketNotifier::event(QEvent*) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #7 0x00007ffff6b8c98a in QCoreApplication::notify(QObject*, QEvent*) ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    ---Type <return> to continue, or q <return> to quit---
    No symbol table info available.
    #8 0x00007ffff6b8cae0 in QCoreApplication::notifyInternal2(QObject*, QEvent*)
    () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #9 0x00007ffff6bda53e in ?? ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #10 0x00007ffff27a11a7 in g_main_context_dispatch ()
    from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #11 0x00007ffff27a1400 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #12 0x00007ffff27a14ac in g_main_context_iteration ()
    from /lib/x86_64-linux-gnu/libglib-2.0.so.0
    No symbol table info available.
    #13 0x00007ffff6bda047 in QEventDispatcherGlib::processEvents(QFlagsQEventLoop::ProcessEventsFlag) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #14 0x00007ffff6b8adca in QEventLoop::exec(QFlagsQEventLoop::ProcessEventsFlag) () from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #15 0x00007ffff6b92bad in QCoreApplication::exec() ()
    from /home/san/Qt/5.7/gcc_64/lib/libQt5Core.so.5
    ---Type <return> to continue, or q <return> to quit---
    No symbol table info available.
    #16 0x0000000000401e5f in main (argc=1, argv=0x7fffffffde58)
    at ../QtBug/main.cpp:16
    app = <incomplete type>
    engine = <incomplete type>
    fooObject = {<QObject> = {<No data fields>},
    static staticMetaObject = {d = {
    superdata = 0x7ffff70251c0 QObject::staticMetaObject,
    stringdata = 0x4033e0 <qt_meta_stringdata_Foo>,
    data = 0x403460 <qt_meta_data_Foo>,
    static_metacall = 0x402c90 <Foo::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0, extradata = 0x0}},
    process = 0x94ab00}
    rootObject = 0x6b4530



  • @MatrixSan Oops I didn't look far enough in the bt.. So the problem here is actually that readyRead is called after the QProcess has been cleaned up. Just to confirm try commenting out your line where you connect finish to deleteLater.

    If it still crashes I'll look in more detail however I'm 95% sure that is the crash. For reals this time. ;)

    Edit: Oh and if it really is the crash as I suspect then to fix it you can deal with all the stuff pending the read on the socket after the process exists and then delete your QProcess, or you can disconnect the readyRead signal before deleting QProcess. Or you could even turn off the IO from the QProcess if you don't really need it... or don't connect readyRead in the first place if you don't need it. Any of those should fix it permanently without disabling your deleteLater.



  • @ambershark yeah that works =) thank you very much! But I still do not understand why the program falls only when I'm using QEventLoop ..



  • @MatrixSan because readyRead has that event loop that doesn't quit for 5 seconds. So the QProcess is deleted before the previous readyRead exited. So the next readyRead tries to be called and the process is gone therefore segfault. :) You could simulate that same behavior and crash with a simple sleep.



  • @ambershark Now everything is clear. Thank you very much for a detailed description :)


  • Qt Champions 2016

    @ambershark said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    You could simulate that same behavior and crash with a simple sleep.

    You can't because sleep will block event processing.

    @MatrixSan
    As already mentioned using local event loops is a somewhat dubious decision. I suggest you add a timeout flag and set it in the handler of the timer signal, if you need to know when an operation has timed out. The problem is that the deferred deletion event is processed in the main event loop while you're still waiting for localLoop.



  • @kshegunov I'm using local event loop because in project where I ran into this problem with "segfault" sometimes there is need to show MessageDialog with warning message and i want to block code execution until user will press "ok" button or close the window and event loop just waits for signal from it. Like this:

    mainViewEngine->load(QUrl("qrc:/src/core/interfaces/WarningMessage.qml"));
    QEventLoop localLoop;
    QObject::connect(moduleObject, SIGNAL(closeSignal()), &localLoop, SLOT(quit()));
    localLoop.exec();
    

    Is it right way?


  • Qt Champions 2016

    @MatrixSan said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    I'm using local event loop because in project where I ran into this problem with "segfault" sometimes there is need to show warning message and i want to block code execution until user will press "ok" button or close the window and event loop just waits for signal from it.

    Then you need to call the deleteLater from the slot where you handle the timeout. Otherwise your code would be equivalent to calling delete this before you've returned from the slot, which is a bad, bad, bad idea.The simplest way is to make the process a stack variable and leave C++ to clean it up for you:

    class Foo : public QObject
    {
        Q_OBJECT
    
    private:
        QProcess process;
    
    public:
        void Foo::start_process()
        {
            // process = new QProcess; <- No need.
            QObject::connect(&process, SIGNAL(readyRead()), this, SLOT(wait_test()));
            process.start("uname -a"); //just for exaple
            qDebug() << "Process started";
        }
    
        void Foo::wait_test()
        {
            QEventLoop localLoop;
            QTimer::singleShot(5000, &localLoop, SLOT(quit()));
            localLoop.exec();
    
            qDebug() << "Time is up";
        }
    }
    


  • @kshegunov Thank you! Now all works fine.



  • @MatrixSan said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    @kshegunov I'm using local event loop because in project where I ran into this problem with "segfault" sometimes there is need to show MessageDialog with warning message and i want to block code execution until user will press "ok" button or close the window and event loop just waits for signal from it. Like this:

    mainViewEngine->load(QUrl("qrc:/src/core/interfaces/WarningMessage.qml"));
    QEventLoop localLoop;
    QObject::connect(moduleObject, SIGNAL(closeSignal()), &localLoop, SLOT(quit()));
    localLoop.exec();
    

    Is it right way?

    But you should have a main event loop and you don't really need the local one since you have events to deal with the rest of what is going on. You just connect signals/slots for what you need to handle and your main event loop can deal with it. I agree with @kshegunov and wouldn't use local event loops on my main event loop thread without good reason.

    Also the reason you are waiting is not a good one. Especially since you put an arbitrary 5s timeout. What if the user doesn't click the ok before that hits?

    @kshegunov You are of course right that the sleep would block. I meant it more to illustrate the point not to actually put it in practice. The point being that the 5 second timer was causing a sleep like situation meaning the QProcess was cleaned up before the signal could get sent to it.


  • Qt Champions 2016

    @ambershark said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    The point being that the 5 second timer was causing a sleep like situation meaning the QProcess was cleaned up before the signal could get sent to it.

    No. I think you misunderstood. deleteLater will remove all pending events for a given object, however here it goes like this:

    1. The signal is emitted and the slot is entered
    2. The local event loop is opened
    3. The local event loop spins the global event loop too(!) - that's the subtle point against using local event loops
    4. The deferred delete event is processed and this (the object whose slot we are currently in) is deleted. (bad)
    5. The local event loop quits and we continue on to return from the slot
    6. SIGSEGV - we deleted the current object while we were in the slot

    You can't reproduce that sequence of events with a sleep, or any other method that doesn't spin the event loop.

    Kind regards.



  • @kshegunov said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    @ambershark said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    The point being that the 5 second timer was causing a sleep like situation meaning the QProcess was cleaned up before the signal could get sent to it.

    No. I think you misunderstood. deleteLater will remove all pending events for a given object, however here it goes like this:

    1. The signal is emitted and the slot is entered
    2. The local event loop is opened
    3. The local event loop spins the global event loop too(!) - that's the subtle point against using local event loops

    I did not realize this happened. I thought the local event loop would block the main event loop. Good to know for future reference. Although I don't think I've ever had a time where I needed 2 event loops on a single thread.

    1. The deferred delete event is processed and this (the object whose slot we are currently in) is deleted. (bad)
    2. The local event loop quits and we continue on to return from the slot
    3. SIGSEGV - we deleted the current object while we were in the slot

    You can't reproduce that sequence of events with a sleep, or any other method that doesn't spin the event loop.

    Kind regards.



  • @ambershark
    I wrote this example with a QTimer, only to simulate and highlight an error, I encountered in another large project. In that project there are several modules that are initialized one by one. During this initialization, I want to check some of the conditions, and if any of them is not satisfied, then it must be a window with a "Warning" message will appear on the screen. For example:

    void CustomModule::initialization()
    {
    	if (!(...check any condition...))
    		WindowsController::show_warning_message("some message to user");
    	
    	if (!(...check any condition...))
    		WindowsController::show_warning_message("some message to user");
    	
    	e.t.c....
    }
    

    Program should not proceed to the next checking, until the user closes the previous "Warning" window. So that's why I desided to use QEventLopp:

    void WindowsController::show_warning_message(const QString& message)
    {
    	if (isWarningMessageLoaded == false)
            {
                /*QQmlApplicationEngine*/mainViewEngine->load(QUrl("qrc:/WarningMessage.qml"));
                isWarningMessageLoaded = true;
            }
    	
    	/*then we get root object of our "Warning message" window, witch has name "warning"*/
    	
    	QObject* rootObject;
            for (int i = 0; i < mainViewEngine->rootObjects().count(); i++)
            {
                QString objectName = mainViewEngine->rootObjects().at(i)->objectName();
                if (!objectName.compare("warning"))
                {
                    rootObject = mainViewEngine->rootObjects().at(i);
                    break;
                }
            }
    	/*set properties*/
    	rootObject->setProperty("text", message);
            rootObject->setProperty("visible", "true");
    	
    	QEventLoop localLoop;
            QObject::connect(rootObject, SIGNAL(closeSignal()), &localLoop, SLOT(quit()));
            localLoop.exec(); /*And now we are waiting for the closeSignal() signal to be received*/
    }
    

    In WarningMessage.qml file:

    MessageDialog {
        objectName: "warning"
        title: "Warning"
        icon: StandardIcon.Warning
        modality: Qt.ApplicationModal
        onAccepted: close()
    
        signal closeSignal()
        onVisibilityChanged: closeSignal()
    }
    

    And it worked in all cases, except one: if after the QEventLoop:exec() call in the other modules there was already working processes, after their completion and quiting "warning" window, in QGuiAllpication::exec() segfault occurred.
    The first idea was to block all signals in application except closeSignal(), but as far as I know, this is not possible.
    And I do not know whether the correct approach I chose to solve this problem...
    But after I placed all the QProcess variables on the stack, as advised by @kshegunov, segfault problem disappeared ...


  • Qt Champions 2016

    @ambershark said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    I did not realize this happened. I thought the local event loop would block the main event loop.

    Yes, most people don't, that's why I went on to try and explain it. But if you think about it, without starting additional threads, that's the only way you can "stop" at a position in code, and still process events.

    Although I don't think I've ever had a time where I needed 2 event loops on a single thread.

    I share the sentiment, however if you use modal dialogs extensively (as I usually do), QDialog::exec() will do just that - start a local event loop. So one should also keep that in mind.

    @MatrixSan said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    But after I placed all the QProcess variables on the stack, as advised by @kshegunov, segfault problem disappeared ...

    I'm glad it worked out in the end.
    Cheers!



  • @kshegunov said in Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit():

    I share the sentiment, however if you use modal dialogs extensively (as I usually do), QDialog::exec() will do just that - start a local event loop. So one should also keep that in mind.

    Yet another thing I didn't think about. I try to steer clear of dialogs in general these days as they've gone out of style, but previously I used them a lot and knew exactly what exec() did. I just didn't relate that to allowing the main event loop and it's baby event loop to coexist. Now that you say that it makes perfect sense, I should have always known that. Just didn't think about it further than it "just working". :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.