PDA

View Full Version : Qt application doesn't close properly during QEventloop



jigglyslime
4th November 2013, 04:47
Hi guys, so i'm handling network manager calls in a different thread. When the thread is running and performing a POST, and i close the window, the application is still running in the debugger without stopping (unless i manually press stop button). I think it might have to do with the event loop but i'm not too sure. Can someone help?

here is my POST method:


QNetworkReply *ApiManager::DoHttpPost(QNetworkAccessManager *NetworkManager, const QUrl &Url, QString Data)
{
QNetworkRequest Request(Url);

QNetworkReply *Reply = NetworkManager->post(Request,Data.toUtf8());
Reply->setProperty("Timeout",false);

//Wait for the reply and add a timeout period
QTimer Timer;
Timer.setSingleShot(true);
Timer.setInterval(10000);

QEventLoop Loop;
QObject::connect(&Timer,SIGNAL(timeout()),&Loop,SLOT(quit()));
QObject::connect(&Timer,&QTimer::timeout,[=]() { Reply->setProperty("Timeout",true); });
QObject::connect(Reply,SIGNAL(finished()),&Loop,SLOT(quit()));
Timer.start();
Loop.exec();

return Reply;
}

here is where i make my thread:

QThread *Thread = Thread_Manager.GetThread();
Worker = new QueueWorker(ItemQueue);
Worker->moveToThread(Thread);

//Connect signals and slots
connect(Thread,SIGNAL(started()),Worker,SLOT(Run() ));
connect(Worker.data(),SIGNAL(DeleteQueueItem(Queue Item*)),this,SLOT(DeleteItem(QueueItem*)));
connect(Worker.data(),SIGNAL(Finished()),Thread,SL OT(quit()));
connect(Worker.data(),SIGNAL(Finished()),Worker.da ta(),SLOT(deleteLater()));
connect(Worker.data(),SIGNAL(AuthUser()),this,SLOT (AuthenticateUser()));

//Make sure not to manually delete the thread, instead send it to the thread manager to delete
connect(Thread,&QThread::finished,[=](){
Thread_Manager.DeleteThread(Thread);
Running = false;
emit PopulateModel();
});
Thread->start();

Thread_Manager creates threads and adds them to a list, on deletion of the manager it deletes the threads in the list.

anda_skoa
4th November 2013, 09:18
Do you signal your threads to stop when the window is closed?

Anyway, stupid questions: why use threads for things that are non-blocking by default?

Cheers,
_

jigglyslime
4th November 2013, 12:33
i call this code in the destructor of the thread manager

foreach(QThread *Thread, ActiveThreadList)
{
Thread->quit();
Thread->wait(); //Application hangs here

DeleteThread(Thread);
}

also not a stupid question, in this application i need to complete certain network requests in order, so i synchronously wait for the reply, doing this in the main thread freezes the GUI so i moved it to another one.

EDIT: I did some testing and it turns out that the thread hangs on the wait call, anyone have any idea?

anda_skoa
4th November 2013, 13:44
Well, wait() needs to wait until the other thread exited. If that thread is currently doing something blocking, then the caller will have to wait until that is done and the thread has returned to its event loop processing.

So in your case, check if one of your threads is currently in one of those blocking calls.

If I may make a suggestion: don't use threads since you don't need them.

Doing a sequence of requests can usually easily be done full asynchronously, e.g. by creating a list of tasks and then taking them one by one and executing them.

Can you give a hint on how your sequence looks like?

Cheers,
_

jigglyslime
5th November 2013, 00:50
Doing a sequence of requests can usually easily be done full asynchronously, e.g. by creating a list of tasks and then taking them one by one and executing them.

How would i go about implementing this?


Can you give a hint on how your sequence looks like?

so basically first and foremost i must make a login request to the server, so i send over the auth data
then i get some user information from the server (only works if user has been authenticated, also there's about 4 files that need to be taken)

then when the user updates something, i send that information to the server.

anda_skoa
5th November 2013, 09:58
If I have something that is a composition of asynchronous tasks, I usually go for a "job" pattern

Basically you have an abstract class that defines the interface for your task, e.g.


class Job : public QObject
{
Q_OBJECT

public:
Job( /* .. your common parameters here */, QObject *parent = 0 );

virtual void start() = 0;

int error() const; // either with an error code member or also pure virtual

signals:
voif finished( Job *job );
};


Usually combined with a job queue that automatically runs one job at a time


class JobQueue : public QObject
{
Q_OBJECT

public:
void append( Job *job );

private:
QQueue<Job*> m_jobs;

private slots:
void runNextJob();
void onJobFinished( Job *job );
};

The queue can either stop if a job finished with an error, or the job can indicate if it should be repeated, or you handle the error outside and prepend() the same job if it has to run
This is very much like QNetworkRequest itself.

This is very much like QNetworkRequest itself, i.e. it also has internal state tracking and signals when it is done.

Cheers,
_

jigglyslime
5th November 2013, 10:54
That's what i'm currently doing, i have a class called QueueItem that directly interfaces with the network class and calls the post methods. Then i have the QueueManager which runs the queue item in a for loop. But the thing is, when i remove the event loop and replace it with a signal and slot connection, the queue manager runs all the QueueItem start functions and they don't return in order that they were executed in.

anda_skoa
5th November 2013, 12:13
Right, this is what I meant with "the queue runs one job at a time"



class JobQueue : public QObject
{
Q_OBJECT

public:
explicit JobQueue( QObject *parent = 0 );
void append( Job *job );

private:
QQueue<Job*> m_jobs;
bool m_running;

private slots:
void runNextJob();
void onJobFinished( Job *job );
};




JobQueue::JobQueue( QObject *parent = 0 )
: QObject( parent )
, m_running( false )
{
}

void JobQueue::append( Job *job )
{
m_jobs << job;
connect( job, SIGNAL(finished(Job*)), this, SLOT(onJobFinished(Job*)) );

// schedule job execution
QMetaObject::invokeMethod( this, "runNextJob", Qt::QueuedConnection );
}

void JobQueue::runNextJob()
{
if ( m_running || m_jobs.isEmpty() )
return;

m_running = true;

m_jobs->dequeue()->start();
}

void JobQueue::onJobFinished( Job *job )
{
m_running = false;

if ( job->error() != 0 )
return; // TODO actual error handling

runNextJob();
}


Cheers,
_

jigglyslime
6th November 2013, 00:45
Oh i see what you mean, Thanks :D

anda_skoa
6th November 2013, 09:03
You can also easily create composite jobs, i.e. a job that contains a job queue and runs a series of sub jobs before it emits its own finished() signal.

Cheers,
_