QNetworkReply emits error signal twice when ContentNotFoundError occures when event loop is started in error slot

StackOverflow https://stackoverflow.com/questions/7650978

Question

Im using QtSDK 4.7.3

I am doing this in (void test()):

mgr = new QNetworkAccessManager();
reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));

connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
    SLOT(onError(QNetworkReply::NetworkError)), Qt::ConnectionType::UniqueConnection);

And of course the slot onError is called:

if (networkError == QNetworkReply::NetworkError::ContentNotFoundError)
{
// Messagebox starts an event loop which
// causes this slot to be called again
QMessageBox m;
m.exec();
}

If i don't have a messagebox/eventloop in the onError slot there is no crash and everything works. But when it is there then the onError slot gets called again when m.exec() is called. When both messageboxes are closed and I leave the function onError the application crashes. The application tries to delete/free memory when this happens. The error "Access violation reading location" does not help any and the call stack is deep in to Qt dlls.

What I have checked:
The signal is not connected twice.
Tried calling test() before and after the QApplication calls it's exec function. (does not matter).
Another error like HostNotFound will not call the onError slot twice.
All my code is executed in the main thread.
Tried disconnecting the onError slot so it is only called once but it still crashes.
Tried calling abort on the request in onError().
Posted the same question on Qt forum (post).

Can anyone help me figure out what is happening here?

Here is the code I use for testing: main.cpp

#include "contentnotfound.h"
#include <QtGui/QApplication>
#include <QTimer>

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

ContentNotFound cnf;

// false: start test after application's event loop have started
if (true) { cnf.test(); }
else { QTimer::singleShot(2000, &cnf, SLOT(test())); }

return a.exec();
}

contentnotfound.h

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>

class ContentNotFound : public QObject
{
Q_OBJECT

public slots:
void test()
{
    mgr = new QNetworkAccessManager();
    reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));

    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
        SLOT(onError(QNetworkReply::NetworkError)), Qt::ConnectionType::UniqueConnection);
}

private slots:
void onError(QNetworkReply::NetworkError networkError)
{
    //reply->disconnect(); // Disconnect all signals

    if (networkError == QNetworkReply::NetworkError::ContentNotFoundError)
    {
        // Messagebox starts an event loop which
        // causes this slot to be called again
        QMessageBox m;
        m.exec();
    }
}

private:
QNetworkAccessManager* mgr;
QNetworkReply* reply;

};
Was it helpful?

Solution

There is a bug in Qt < 4.8.0: https://bugreports.qt.io/browse/QTBUG-16333

Modifying the connection with a queued one solves the problem:

contentnotfound.h:

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>

class ContentNotFound : public QObject
{
Q_OBJECT

public slots:
void test()
{
    qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
    mgr = new QNetworkAccessManager(this);
    reply = mgr->get(QNetworkRequest(QUrl("http://developer.qt.nokia.com/fileNotExisting.txt")));

    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
        SLOT(onError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
}

private slots:
void onError(QNetworkReply::NetworkError networkError)
{
    //reply->disconnect(); // Disconnect all signals

    if (networkError == QNetworkReply::ContentNotFoundError)
    {
        // Messagebox starts an event loop which
        // causes this slot to be called again
        QMessageBox m;
        m.exec();
    }
}

private:
QNetworkAccessManager* mgr;
QNetworkReply* reply;

};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top