PDA

View Full Version : Program hangs up in QTcpSocket::readLine()



vfernandez
20th February 2007, 21:13
I have a GUI thread and a worker thread which reads from a socket. The worker thread uses the readyRead() signal to read input from the socket, process it and emit some signals that are connected to the GUI thread using a queued connection. The problem is the application sometimes just randomly blocks. Using gdb I've found just the exact place where it blocks and it seems to be just in the Qt code, which is quite strange. I could understand the worker thread getting blocked if there was no more available data in the socket ( although that shouldn't happen since I use QTcpSocket::canReadLine() ) but the GUI is getting blocked, thus not processing events and causing the typical efects of not repainting when it's needed.

This is the relevant code of the worker thread:


void ManagerThread::run()
{
m_mutex.lock();
QString hostName = m_hostName;
quint16 port = m_port;
m_mutex.unlock();

m_socket = new QTcpSocket;
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(handleConnectionError(QAbstractSocket::Socket Error)));

m_socket->connectToHost(hostName, port);
if (!m_socket->waitForConnected()) {
delete m_socket;
return;
}

if(sendLogin()) {
connect(m_socket, SIGNAL(readyRead()),
this, SLOT(readInput()));
setConnected(true);
emit connected();
exec();
setConnected(false);
}

m_socket->disconnectFromHost();

delete m_socket;
}


void ManagerThread::readInput()
{
bool timerStarted = false;

while(m_socket->canReadLine()) {
QString line = m_socket->readLine();
line.remove('\r');
line.remove('\n');
if(line.isEmpty()) {
m_pendingCommands.append(m_currentCommand);
m_currentCommand.clear();

if(!timerStarted) {
QTimer::singleShot(0, this, SLOT(parsePendingCommands()));
timerStarted = true;
}
} else
m_currentCommand.append(line);
}
}

It hangs in the m_socket->readLine() call. When it hangs, I press Ctrl+C and this is the backtrace I get:


(gdb) run
Starting program: /home/djworld/curro/dialapplet/dialapplet/bin/dialapplet
Failed to read a valid object file image from memory.
[Thread debugging using libthread_db enabled]
[New Thread -1224792368 (LWP 10926)]
Qt: gdb: -nograb added to command-line options.
Use the -dograb option to enforce grabbing.
[New Thread -1228108912 (LWP 10929)]
[New Thread -1236501616 (LWP 10930)]
[New Thread -1248855152 (LWP 12367)]

Program received signal SIGINT, Interrupt.
[Switching to Thread -1224792368 (LWP 10926)]
0xb7463b06 in QRingBuffer::readPointer (this=0x80e8694)
at ../../include/QtCore/private/../../../src/corelib/tools/qringbuffer_p.h:54
54 ../../include/QtCore/private/../../../src/corelib/tools/qringbuffer_p.h:
File or directory doesn't exist.

in ../../include/QtCore/private/../../../src/corelib/tools/qringbuffer_p.h
(gdb) bt
#0 0xb7463b06 in QRingBuffer::readPointer (this=0x80e8694)
at ../../include/QtCore/private/../../../src/corelib/tools/qringbuffer_p.h:54
#1 0xb7475494 in QAbstractSocket::readData (this=0x8164370,
data=0x81f6418 "_madrugada\nQueue: Cola_madrugLocation:
SIP/101\naMembership:Penalty: 0\n stCallsTaken: LastCall: Status: 6\nPEvent:
QueueMemberStaPrivilege: aQueue: Cola\ngent,aMembership:CallsTaken:
26\ntic\nl\nStatus: 6\nP"..., maxSize=16384) at qabstractsocket.cpp:1699
#2 0xb732701a in QIODevice::read (this=0x8164370, data=0xbf9e8d2a "\236�I ",
maxSize=1) at io/qiodevice.cpp:768
#3 0xb73183d6 in QIODevice::getChar (this=0x8164370, c=0xbf9e8d77 "\b")
at ../../include/QtCore/../../src/corelib/io/qiodevice.h:116
#4 0xb7327590 in QIODevice::readLineData (this=0x8164370,
data=0x81d5570 "0\024Y]\001", maxSize=4063) at io/qiodevice.cpp:1112
#5 0xb74751f8 in QAbstractSocket::readLineData (this=0x8164370,
data=0x81d5570 "0\024Y]\001", maxlen=4063) at qabstractsocket.cpp:1719
#6 0xb732689d in QIODevice::readLine (this=0x8164370, data=0x81d5550 "Event:
QueueMemberStaPrivilege: 0\024Y]\001", maxSize=4095) at io/qiodevice.cpp:1013
#7 0xb7326b10 in QIODevice::readLine (this=0x8164370, maxSize=0) at
io/qiodevice.cpp:1081
#8 0x08052e01 in ManagerThread::readInput (this=0x8141408) at
managerthread.cpp:103
#9 0x08073bfd in ManagerThread::qt_metacall (this=0x8141408,
_c=QMetaObject::InvokeMetaMethod, _id=8, _a=0xb5900648) at
moc_managerthread.cpp:95
#10 0xb7399b2a in QObject::event (this=0x8141408, e=0xb5900668) at
kernel/qobject.cpp:1025
#11 0xb77ed7e1 in QApplicationPrivate::notify_helper (this=0x8095278,
receiver=0x8141408, e=0xb5900668) at kernel/qapplication.cpp:3434
#12 0xb77edada in QApplication::notify (this=0xbf9e9774, receiver=0x8141408,
e=0xb5900668) at kernel/qapplication.cpp:3009
#13 0xb738936b in QCoreApplication::sendEvent (receiver=0x8141408,
event=0xb5900668) at kernel/qcoreapplication.h:183
#14 0xb7386ce0 in QCoreApplication::sendPostedEvents (receiver=0x0,
event_type=0) at kernel/qcoreapplication.cpp:1021
#15 0xb7888f20 in QCoreApplication::sendPostedEvents ()
at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:188
#16 0xb788837e in QEventDispatcherX11::processEvents (this=0x809ca00,
flags=@0xbf9e95ac) at kernel/qeventdispatcher_x11.cpp:55
#17 0xb7383f06 in QEventLoop::processEvents (this=0xbf9e963c,
flags=@0xbf9e95f0) at kernel/qeventloop.cpp:126
#18 0xb7384089 in QEventLoop::exec (this=0xbf9e963c, flags=@0xbf9e9644) at
kernel/qeventloop.cpp:172
#19 0xb73875b0 in QCoreApplication::exec () at kernel/qcoreapplication.cpp:727
#20 0xb77ed364 in QApplication::exec () at kernel/qapplication.cpp:2927
#21 0x0806138c in main (argc=Cannot access memory at address 0x0
) at main.cpp:50

Any idea? I'm using Qt 4.2.2 on openSUSE 10.2.

Bitto
20th February 2007, 21:28
Are you able to reproduce this reliably? If so, can you please post a complete example that reproduces it?

Off the top of my head, it might look like the slot you're invoking in your QThread subclass is run from the thread that created it, rather than the thread that runs run(). Tongue-in-cheek, try creating a QTcpSocket subclass that handles its own readyReads, instead of declaring the slot in the thread class. ;-)

jacek
20th February 2007, 21:40
Try:
connect( m_socket, SIGNAL( readyRead() ), this, SLOT( readInput() ), Qt::DirectConnection );Otherwise readInput() will be invoked by the GUI thread (as "this" lives in the GUI thread and you create a queued connection).

jpn
20th February 2007, 21:40
"This" aka ManagerThread lives in the thread it was created. Mark the connections explicitly as direct to get the slots executed in the same thread than what's being executed in ManagetThread::run():


connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(handleConnectionError(QAbstractSocket::Socket Error)), Qt::DirectConnection);
...
connect(m_socket, SIGNAL(readyRead()), this, SLOT(readInput()), Qt::DirectConnection);

vfernandez
21st February 2007, 12:36
Thanks! That was exactly the problem. I would never have thought the readInput() function was being run in the gui thread but I've inserted qDebug() << QThread::currentThread(); in it and damn! that was happening. It would be nice if the Qt documentation warned about this thing. IMHO, it's a bit confusing at this point.