void QGCCore::getLatestGCSVersion()
{
QString url = "http://xxxxx";
QNetworkRequest newRequest(url);
newRequest.setUrl(url);
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
networkManager->get(newRequest);
}
void QGCCore::downloadFinished(QNetworkReply * reply)
{
reply->deleteLater();
}
The above code, with empty constructor and destructor.
If I start the software and close it, it gives warning:
QWaitCondition: destroyed while thread is still running.
However, if I wait a while to close this, then it is Ok...
What's going wrong here?
Related
My application makes use of QNetworkReply's for send and receiving data from a RESTful API.
There are many tutorials available for using the QNetworkReply with QNetworkAccessManager
Once such example which I used can be found here or even here
Basic Usage:
// Header
QNetworkAccessManager *manager;
QNetworkReply *myReply;
QMetaObject::Connection conReply;
// Making the request
void MainWindow::makeRequest(QString url) {
//...
QNetworkRequest request(QUrl(url));
myReply = manager->get(request) // or post(request)
conReply = QObject::connect(myReply, SIGNAL(finished()), this, SLOT(myReplyResponse()));
}
// Handling the request
void MainWindow::myReplyResponse(){
QObject::disconnect(conReply);
QByteArray data = myReply->readAll();
// or QByteArray data = myReply->read(myReply->bytesAvailable());
myReply->deleteLater();
// do something with this data
//...
}
Using a similar implementation, I request data every X seconds.
Problem:
When receiving the finished() signal, the code handling the reply is triggered, but when reading the data, I get a SIGSEGV.
This issue seems to occur at random, thus I cannot determine what triggers it.
Any suggestions would be gladly accepted.
What is probably happening is that it is delaying the order, let's say that an order is sent every second but it takes 2 seconds to replicate, after 2 seconds you have read the reply and you have deleted it from memory, when the other comes myReply is an empty pointer. What you must do is use sender() to obtain the replica, and it is always advisable to validate that you do not have an empty pointer:
*.h
private:
QNetworkAccessManager *manager;
*.cpp
[...]
manager = new QNetworkAccessManager(this);
[...]
void MainWindow::makeRequest(const QString &url)
{
Qurl mUrl(url);
QNetworkRequest request(mUrl);
QNetworkReply *myReply = manager->get(request); // or post(request)
connect(myReply, &QNetworkReply::finished, this, &MainWindow::myReplyResponse);
}
void MainWindow::myReplyResponse()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if(reply){
QByteArray data = reply->readAll();
qDebug()<<data;
reply->deleteLater();
}
}
I tried to emit signal finished() from my class. But when I'm connecting the signal to my slot, it didn't do anything.
My class' name is blend_install, I declared it as blendinstaller and tried to connect that to the QEventLoop.
....
QEventLoop ac;
connect(&blendinstaller, SIGNAL(finished()), &ac, SLOT(quit()));
blendinstaller.show_progress();
blendinstaller.download(); // this will execute everything and in the end emit finished()
ac.exec();
....
The download() function:
current_prog = BLEND_INSTALL_NONE;
emit progress_changed(current_prog);
manager = new QNetworkAccessManager;
file_handler = new QFile(downloadTo);
file_handler->open(QFile::WriteOnly);
.... handle error .... // each of this (error handling) will emit finished() signal and return;
.... // each of this will represent the process of reporting event changes (for logging), emit a SIGNAL()
QNetworkRequest request;
request.setUrl(QUrl(downloadFrom));
reply = manager->get(request);
event = new QEventLoop;
connect(reply,SIGNAL(finished()),event,SLOT(quit()));
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(downloadError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(downloadProgressL(qint64,qint64)));
event->exec();
.... handle error ....
.... write reply.readAll() to file ....
....
// these are instruction for a custom QProcess instance
proc.setProgram(extractWith);
proc.setArguments(ar);
proc.setWorkingDirectory(downloadIn);
event = new QEventLoop;
connect(&proc,SIGNAL(finished(int)),event,SLOT(quit()));
connect(&proc,SIGNAL(error(QProcess::ProcessError)),this,SLOT(extractError(QProcess::ProcessError)));
connect(&proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(extractFinished(int,QProcess::ExitStatus)));
proc.start();
proc.open_console();
event->exec();
.... handle error ....
....
.... attempt to find output of QProcess (extract an archive) ....
.... handle error, output of QProcess not found ....
....
emit installed(installOn);
emit finished(); // the SIGNAL I want to get.
qDebug("It's finished installing!");
So, TL;DR each of errors handling will return from the function but also emit finished() and in the end of function (assuming there is no error) it will emit finished().
It won't quit the loop.
Any idea?
The problem with your download() method is, that it is already a synchron method. You don't need this event loop. You already do everything in event loops inside your download() method.
Side note: And you seem to have some memory leaks, since you create QEventLoop without parent and never delete it.
UPDATE #1:
Your finished() event is not being handled by the outer QEventLoop (ac), because the finished() signal gets emitted before the QEventLoop even begins to handle events with exec().
As a ugly workarround you could invoke download() after exec() with a queued QMetaObject::invokeMethod() (Qt::QueuedConnection) call (but i would not recommend it).
Update #2
Here is a small example, which is not perfect, of course :P
class BlendDownloader
{
Q_OBJECT
public:
BlenDownloader() :
_file(NULL)
{
}
void download()
{
_file = new QFile("C:/myfile.blubb");
QNetworkRequest req("your url here");
QNetworkReply* reply = _mgr.get(req);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(onDownloadFinished()));
// TODO: Also handle error callback here
}
private slots:
void onDownloadFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
reply->deleteLater();
// Write response data to file.
// Note: You might get problems with big files,
// since this buffers the entire response of QNetworkReply
// in internal buffer. It's better to use the "readyRead()"
// signal and write incrementally.
_file->write(reply->readAll());
// Do your parsing stuff now and emit "finished()" at the end.
// ... parsing, validation here ...
// Clean up
_file->close();
delete _file;
_file = NULL;
emit finished();
}
private:
QNetworkManager _mgr;
QFile* _file;
};
I have the following code which is used to access the network using a thread pool(aka, the following code might be called by several different threads at the same time.)
...
QEventLoop q;
QNetworkReply *reply;
QNetworkRequest qheader;
QNetworkAccessManager network;
QTimer tT;
tT.setSingleShot(true);
connect(&network, SIGNAL(finished(QNetworkReply*)),
&q, SLOT(quit()));
connect(&tT, SIGNAL(timeout()), &q, SLOT(quit()));
network.setProxy(Proxy);
QString url=MakeImageUrl(type,pos,zoom,LanguageStr);
qheader.setUrl(QUrl(url));
qheader.setRawHeader("User-Agent",UserAgent);
qheader.setRawHeader("Accept","*/*");
reply=network.get(qheader);
ret=reply->readAll();
reply->deleteLater();
...
Then the program some times crash at the reply = network.get(qheader), am I doing anything wrong here? I have tried qt5.4.1 and qt4.8.6, though the callstack of the crash is not exactly the same, I just wonder how correct is the above code?
This is my code. But I am confused where should I delete m_networkManager. I can do that in
onRequestCompleted() slot but the problem is my program calls getData function frequently.
My fear is this case:
getData() is called.
Before onRequestCompleted() slot is fired, my
program calls getData() again.
onRequestCompleted() for the first getData() call gets fired.
my program deletes m_networkManager which was actually allocated other memory when getData() was called second time.
I think this situation can cause crash.
void MyApp::getData() {
QNetworkRequest request;
request.setUrl(QUrl("http://www.domain.foo"));
m_networkManager = new QNetworkAccessManager(this); // Instance variable
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onRequestCompleted(QNetworkReply *)));
m_networkManager->get(request);
}
void MyApp::onRequestCompleted(QNetworkReply *reply) {
QByteArray data = reply->readAll();
reply->deleteLater();
//m_networkManager->deleteLater();
}
Since you create new QNetworkAccessManager instance each time MyApp::getData() called, you need to manage to delete the right one in MyApp::onRequestCompleted() function. The problem in your code is that you always delete the last created QNetworkAccessManager and not the one that caused the slot call. My suggestion is following:
void MyApp::getData() {
[..]
// Not class member. Will be deleted in the slot.
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onRequestCompleted(QNetworkReply *)));
[..]
}
void MyApp::onRequestCompleted(QNetworkReply *reply) {
QByteArray data = reply->readAll();
reply->deleteLater();
// Delete object whose signal triggered this slot.
QObject *networkManager = sender();
networkManager->deleteLater();
}
EDIT:
I tried doing what you guys told me in comments ... :
Citizen * c = new Citizen(this);
QThread thread;
c->moveToThread(&thread);
connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
thread.start();
This produces even more errors:
QThread: Destroyed while thread is still running
ASSERT failure in QThread::setTerminationEnabled(): "Current thread was not started with QThread.", file c:\ndk_buildrepos\qt-desktop\src\corelib\thread\qthread_win.cpp, line 542
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QObject::killTimers: timers cannot be stopped from another thread
I am having problems with this error ... I'm stuck on this for 2 days already and can't get a solution.
Header:
class Citizen : public QThread
{
Q_OBJECT
QNetworkAccessManager * manager;
private slots:
void onReplyFinished(QNetworkReply* net_reply);
public:
Citizen(QObject * parent);
void run();
};
Implementation:
Citizen::Citizen(QObject * parent)
{
manager = new QNetworkAccessManager;
connect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
}
void Citizen::onReplyFinished(QNetworkReply* net_reply)
{
emit onFinished(net_reply);
}
void Citizen::run()
{
manager->get(QNetworkRequest(QUrl("http://google.com"));
QEventLoop eLoop;
connect(manager, SIGNAL( finished( QNetworkReply * ) ), &eLoop, SLOT(quit()));
eLoop.exec(QEventLoop::ExcludeUserInputEvents);
qDebug() << "loaded google!";
exec();
}
When manager->get() gets executed, I get the following error:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0xc996cf8), parent's thread is QThread(0xaba48d8), current thread is Citizen(0xca7ae08)
When eLoop.exec() gets executed:
QObject::startTimer: timers cannot be started from another thread
I start this thread in the following manner:
Citizen * c = new Citizen(this);
c->start();
Why does this happen? How to solve this?
QObject: Cannot create children for a parent that is in a different thread.
You get this because you are creating the QNetworkAccessmanager in the constructor of Citizen, which is being called in the "original" thread. When the Citizen object is moved to the new thread the QNetworkAccessmanager still has its thread affinity set to the original thread but in the run call it will attempt to create the QNetworkReply object ( and probably other objects aswell ) in the new thread. Which produces the warning above.
If you create the manager in the run slot(or at any point after the Citizen object is moved to the new thread) that will not happen.
However you still have some issues. For instance, the Citizen class really doesn't need to be a QThread. It needlessly complicates it. It will suffice for your purpose(afaict) to subclass a QObject. Just make a normal slot and connect that to the QThread::started() signal. And as OrcunC pointed out you need to make sure that the QThread instance is properly scoped.
For more on threading: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
Example:
QThread *thread = new QThread;
thread->start();
Citizen *worker = new Citizen;
worker->moveToThread(thread);
//startWorking can be equivalent of the run function
//in your current implementation and this is where you should
//create the QNetworkAccessManager
QMetaObject::invokeMethod(worker,"startWorking");
I will just try to answer why you are seeing QThread: Destroyed while thread is still running error.
If you do this
void mtMethod () {
Citizen * c = new Citizen(this);
QThread thread;
c->moveToThread(&thread);
connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
thread.start();
}
The thread object will be destroyed when you exit the function but the thread that has been started is still running !. Qt is warning you that you should either stop the thread or create the thread object in a bigger scope. (i.e make it a member function of your class). Something like this :
class myClass
{
virtual ~myClass ();
QThread mythread;
};
myClass::~myClass
{
mythread.stop ();
}
void myClass::mtMethod () {
Citizen * c = new Citizen(this);
c->moveToThread(&mythread);
connect(&mythread, SIGNAL(started()), c, SLOT(ProcessActions()));
mythread.start();
}
I don't believe the new thread exists until run is called. So the constructor is a different thread than run(). What happens if you move the creation of the manager object from the constructor to run()? I imagine that will fix the first error, if not the timer error as well.
Also, I think many people are still building threads the way you are, but you might want to check out this.
You need to consider thread affinity. That error message is not lying or insane, it's telling you exactly what's wrong.
Your problems are mostly due to trying to subclass QThread. Even though the documentation recommends it, it is not the best way to use QThread. Please see this question and answer for more information and links.
I haven't figured out the startTimers error although it could be related to the first one. In any case, you should be able to fix the first error. I have run into this problem in Qt a few times and I find this to be the "best" way to work around it is to create an initialize function and a cleanUp function. All members of the class are pointers that are initialized to NULL until run is called. Note that "best" is in quotes because there are sure to be differing opinions but it works for most situations for me.
Header
class Citizen : public QThread {
Q_OBJECT
QNetworkAccessManager * manager;
private slots:
void onReplyFinished(QNetworkReply* net_reply);
public:
Citizen(QObject * parent);
void run();
private:
void initialize();
void cleanUp();
};
Implementation
Citizen::Citizen(QObject * parent) :
manager(NULL) {
}
void Citizen::onReplyFinished(QNetworkReply* net_reply) {
emit onFinished(net_reply);
}
void Citizen::run() {
initialize();
manager->get(QNetworkRequest(QUrl("http://google.com"));
QEventLoop eLoop;
connect(manager, SIGNAL( finished( QNetworkReply * ) ),
&eLoop, SLOT(quit()));
eLoop.exec(QEventLoop::ExcludeUserInputEvents);
qDebug() << "loaded google!";
exec();
cleanUp();
}
void Citizen::initialize() {
manager = new QNetworkAccessManager;
connect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
}
void Citizen::cleanUp() {
delete manager;
disconnect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
}