Clean up before closing the QCoreApplication - qt

I have a console-based QCoreApplication which has timers and does socket communication and also uses locked mutex.
When I close the application manually, it gives error saying some mutex is locked and it is timed out. Is there any way I can do clean up in a console application when user closes it?

Cleanup should be handled by destructors and child-parent relationship.
Make your master object (the one in the main) a child of QApplication so it is destructed with all its childs before QApplication is.
Are you sure you killed all your threads? If it is a thread with an eventloop be sure to call QThread::quit() to exit the eventloop before you call QThread::wait()
You can also use the void QApplication::qAddPostRoutine ( QtCleanUpFunction ptr )
to do some special cleanup.
For debugging those messages you can use QtMsgHandler qInstallMsgHandler ( QtMsgHandler h ) and write your own message handler to capture those warnings. If you can simulate the problem you can set a breakpoint on the message and see on the stack where the message is coming from.
void debugMessageHandler( QtMsgType type, const char *msg ){
if(QString(msg).contains( "The message you can see in the console" )){
int breakPointOnThisLine(0);
}
switch ( type ) {
case QtDebugMsg:
fprintf( stderr, "Debug: %s\n", msg );
break;
case QtWarningMsg:
fprintf( stderr, "Warning: %s\n", msg );
break;
case QtFatalMsg:
fprintf( stderr, "Fatal: %s\n", msg );
abort();
}
}
In order to clean up with destructor and child-parent relation ship you can catch the console close signal and call QCoreApplication::exit() to the application instance.
#include <csignal>
#include <QtCore/QCoreApplication>
using namespace std;
struct CleanExit{
CleanExit() {
signal(SIGINT, &CleanExit::exitQt);
signal(SIGTERM, &CleanExit::exitQt);
signal(SIGBREAK, &CleanExit::exitQt) ;
}
static void exitQt(int sig) {
QCoreApplication::exit(0);
}
};
int main(int argc, char *argv[])
{
CleanExit cleanExit;
QCoreApplication a(argc, argv);
return a.exec();
}

Turns out that closing command line application (checked on Win7 & VS2010) by pressing 'close' (red x button on title bar) passes the STATUS_CONTROL_C_EXIT signal to the application. All threads are aborted with this code.
The thread 'Main Thread' (0x980) has exited with code -1073741510
(0xc000013a).
The thread 'QThread' (0x2388) has exited with code
-1073741510 (0xc000013a).
That means that there is no way to intercept this with the QCoreApplication::aboutToQuit() signal.
Take a look at winnt.h or ntstatus.h. That is the value assigned to the
manifest constant STATUS_CONTROL_C_EXIT. The runtime is just
choosing to end your program with the code to note the user's cancel
operation.

you can connect to QCoreApplication::aboutToQuit signal and do the necessary clean up there.

Related

QApplication recover from segmentation fault

I want to be able to recover from a Segmentation Fault in MyApplication by catching the SIGSEGV and restarting QApplication. So for testing purposes I'm injecting a segmentation fault in my code.
The issue is that the signal handler that catches the SIGSEGV is getting a non-stop stream of SIGSEGVs. At first I thought it was the while loop in my main but it still happens even though I comment out the while loop. So my questions are simple: Is it even possible to recover from a Segmentation Fault in Qt? Why am I getting rolling SIGSEGVs non-stop?
#include <QApplication>
#include <QDebug>
#include "MyApplication.h"
#include <initializer_list>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#define RESTART_CODE 1000
void catchUnixSignals(std::initializer_list<int> quitSignals)
{
auto handler = [](int sig) -> void
{
if (sig == SIGSEGV)
{
QCoreApplication::exit(RESTART_CODE);
}
else
{
QCoreApplication::quit();
}
};
sigset_t blocking_mask;
sigemptyset(&blocking_mask);
for (auto sig : quitSignals)
sigaddset(&blocking_mask, sig);
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = blocking_mask;
sa.sa_flags = 0;
for (auto sig : quitSignals)
sigaction(sig, &sa, nullptr);
}
int main(int argc, char *argv[])
{
catchUnixSignals({SIGSEGV, SIGQUIT, SIGINT, SIGTERM, SIGHUP, SIGKILL});
int i = 0;
do
{
QApplication app(argc, argv);
MyApp myapp;
MyApp.start();
app.exec();
if (app.exec() != RESTART_CODE) break;
} while(1);
return 0;
}
This does not directly answers your question, but is another way to achieve similar behavior.
To recover from a segmentation fault, an option is to use a watchdog, i.e. another independent process that checks the status of your main software and restarts it when needed.
When you start your software, you create another process that runs a 2nd software, the watchdog. Ensure to start it in "detached" mode to avoid that it gets closed if your main software crashes.
In the watchdog, frequently call "tasklist" on Windows or "ps" or "top" on Linux and parse the output to check whether your software is still running. OR, use a UDP or TCP port to communicate between the main software and the watchdog, to tell the watchdog that the main software is still running well.
In the watchdog, if the main software is no longer running, restart the main software process (also in detached).
CAUTION: You need to manage the case where the main software is exited correctly. In that case the main software should either kill the watchdog itself when exiting normally (calling "kill" on the pid), or send it a message so that the watchdog exits as well.

I can't quit properly my apps if I use mysql connection

If I do not use connection I can properly exit.
In Pdv.h file
namespace Pdv {
...
extern QSqlDatabase db;
...
}
In LoginDialog.cpp file
QSqlDatabase Pdv::db;
...
Pdv::db= QSqlDatabase::addDatabase("QMYSQL3");
Pdv::db.setHostName(Pdv::DB_URL);
Pdv::db.setUserName(Pdv::DB_USER);
Pdv::db.setPassword(Pdv::DB_PASS);
Pdv::db.setDatabaseName(Pdv::DB_DB);
if(!Pdv::db.open()) {
...
// Checking user login/password and retrieve many variables
...
In mainwindow.cpp file
...
void MainWindow::closeEvent(QCloseEvent *event) {
...
if(Pdv::db.isOpen()) {
qDebug() << "Opened 1";
Pdv::db.close();
qDebug() << Pdv::db.lastError();
if(Pdv::db.isOpen())
qDebug() << "Opened 2";
}
Pdv::app->quit(); // or QApplication::quit();
}
I got this error in QTCreator console
Opened 1
QSqlError("", "", "")
Le programme s'est terminé subitement.
/home/cosmic/src/build-Pdv-Desktop-Debug/Pdv crashed.
A idea?
To make proper exit with usage of QSqlDatabase, you need preferably:
remove all instances of QSqlDatabase objects (because as you copy them, they will keep connection open).
As second condition, you need to use QSqlDatabe::removeDatabase() call. (also this call will make qDebug message if database is still in use occasionally - some QSqlDatabase object is left somewhere - it will help to identify a problem).
If you close and delete your MainWindow, and your program then crashes, then other parts of the program must be trying to use the MainWindow pointer even though it is destroyed.
I think the problem is the line of code Pdv::app->quit(); Try with QApplication::quit(); instead or review the code in Pdv::app->quit();.

QtDBus Simply Example With PowerManager

I'm trying to use QtDbus to communicate with interface provided by PowerManager in my system. My goal is very simple. I will be writing code which causes my system to hibernate using DBus interface.
So, I installed d-feet application to see what interfaces DBus is available on my system, and what I saw:
As we see, I have a few interfaces and methods from which I can choose something. My choice is Hibernate(), from interface org.freedesktop.PowerManagment
In this goal I prepared some extremely simple code to only understand mechanism. I of course used Qt library:
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtDBus/QtDBus>
#include <QDBusInterface>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (!QDBusConnection::sessionBus().isConnected()) {
fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"
"To start it, run:\n"
"\teval `dbus-launch --auto-syntax`\n");
return 1;
}
QDBusInterface iface("org.freedesktop.PowerManagement" ,"/" , "" , QDBusConnection::sessionBus());
if(iface.isValid())
{
qDebug() << "Is good";
QDBusReply<QString> reply = iface.call("Methods" , "Hibernate");
if(reply.isValid())
{
qDebug() << "Hibernate by by " << qPrintable(reply.value());
}
qDebug() << "some error " << qPrintable(reply.error().message());
}
return 0;
}
Unfortunately I get error in my terminal:
Is good
some error Method "Methods" with signature "s" on interface "(null)" doesn't exist
So please tell me what's wrong with this code? I am sure that I forgot some arguments in function QDBusInterface::call() but what ?
When creating interface you have to specify correct interface, path, service. So that's why your iface object should be created like this:
QDBusInterface iface("org.freedesktop.PowerManagement", // from list on left
"/org/freedesktop/PowerManagement", // from first line of screenshot
"org.freedesktop.PowerManagement", // from above Methods
QDBusConnection::sessionBus());
Moreover, when calling a method you need to use it's name and arguments (if any):
iface.call("Hibernate");
And Hibernate() doesn't have an output argument, so you have to use QDBusReply<void> and you can't check for .value()
QDBusReply<void> reply = iface.call("Hibernate");
if(reply.isValid())
{
// reply.value() is not valid here
}

Qt, QCoreApplication and QFtp

I want to use QFtp for the first time and googled a lot to find out how it should be used. This, among others is a typical example:
#include <QCoreApplication>
#include <QFtp>
#include <QFile>
int main(int argc, char ** argv)
{
QCoreApplication app(argc, argv);
QFile *file = new QFile( "C:\\Devel\\THP\\tmp\\test.txt" );
file->open(QIODevice::ReadWrite);
QFtp *ftp = new QFtp();
ftp->setTransferMode(QFtp::Active);
ftp->connectToHost("ftp.trolltech.com");
ftp->login();
ftp->cd("qt");
ftp->get("INSTALL",file);
ftp->close();
QObject::connect(ftp, SIGNAL(done(bool)), &app, SLOT(quit()));
int ret = app.exec();
delete ftp;
delete file;
return ret;
}
The question:
As far as I understood, the QCoreApplication app is needed to handle the "done" signal, emmited upon finalization of ftp-get. Now, the ftp->get is called before the connect and even before the app handler is running at all (app.exec() is called afterwards).
What happens, if the file transfer has completed already before the "connect" statement? In fact, that will not happen, but I could put an artificial delay of, say 1 minute between ftp->close() and the connect(...). During this time, the ftp get will surely be finished. What would happen?
Note that QFtp is really only meant for legacy Qt applications and it is now suggested that QNetworkAccessManager and QNetworkReply are used instead, as detailed in the Qt documentation.
That being said, with your connect call being positioned after the connection to the FTP site and retrieving of the file, should the file be downloaded first, the 'quit' signal would never be reached. If you make the connection straight after creating the QFtp object, then this won't be an issue: -
QFtp *ftp = new QFtp();
QObject::connect(ftp, SIGNAL(done(bool)), &app, SLOT(quit()));
This guarantees that the 'quit' slot will be called when the QFtp object emits the 'done' signal.
QFtp *ftp = new QFtp();
QObject::connect(ftp, SIGNAL(done(bool)), &app, SLOT(quit()));
ftp->setTransferMode(QFtp::Active);
ftp->connectToHost("ftp.trolltech.com");
ftp->login();
ftp->cd("qt");
ftp->get("INSTALL",file);
ftp->close();
int ret = app.exec();
In reality though, I would expect the connect in your example would complete before the machine had time to negotiate a connection to another server, login and start the download of the file.

-[CFRunLoopTimer release]: message sent to deallocated instance 0x4e281f0

i have done chat app on xcode 4 without errors , but when i lunch the app it takes 4 seconds then it show me thread error ( SIGKILL & EXE_BAD_ACCESS ) at main.m file
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); // thread
[pool release];
return retVal;
}
then i tried NSZombieEnabled and i get this < -[CFRunLoopTimer release]: message sent to deallocated instance 0x4e281f0 >
what i can do ? please
You're either releasing an instance of CFRunLoopTimer (or possibly NSTimer, they're toll-free bridged) twice, or you're releasing an instance that you don't own. If you use the analyzer in XCode, there is a good chance it will flag the incorrect release for you; otherwise you'll have to look through your code wherever you use these classes and verify that you release appropriately.

Resources