[Qt-interest] QProcess could close the reading pipe, thus killing the child process with SIGPIPE

Thiago Macieira thiago.macieira at trolltech.com
Sun May 3 18:21:32 CEST 2009


Jan Kundrát wrote:
>Thiago Macieira wrote:
>> It's not a problem.
>>
>> That slot should not be called unless there's something to be read. If
>> there's a read of 0, that means the pipe has closed. So the code is
>> right.
>
>Hi Thiago,
>OK, that makes sense. I've tried to go through the Qt sources to a call
>to select() or poll(), but it's probably buried deep inside the glib
>event loop, so I gave up. Let's assume for a while that the OS is
>correct for a while.

You can also run your application with QT_NO_GLIB=1, which will make the 
Qt event loop system avoid glib and instead use its own implementation, 
which you can find in src/corelib/kernel (the QAbstractEventDispatcher and 
QEventDispatcherUnix classes). The main thread in a GUI program will use 
the X11 dispatcher (src/gui/kernel), but that's just for handling the X11 
socket and its events.

>This QProcess is created in a separate thread. This thread has its own
>event loop and I'm not touching the process in any other thread at all.
>I've attached the relevant code, but let me explain what it is supposed
>to do:

You're talking about QProcess here, but your code is actually talking 
about socket.

Not that it should matter because the behaviour should be the same. But 
QAbstractSocket has a lot more testing and workarounds for broken systems 
than QProcess does. For example, some systems are known to produce 
spurious read events (Windows) even when there's nothing to be read.

Linux is actually fairly well behaved in that aspect.

>* There's a QueuedConnection between the readyReady() and one method

Shouldn't be needed. Read the description of the "Won't fix" in the task 
217111.

>* The processLine is a bit more complex; due to the nature of the
>protocol in question, we don't know whether we will have to read some
>more data before that line is processed. That means that from inside a
>(queued) slot connected to readyRead, we sometimes call
>waitForReadyRead(), read(size) and readLine(). Is that safe?

It's safe in an unencrypted socket, but not in a process. Again, I refer 
to the description in task 217111: there's a workaround for buggy user 
code that does waitForReadyRead inside the readyRead slot in 
QAbstractSocket. That code is not present in QProcess and is the same code 
that makes waitForReadyRead in QSslSocket impossible.

So you have three different behaviour, depending on the task, due to this 
workaround:
 - QTcpSocket: waitForReadyRead inside readyRead() works, but doesn't 
actually emit the signal
 - QProcess: waitForReadyRead inside readyRead() works *and* emits the 
signal, which means your slot will recurse (this is the original and 
intended behaviour)
 - QSslSocket: waitForReadyRead inside readyRead() does not work and will 
lock up (because QTcpSocket doesn't emit readyRead())

So here it's important to know which class you're using.

If you don't want to handle the difference, you should do as I described in 
task 217111: if you don't have enough data, simply return and wait for 
readyRead() to be emitted again with more data.

If you don't want to do your own buffering, you can use the peek() function 
to read the data but not discard from the QIODevice buffers. In the context 
of an IMAP parser, I'd use readLine() and store that line only. If there 
are any continuations, then you return and wait for more data, until all 
data has been received (you know the number of bytes).

>If there's no bug in my OS, I'd guess that somehow I managed to read the
>data that interrupted the select() deep inside the event loop. However,
>I don't see how that could happen, given that the event loop should run
>in the same thread as my handler (right?). Is that a possible scenario?

No. From your initial description, the select loop got a read activation 
on the file descriptor, but when checking the number of bytes, we got 0. 
(Note that the code didn't actually try to read, but simply did an 
FIONREAD) The only case in which a zero-read doesn't mean EOF is that of 
zero-length UDP datagrams.

A pipe or a TCP socket are stream devices. They never produce zero-reads 
unless it's EOF.

From your description, I can only think of bug in the OS. If you can 
reproduce this, let me know. But I need a small testcase for that. 

The code you posted seemed right at first glance, except for the 
waitForReadyRead calls (and the use of a thread in the first place). The 
use of a thread points to the only other possibility here: there's a 
threading mismatch and the QIODevice object *is* actually being touched 
from outside its thread. I'm going to take a quick look at your code.


-- 
Thiago Macieira - thiago.macieira (AT) nokia.com
  Senior Product Manager - Nokia, Qt Software
      Sandakerveien 116, NO-0402 Oslo, Norway
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part.
Url : http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20090503/2a8fd43e/attachment.bin 


More information about the Qt-interest-old mailing list