Solved Segmentation fault in QQuiApplication::exec() after invoking QEventLoop::quit()
-
@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
todeleteLater
.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 :)
-
@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 forlocalLoop
. -
@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?
-
@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 callingdelete 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. -
@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:- The signal is emitted and the slot is entered
- The local event loop is opened
- The local event loop spins the global event loop too(!) - that's the subtle point against using local event loops
- The deferred delete event is processed and
this
(the object whose slot we are currently in) is deleted. (bad) - The local event loop quits and we continue on to return from the slot
- 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:- The signal is emitted and the slot is entered
- The local event loop is opened
- 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.
- The deferred delete event is processed and
this
(the object whose slot we are currently in) is deleted. (bad) - The local event loop quits and we continue on to return from the slot
- 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 ... -
@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". :)