PDA

View Full Version : QNetworkAccessManager->Post() causes SIGSEGV on exit



badfishbeefcake
16th November 2016, 04:41
Wow, I need help, I have no clue on what is going on. Thanks in advance!

I have a QGUIApplication, getting a singleton from class X. Class X has a QNetworkAccessManager.

I don't use the Post() method, it's all good when I close the window. But, as soon as I use the Post method(), when I close the window, I get a segfault. The only info I get from the stack is this:
ntdll!RtlFreeHeap
ucrtbase!free
LIBEAY32!CRYPTO_free

LIBEAY32!CRYPTO_free seems to be related to SSL. I have nothing about SSL in my code.

In the code, Post() works fine, I'm posting a QByteArray, I receive the reply in the slot called by the Finished() signal.

Here some code:


Main.cpp


//AboutToClose() calls Finalize() in ClassX
QObject::connect(&app, SIGNAL(aboutToQuit()), &OtherClass, SLOT(AboutToClose()));


Class X.h


#include <QNetworkAccessManager>
#include <QNetworkReply>

private:
QNetworkAccessManager* m_Manager;

public Q_SLOTS:
void onFinished(QNetworkReply* pReply);


Class X.cpp


void ClassX::InitMyStuff()
{
m_Manager = new QNetworkAccessManager(this);

if(!connect(m_Manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)))) { ASSERT(FALSE); }

PostData();
}

void ClassX::PostData()
{
QFile file(...);
...file.open(QIODevice::ReadOnly)...

QString sUrl(BASE_URL);

QNetworkRequest request = QNetworkRequest(QUrl(sUrl));
request.setHeader(QNetworkRequest::ContentTypeHead er, "application/x-www-form-urlenconded");

m_Manager->post(request, file.readAll());

file.close();
}

void ClassX::onFinished(QNetworkReply *pReply)
{
pReply->close();
pReply->deleteLater();
}

// Look above at Main.cpp to see how this is called
void ClassX::Finalize()
{
m_Manager->disconnect();
delete m_Manager;
m_Manager = NULL;
}

d_stranz
17th November 2016, 17:29
You have created m_Manager as a child of your ClassX instance. In your Finalize() method, you delete the m_Manager instance, but you don't tell ClassX you have done that, so when the ClassX instance goes out of scope, it tries to delete something that is already gone (or perhaps does something else, referencing an invalid pointer). You can either:

- create m_Manager with no parent
- call deleteLater() in Finalize() rather than operator delete()
- do nothing except remove the delete() call.

In the last case, since m_Manager is created with the ClassX instance as a parent, it will automatically be deleted when the ClassX instance is. If the only place you call Finalize() is when you are exiting the app, then all that needs to happen there is to call QNetworkAccessManager::disconnect() and maybe not even that, since I would expect the QNetworkAccessManager destructor to disconnect any open connections.

It almost always causes a SIGSEGV when you call delete on a QObject instance that is owned by something else because that doesn't give Qt the opportunity to clean up after itself before the pointer becomes invalid. If you have to delete something, better to call deleteLater() and let it occur during the normal course of Qt event processing.

anda_skoa
18th November 2016, 09:35
You have created m_Manager as a child of your ClassX instance. In your Finalize() method, you delete the m_Manager instance, but you don't tell ClassX you have done that, so when the ClassX instance goes out of scope, it tries to delete something that is already gone (or perhaps does something else, referencing an invalid pointer). You can either:

That should be ok as long as the delete happens before the parent/child cleanup.

QObjects inform their parent when they are being deleted and the parent removes them from its children list.

Cheers,
_

d_stranz
20th November 2016, 20:39
That should be ok as long as the delete happens before the parent/child cleanup.

Yes, but I'm guessing that there is something event loop-dependent that using operator delete() doesn't allow to happen before the instance is gone, and this is what is causing the SIGSEGV as control returns to the event loop. I am superstitious enough that I almost always use deleteLater() on QObject instances that I manually remove unless I know for sure it is safe to delete them directly.