How to wait synchronously for a Signal in Qt
Article Metadata
Overview
This article shows how to receive signal from a synchronous request.
Note: This approach works well for test cases, but it is not recommended to use this in real applications. QEventLoop::exec() causes the event loop to recurse, and as such, can cause application code to also recurse. Usually, this recursion is unexpected. If this approach is used often, it's possible to end up with several layers of QEventLoop::exec() which cannot continue due to recursion.
- There may be times when you want to wait synchronously for signal to proceed eg: when implementing test cases.
- QEventLoop can be used to wait for specific event.
- The example below demonstrates how to wait for http response and prints the received response, for simplicity, the example doesn't consider the case when we wait endlessly, timer could be attached to Event loop, so that we end the loop after x time interval.
QNetworkAccessManager *networkMgr = new QNetworkAccessManager(this);
QNetworkReply *reply = networkMgr->get( QNetworkRequest( QUrl( "http://www.google.com" ) ) );
QEventLoop loop;
QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
// Execute the event loop here, now we will wait here until readyRead() signal is emitted
// which in turn will trigger event loop quit.
loop.exec();
// Lets print the HTTP GET response.
qDebug( reply->readAll());
Warning: For test cases, this approach works well. For real applications, avoid using it. It's better to split the function that wants to wait in two, and handle the signal is a separate slot.
void Class::startDownloadSlot()
{
QNetworkAccessManager networkMgr = new QNetworkAccessManager();
QNetworkReply *reply = networkMgr.get( QNetworkRequest( QUrl( "http://www.google.com" ) ) );
connect(reply, SIGNAL(readyRead()), this, SLOT(downloadFinishedSlot()));
// return to the eventloop here, signal will be emitted later, calling the slot below
}
void Class::downloadFinishedSlot()
{
// this signal is sent by QNetworkReply
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
// Lets print the HTTP GET response.
qDebug( reply->readAll());
}


Contents
Dontbugme123456 - Race Condition
There is a massive race condition in this, in BOTH of the above examples:
What happens if the data becomes available on the socket before you've bound the signal? It's unlikely, but possible. This code is not safe (And regretfully I see that it is being quoted around the internet!)dontbugme123456 04:48, 19 March 2012 (EET)
Hamishwillee - How would you re-write it?
Hi
How would you re-write it then? I agree its a race condition, but probably not a "massive" one since we're talking very little time to connect the signal while getting information back from the socket would be tens of milliseconds at best.
Regards
Hamishhamishwillee 08:11, 19 March 2012 (EET)
Aiiaiiio - Connect before GET
Not everything is clear for me about QEventLoop, but if I understand things right, you could do the following to avoid the race condition: Create and connect the event loop before calling get(). I believe, if the signal is emitted before we call loop.exec() won't cause a problem. The signal is already queued, and it will be processed immediately as we call exec().
Do I understand it right? I am doing something similar to this, and it works well, but I want to be sure.
-- TamásAiiaiiio 00:57, 17 June 2012 (EEST)
Wysota - Re: Race Condition
There is no race condition here. Qt's socket handlers read data from sockets as a result of handling events (or to be precise as a result of some code in the event dispatcher) therefore QNetworkReply can't be signalled as finished() before flow returns to the event loop.wysota 19:29, 7 December 2012 (EET)