1

I have a rather simple application that seems to deadlock under certain conditions when I invoke QEventLoop::exec. The application calls this function in two scenarios:

  • when certain data arrives on the socket
  • upon a timer event

in both cases it is used in the following context (just an http query, nothing special really):

QNetworkReply::NetworkError HttpGetMessagesStrategy::syncHttp(const QUrl url, QByteArray &dst) const
{
    QNetworkRequest request(url);
    request.setRawHeader("Cache-Control", "no-cache");

    QNetworkAccessManager mgr;
    QEventLoop eventLoop;
    QObject::connect(&mgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
    QNetworkReply *reply = mgr.get(request);
    if (reply == NULL) {
        return QNetworkReply::UnknownNetworkError;
    }

    eventLoop.exec();

    QNetworkReply::NetworkError error = reply->error();
    if (error == QNetworkReply::NoError) {
        dst += reply->readAll();
    }
    delete reply;

    return error;
}

Here's what happens when it tries to call it:

...    
#56 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
#57 0x0000003404b804a2 in ?? () from /usr/lib64/libQtCore.so.4
#58 0x0000003404b7d928 in ?? () from /usr/lib64/libQtCore.so.4
#59 0x00000033fba38f0e in g_main_context_dispatch () from /lib64/libglib-2.0.so.0
#60 0x00000033fba3c938 in ?? () from /lib64/libglib-2.0.so.0
#61 0x00000033fba3ca3a in g_main_context_iteration () from /lib64/libglib-2.0.so.0
#62 0x0000003404b7d5f3 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#63 0x0000003404b56722 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#64 0x0000003404b569ec in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#65 0x00007f41c1b4eec7 in HttpGetMessagesStrategy::syncHttp (this=<value optimized out>, url=<value optimized out>, dst=...) at HttpGetMessagesStrategy.cpp:49
#70 0x000000000040e57c in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902bb5f0) at DevicePlugin.cpp:250
#71 0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#72 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

As one can see instead of waiting for a response of the HTTP request, it picked up another event and started to process it. As a result I'm getting a huge number of frames (746) in the current thread before my application comes to a stop and then I see lines like this:

#0  0x00000033faa0efe0 in __pause_nocancel () from /lib64/libpthread.so.0
#1  0x00000033faa0917b in __pthread_mutex_lock_full () from /lib64/libpthread.so.0
#2  0x0000003404a702a3 in ?? () from /usr/lib64/libQtCore.so.4
#3  0x0000003404a6cd95 in QMutex::lock() () from /usr/lib64/libQtCore.so.4
#4  0x0000003404b57952 in QCoreApplication::postEvent(QObject*, QEvent*, int) () from /usr/lib64/libQtCore.so.4
#5  0x000000000040e293 in DevicePlugin::destroyConnection (this=0x1267390, c=0x1a85fb0) at DevicePlugin.cpp:194
#6  0x000000000040e5b6 in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902b9230) at DevicePlugin.cpp:254
#7  0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#8  0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

Could anyone kindly explain me what am I doing wrong here?

krakovjak
  • 51
  • 1
  • 7

3 Answers3

0

http://qt-project.org/doc/qt-4.8/qeventloop.html#exec

int QEventLoop::exec ( ProcessEventsFlags flags = AllEvents ) Enters the main event loop and waits until exit() is called. Returns the value that was passed to exit().

exec() is the representation of the QT event handling and is always a blocking call until the current Thread returns with exit(). This allows Threads outside of the main loop exec() to use QT Signals and Slots without conflicting.

You should to connect you finished() call with the your own handling Slot defined in your Header file.

Don't forget to inherit every class from QObject (or any other QT Object) if you want to use Signals and Slots and use the QOBJECT Makro.

pcaaron
  • 37
  • 4
0

You have two event loops running. The second event loop doesn't block the first one - it just blocks current method, so processing can't return to the first event loop. However, when the data arrives to the socket the event gets fired and your method runs again. Seems for me the only solution is to change your design to work without blocking in your syncHttp method, and rewire the whole interaction with this method using signals & slots.

As for the exec idiom, from the link you've provided:

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.

If you have an event driven application (and usually you have, if you're using Qt) and suddenly need to block like that - it means there is a design problem.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • Thanks for reply. I'll give it a shot. Although I was almost certain that this is somewhat canonical way to perform synchronous http requests: http://developer.nokia.com/community/wiki/How_to_wait_synchronously_for_a_Signal_in_Qt – krakovjak Aug 23 '14 at 03:21
  • @ArtemPervin Yes, it is a known and often used idiom. However IIRC not in these circumstances. – BartoszKP Aug 23 '14 at 09:12
0

You're trying to use QNetworkManager and QNetworkReply in a blocking manner, which it was not designed to do.

Consider using QTcpSocket instead or redesign your program to use the asynchronous mechanism.

RobbieE
  • 4,280
  • 3
  • 22
  • 36