Proper way to close QCoreApplication

Rorschach picture Rorschach · Mar 25, 2015 · Viewed 10.6k times · Source

I'm making a Qt5 QCoreApplication for downloading files from FTP servers (started at HTTP, now switched).

I encounter a problem when my program is to be closed. After I added exit(0) into downloader.cpp, my program is now ending, but I get the following error :

QWaitCondition: Destroyed while threads are still waiting.

My code is as follows :

main.cpp

#include <QCoreApplication>
#include <downloader.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Downloader d;
    d.doDownload();

    a.exec();
}


**downloader.cpp**

#include "downloader.h"

Downloader::Downloader(QObject *parent) :
    QObject(parent)
{
}

void Downloader::doDownload() {

manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));

manager->get(QNetworkRequest(QUrl("ftp://ftp.fao.org/Public/GIEWS/windisp/40manual/wd4en.pdf")));
}


void Downloader::replyFinished (QNetworkReply *reply)
{

    if(reply->error()) {
        qDebug() << "ERROR!";
        qDebug() << reply->errorString();
    }
    else

    {
        qDebug() << "Download finished!";

        QFile *file = new QFile("C:/Users/jelicicm/Desktop/wd4en.pdf");

        if(file->open(QFile::Append))
        {
            file->write(reply->readAll());
            file->flush(); file->close();
            qDebug() <<"Downloaded file size:" <<(file->size())/1024<<"KB";
        }
        delete file;
    }

    reply->deleteLater();
    exit(0);

}

I get the following output :

Download Finished! Downloaded file size... QWaitCondition: Destroyed while threads are still waiting.

As far as I can notice, everything I imagined is done. File is downloaded, and its size is presented. But I guess this error must mean something.

Can somebody explain to me what this error is, why is it occurring and how to patch it?

Answer

Walt Dizzy Records picture Walt Dizzy Records · Mar 25, 2015

You have some problems with your code. You are treating Qt like it is procedural in nature whereas it is actually event-driven via a main loop.

First, it is wrong to call qApp->exit(0) before the QApplication loop is started. According to Qt, "If the event loop is not running, this function does nothing." Your event loop is not yet running because you called doDownload before you called exec

Second, by the time you ran QApplication::exec, you have not yet created any top-level windows or events to dispatch. I am not sure what is supposed to happen when you call exec with no work to do. Semantically, of course, the call to exec does nothing. Technically... perhaps it could lead to thread errors. Again, I am not sure, but I know that you are not supposed to use exec in this manner.

Run your main event loop before doing all your fancy Qt stuff. This allows Qt to call into your code. Add a slot, doDownload and a signal, finished to your Downloader.

class Downloader : public QObject 
{
  Q_OBJECT

public:
  Downloader(QObject * parent = nullptr);

private slots:
  void doDownload();

signals:
  void finished();
}

...

void Downloader::doDownload() 
{
  // Same implementation as before
  // Emit signal when finished
  emit finished();
}

Then call your slot after inversion of control has been established by calling exec and starting the main loop:

#include <QCoreApplication>
#include "Downloader.h"

int main(int argc, char *argv[])
{
  QCoreApplication a(argc, argv);

  Downloader d;

  // Quit application when work is finished
  QObject::connect(&d, SIGNAL(finished()), &a, SLOT(quit())); // changed the 
  //variable name 'app' to 'a'

  // Run the user-hook (doDownload) from the application event loop.
  QTimer::singleShot(0, &d, SLOT(doDownload()));

  return a.exec();
}

Now Qt will call into your code. Instead of explicitly exiting the application, simply emit finished() and everything should be cleaned up correctly.

Please let me know if this solves your issue. If not, there may be other fish to fry.