Qt/C++ event loop exception handling - qt

I am having an application heavily based on QT and on a lot of third party libs. These happen to throw some exceptions in several cases.
In a native Qt App this causes the application to abort or terminate. Often the main data model is still intact as I am keeping it in pure Qt with no external data.
So I am thinking that I could also just recover by telling the user that there has occurred an error in this an that process and he should save now or even decide to continue working on the main model.
Currently the program just silently exits without even telling a story.

Sometimes it's really hard to catch all exception. If one exception accidently slips through, the following helps a lot. Inherit from QApplication and override the notify() function in the following way
bool MyApplication::notify(
QObject * receiver,
QEvent * event )
{
try
{
return QApplication::notify(receiver, event);
}
catch(...)
{
assert( !"Oops. Forgot to catch exception?" );
// may be handle exception here ...
}
return false;
}
Then replace the QApplication in your main() function by your custom class. All events and slots are issued through this function, so that all exceptions can be caught and your application becomes stable.

As stated in the Qt documentation here, Qt is currently not fully exception safe. The "Recovering from exceptions" section on that page describes the only thing which you can do in a Qt application when an exception is thrown - clean up and exit the app.
Given that you are using third party libraries which do throw exceptions, you need to catch these at the boundary between the external library and the Qt code, and handle them there - as stated in Caleb's comment. If the error must be propagated into the Qt application, this must be done either by returning an error code (if possible), or by posting an event.

Related

Why the task of QThread can not be executed until the function returns

For example:
void MainWidget::testThreadTask()
{
qDebug() << "On test task";
}
void MainWidget::onBtnClick()
{
QThread *thread = new QThread;
connect(thread, QThread::started, this, testThreadTask);
thread->start();
qDebug() << "Thread START, now we wait 5s";
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < 5000)
{
}
qDebug() << "END";
}
The program output is:
START wait 5s
END
On test task
I want to create a task to handle something after the button is pressed, and then the function will wait for the task to complete before returning.
In fact, it may not be necessary to create a new task and wait for it to execute, because since you have to wait and get stuck there, why not run it directly in the function.
But this is actually a problem when I deal with QT serial data. I want to send the data to the serial port after pressing the button, and then wait for the data (by constantly reading), but I find that when I have been waiting, the serial port can not read the data at all, only when I exit the function the serial port can read the data.
Is there any way to deal with serial data sending and receiving synchronization?
void MainWidget::onBtnClick()
{
serial->write("Test");
if (serial->bytesAvailable())
{
QByteArray data = serialIo->readAll();
// handle the data
}
}
You are mistaken with what is happening in your application. I suggest you read Threads and QObjects (the entire page), Qt::ConnectionType and the detailed description of QThread.
What is happening to you is:
MainWidget does not live in thread. For the slot of a regular object to be called from thread, it first needs to be moved to that thread.Note that subclasses of QWidget cannot be moved to another thread. Because some OS supported by Qt limit where windows can live, they made the choice to force all QWidget to stay in the main thread, in all OS Qt can execute on.
When you connect thread to this (which BTW is incorrect in your question, it should have been with ampersands connect(thread, &QThread::started, this, &MainWidget::testThreadTask);), you create a queued connection, even though the thread has not technically started yet.
When you start the thread:
It fires its started signal.
Because the connection is a Qt::QueuedConnection, the slot will only be executed after returning to the main thread's event loop, i.e. some time after returning from onBtnClick.
Notes:
You would have more useful information in qDebug() about the threads running your code by using QThread::currentThread().Even better than that, your IDE should provide you a window specifically to see what thread has reached a breakpoint (Ctrl+Alt+H on Visual Studio).
At the risk of insisting, keep in mind this warning from the Qt help:
Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.
With that said, because you wait 5 seconds before returning to the event loop and because it is only test code (= there should be no bug + it does not matter even if there is one), you should try to create a Qt::DirectConnection, just to see the slot be invoked from the worker thread.
The detailed description of QThread (link above) shows a complete working example of a worker object being moved to the new thread before it is started. The point is:
A worker object is created, then moved to the worker thread.
Connections are created for the controller to send QString to the worker object via signal/slot and for the worker object to return result to the controller via signal/slot too.
All these connections are Qt::QueuedConnection by default since the worker object was moved.
The worker thread is started. Since run was not overriden, it starts an event loop (in exec).
And there you have it.
Remember 1 things: widgets cannot be moved!!! Create your own worker object.

Asp.net unhandled exception in timer callback crashes w3wp and error not logged

I am experiencing some randomly happening unhandled exception causing w3wp to crash. I want to trace the cause of that exception. I already have a global Application_Error handler override in my MvcApplication class, so the crash must be caused by some out-of-http-context exception. In order to replicate the problem I genereate one myself in a timer callback, and try to trace it. Simplified code like
public static class MonitorTimers
{
public static Timer _taskMonitorTimer = new Timer(state: null, dueTime: 1000, period: 1000, callback: (state) =>
{
throw new Exception("Ouch! Me dead.");
});
}
In my local development environment (iisexpress launched by VS2017) and test environment (IIS 8.5), when the app starts and then crashes, the following can be seen in event viewer:
The most useful Event 1325 and 1026 sourced from ASP.NET and .NET Runtime shows the stack trace - just the thing I need.
My problem is, in my production machine (also IIS 8.5) I can't find the useful event 1325. Only a crash report, bearing no more information than I know. So I don't know what caused the error. I could surround my timer callback with try...catch block but the error could well be caused by something else (unmanaged libraries, error in static class initialization) then I still can't trace.
So suggestions on why event 1325 is missing or some tools that can show the log and analyse the stack trace is greatly appreciated. Thank you.
So, in your case you generate Exception diring Loading of application domain.
When CLR load application domain it firstly init static fields. So, if your code has problem with static fields, then exception will throw until it specifies Application_Error handler.
One more point, Is your application take a lot of memory? There are 2 cases when application can not write logs and execute code in catch block: StackOverflowException and OutOfMemoryException. Can you check is it has some memory leaks or infinite recursion?
One more point: set in visual studion setting to break when any exception throwed.
One more point: It is better to move your initialization logic from static constructors to ApplicationStart or something like this. You can do it temporary, for catch the bag and then move it to previous state.

Detect QApplication exit as normal or crash

I need to detect the application get exit as normal or crash. QProcess have the finished() signal and can get the exit code. But i need this exit code for QApplication when the application get crash or close.
When your process crashes, it's gone. The crash means that the process has finished because of an unhandled exception. Your job should be to prevent the crash from happening. In other words: handle the exceptions. Note that the exceptions may not be C++ exceptions, they may be low-level platform-specific mechanisms, such as native exceptions on Windows or signals on UNIX. You'd have to handle those, but recognize that the underlying issue is not fixed merely because you catch such an exception. You must assume that the state of your application has been corrupted, and the only safe thing to do is to exit ASAP anyway. For example, do not try to modify any files: you're likely to corrupt them.
I don't think this is something you can do just like that. Reading the value returned by QApplication::exec() is related to the Qt infrastructure:
Enters the main event loop and waits until exit() is called, then
returns the value that was set to exit() (which is 0 if exit() is
called via quit()).
Usually your main looks like this:
#include <QApplication>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
// Initialize your widget(s)
return a.exec(); // You can store this and check its value
}
However if I'm not mistaken this doesn't include handling a crash of your application (segmentation fault, unhandled exception etc.). In Linux people usually use a script which starts the application and then reads its exit code after the application quits or crashes. If you use Linux you can use echo $? to read the exit code from the bash scrip (or its equivalent for a different shell) and then do something based on its value.
Note also that you can at least do some exception handling since some crashes result in exactly that - an exception that has been thrown for some reason and has not been processed properly. Unhandled exceptions in Qt get propagated to the top level (that is QCoreApplication).

Qt SSL: Handshake failed for requests made from QML

I make some https requests directly from my qml views, for instance for image sources. As I have a self signed certificate server side, I need to tell qt to ignore some ssl errors (I control both the server and the client applications, so this shouldn't really be a problem).
I've made a QQmlNetworkAccessManagerFactory to create NAMs, where I connect to the sslErrors signal.
UltraQmlAccessManagerFactory.h:
#ifndef FACKFACKTORy_H
#define FACKFACKTORy_H
#include <QQmlNetworkAccessManagerFactory>
#include <QObject>
#include <QNetworkReply>
#include <QList>
#include <QSslError>
#include <QNetworkAccessManager>
#include <QDebug>
#include <QSslCertificate>
class UltraQmlNetworkAccessManagerFactory : public QObject,
public QQmlNetworkAccessManagerFactory {
Q_OBJECT
private:
QNetworkAccessManager* nam;
QList<QSslError> expectedSslErrors;
public:
explicit UltraQmlNetworkAccessManagerFactory();
~UltraQmlNetworkAccessManagerFactory();
virtual QNetworkAccessManager* create(QObject* parent);
public slots:
void onIgnoreSslErrors(QNetworkReply* reply, QList<QSslError> errors);
};
#endif
UltraQmlNetworkAccessManagerFactory.cpp:
#include "UltraQmlNetworkAccessManagerFactory.h"
UltraQmlNetworkAccessManagerFactory::UltraQmlNetworkAccessManagerFactory() {
}
UltraQmlNetworkAccessManagerFactory::~UltraQmlNetworkAccessManagerFactory() {
delete nam;
}
QNetworkAccessManager* UltraQmlNetworkAccessManagerFactory::create(QObject* parent) {
QNetworkAccessManager* nam = new QNetworkAccessManager(parent);
QObject::connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>))
);
return nam;
}
void UltraQmlNetworkAccessManagerFactory::onIgnoreSslErrors(QNetworkReply *reply, QList<QSslError> errors) {
for (int i = 0; i < errors.size(); i++) {
qDebug() << "e: " << errors.at(i) << endl;
}
reply->ignoreSslErrors(errors);
}
There is also some glue in main.cpp that sets this factory to be used, I doubt that part is a source of errors as the qDebug prints are visible in the output.
As can be seen in the .cpp file in the function/slot onIgnoreSslErrors, I try to ignore every error (as a test) that I receive, but in the output I do not get the expected results.
Output
e: "The certificate is self-signed, and untrusted"
qrc:/qml/file/ImageView.qml:16:5: QML Image: SSL handshake failed
I have successfully made QNetworkRequests from C++ directly with a QSslConfiguration, specifying TLSV1_0 and a certificate. As I have a suspicion that the handshake fails because one side expects SSL and the other TLS I have also tried to set the QSslConfiguration on the QNetworkRequest object throgh reply->request(); This, however, changes nothing.
(This is a very old one but as I stumbled over this recently and haven't found the answer, I think it's still worth answering)
You don't show the place where you actually setup the factory object but it is extremely likely that it does not belong to the same (actually, any) thread used when its create() method is called. Here's an excerpt from the Qt documentation on the class:
the developer should be careful if the signals of the object to be returned from create() are connected to the slots of an object that may be created in a different thread
It further mentions authenticationRequired() signal but sslErrors() acts in the same way: both signals, among a few others, need either a direct or a blocking queued connection so that by the time of returning to the place of emitting the signal the network reply object has already been configured by the slot.
What happens in your case is (very likely) the following (TL;DR: your slot is called asynchronously by a queued connection because it lives in a different thread, while sslErrors() requires a synchronous change to the running network reply object; despite the order of log lines, the request fails first and ignoreSslErrors() is called later):
The factory object is created, and the QML engine configured, in the main thread.
QML engine spawns a few threads to perform backend stuff, notably network requests for URLs (I'm making the assumption here that your ImageView.qml has an Image component). To perform the network request, these threads call UltraQmlNetworkAccessManagerFactory::create().
create() produces a NAM object and sets up a connection on it. The parent here is either the QQmlEngine object or (specifically for image requests) the backend thread object, as you can see e.g. here. Therefore, this NAM object belongs to the backend thread.
connect() uses Qt::AutoConnection type by default, which, since the threads of the factory and of the NAM object are different, corresponds to Qt::QueuedConnection. As a sidenote, the threads are checked at the time of signal invocation.
Eventually a QNetworkAccessManager::sslErrors() signal is emitted. Since this is a queued connection, the only thing that immediately happens is placing an invocation of onIgnoreSslErrors() on the event queue for the main thread.
If you're very lucky, you may have a context switch to the main thread right after that - but there's literally nothing to ensure that so it's much more likely that control returns to the site where QNetworkAccessManager::sslErrors() was emitted. And since ignoreSslErrors() wasn't called, the request fails the handshake. The backend thread posts relevant data from the (failed) QNetworkReply object back to the main thread - see the postReply call in the middle of the method (or it may do it a bit later - that doesn't matter anymore).
Once the context switches to the main thread ignoreSslErrors() is executed - alas, it's already too late, as the network reply has very likely finished with failure already; but that first log line comes out now.
The main thread goes on through the event loop and finds the QQuickPixmapReply::Event object with the failure data. After some unroll of the calls and signals the failed image ends up in QQuickImageBase::requestFinished() that prints the second log line for you .
As for the fix, it's tempting to just specify Qt::BlockingQueuedConnection as the fifth parameter to connect(). Unfortunately, this will deadlock if a request is ever made from QQmlEngine that runs in the main thread - and uses a NAM instance created by this factory to, e.g., request QML components over the network. Best I could come out with so far is an equivalent of
connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>)),
currentThread() == this->thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection
);

Application_Start timeout?

I have one piece of code that gets run on Application_Start for seeding demo data into my database, but I'm getting an exception saying:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
While trying to enumerate one of my entities DB.ENTITY.SELECT(x => x.Id == value);
I've checked my code and I'm not disposing my context before my operation, Below is an outline of my current implementation:
protected void Application_Start()
{
SeedDemoData();
}
public static void SeedDemoData()
{
using(var context = new DBContext())
{
// my code is run here.
}
}
So I was wondering if Application_Start is timing out and forcing my db context to close its connection before it completes.
Note: I know the code because I'm using it on a different place and it is unit tested and over there it works without any issues.
Any ideas of what could be the issue here? or what I'm missing?
After a few hours investigating the issue I found that it is being caused by the data context having pending changes on a different thread. Our current implementation for database upgrades/migrations runs on a parallel thread to our App_Start method so I noticed that the entity I'm trying enumerate is being altered at the same time, even that they are being run on different data contexts EF is noticing that something is wrong while accessing the entity and returning an incorrect error message saying that the datacontext is disposed while the actual exception is that the entity state is modified but not saved.
The actual solution for my issue was to move all the seed data functions to the database upgrades/migrations scripts so that the entities are only modified on one place at the time.

Resources