I am not sure if the title of my question is formulated correctly, so to explain what I really mean, consider the following example:
I create a QApplication
and a QWidget
with a QPushButton
on it. Then I attach a handler to the click signal from the button that looks like this:
void MyWidget::on_pushButton_clicked(){
//Never return
while(true);
}
Finally I start the event loop for the application and when I run the program and the window shows up click the button.
This will in my case stall the entire application. So my question at this point is how can I "detect" that this kind of hangup has occurred in my application from the code?
I know writing code that does not return in the signal handler is bad practice, I ask this question because I want to detect errors and recover from them, possibly by restarting the application altogether in an effort to improve resilience when in production.
Thanks!
To detect a hung event loop, you need to wrap the operation that can hang the event loop, to detect when it takes too long. The "operation" is the
QCoreApplication::notify
member. It is called to deliver events to all event loops, in all threads. An event loop hang occurs when the code that processes an event takes too long.When
notify
is entered for a given thread, you can note the time of entry. A scanner running in a dedicated thread can then iterate the list of such times, and pick up the threads that have been stuck for too long.The example below illustrates this, with histograms, too. A thread with an event loop that was stuck longer than the timeout threshold will be highlighted in red. I also illustrate how one can wrap a viewmodel around a data source. Qt 5 and a C++11 compiler are required.
Note: The runtime warnings
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
are not a real issue. They are a Qt bug of only cosmetic consequence and can be ignored in this particular case. You can work around them -- see this question.Some ideas, the actual solution really depends on what you need to do and what kind of feedback you need to have (a UI popping up? something recorded in logs? a debugging feature?)
Use a QTimer and record the time between invocations of a slot of yours
If the slot invocation is delayed by a significant w.r.t. the expected timer timeout, your event loop has been stuck somewhere. This will let you know there has been a problem, but won't tell you where it got stuck.
Use a separate QThread
Send periodically a signal to an object living in the main thread (or send an event; a cross-thread signal is implemented via events anyhow); the slot connected to that signal sends a signal back to the thread. If the "pong" takes too much (you can have a separate timer in the thread) do something --
abort()
,raise()
, i.e. an action which will cause a debugger to stop and you to see the main's thread stack trace, to deduce where you got stuck.Note that since you're running a separate thread you can't just pop up messageboxes or similar -- no UI in other threads! At most, log the event.
Use a separate thread (2)
Qt's event loop emits some signals (see QAbstractEventLoop), in theory you could attach to those in a separate thread and detect if control is not returning to it any more. Or, subclass QAEL to the same means.
Use a QTcpServer / QLocalServer
Same ping/pong concept, but using separate processes -- write a small TCP / local socket client which periodically sends a ping to your application, if the pong doesn't get back in a short while act (now you can also use an UI).