[Qt-interest] Why Bus error clientSocket->waitForConnected()?
Thiago Macieira
thiago at kde.org
Mon May 3 18:31:48 CEST 2010
Em Segunda-feira 03 Maio 2010, às 16:28:09, Kustaa Nyholm escreveu:
> >> Why? Who needs a non blocking readline? Like I've already said several
> >> times I would love to see a use case where this is needed...
> >
> > Because none of the QIODevice API is blocking, except for the waitFor
> > functions.
>
> Yes, I got that a long time ago, how about the use case?
See here:
> > The design principle is that you can call any function and expect it to
> > return immediately. That makes the API usable from an event-driven
> > application in the GUI thread, without worrying that the GUI might
> > freeze because the socket hasn't received enough bytes.
>
> So that [you can call any function and expect it to return immediately] is
> the overriding design consideration? Ok, this explains it, so it was just
> that readLine() design was hostage to this consideration?
The design decision is that methods don't block unless they are called
"waitFor". Or, put in another way, the API is asynchronous: the actual sending
and receiving of data happens in the event loop, when the socket notifiers
indicate that there is data available or that it's clear for sending.
Unlike Java, Qt does not require the use of threads for using sockets.
Non-blocking sockets have been common-place in Unix systems for 20 years or
more.
What's more, adding a "blocking mode" to the device classes was considered and
discarded in the design process. It was deemed too complex to maintain and to
understand, as well as prone to mistakes (using a blocking socket in the GUI
thread).
> > Please stop reading Qt 3.3 documentation. It's irrelevant.
>
> Sorry, I presumed that this would have been unchanged from version
> to version in the name of backwards compatibility. I'm actually using Qt
> 4.7 but routinely google for the API docs as I find it easier than using
> the Qt documentation and in my laziness I did not bother check the
> version.
Source and binary compatibility are not guaranteed across a major release (Qt
4.0). Most of the behaviour was kept, however, but we did take the opportunity
to clean up some mistakes and improve where things didn't work.
> > However, Qt 3.3 had the behaviour I wanted it to have in Qt 4. It was
> > changed in the 4.0 process and now we have to live with it.
>
> Are you going to change this back?
Maybe in Qt 5.0, which is not scheduled at all, so it will not happen before
2013.
I don't know why this specific change was made in Qt 4.0, but it was done and
even documented as such.
> > The API design is perfectly good. The API is non-blocking and that was
> > intentional.
>
> Only as far as your design criteria [non-blocking API] is satisfied,
> your users may feel and some actually do feel differently when it comes to
> readLine().
>
> Everyone is entitled to their opinion I guess.
Of course.
> > If the device is a file or all data is available (such as a closed
> > socket,
> >
> > finished process or finished QNetworkReply):
> > QByteArray line;
> > while (!(line = device.readLine()).isNull())
> >
> > processLine(line);
>
> For that to work, I as a coder, need to know and care if it is a file
> or close socket or what have you ... doesn't sound good. Surely I
> should be able to write code for something as simple as this that does
> not need to care.
Well, yes. If you find out that there's no more data available, you must be
able to decide whether that means there will never be more data available
(closed socket, finished reply, exited sub-process, etc.) or whether some more
may arrive in the future.
And without blocking, otherwise your GUI would freeze.
> > Or if you insist in doing it the blocking way:
> Well, that was the requirement in the OP's problem and in
> many cases that makes perfect sense.
Indeed it does, but it's technically limited to one socket per thread. As soon
as you need two, this breaks.
Also note that, unlike low-level BSD sockets, Qt takes care of writing more
bytes to the socket while waiting for incoming bytes to arrive. That is, if
both ends of the connection try to send big buffers to one another, with low-
level blocking sockets they'd deadlock: someone must read off the kernel buffers
so that more bytes are received.
Qt's "blocking" waitForReadyRead() and waitForBytesWritten() APIs are done on
top of non-blocking sockets and use a proper select(2) loop.
> > while (device.waitForReadyRead(-1)) {
> > while (device.canReadLine())
> > processLine(device.readLine());
> > }
> > if (device.bytesAvailable())
> > processLine(device.readLine()); /* device finished without a
> > newline
>
> Ok, more or less what I sketched, and demonstrates the main issue of
> having a non blocking readLine() in that the processLine needs to be
> presented twice preventing coding of that processing
> in-line unless I want to copy/paste the code. Not a big deal,
> but shows that from the readLine() point of view the API design
> leaves something to desire for, but I know understand that Qt's
> overall design goal is/was non blocking API.
Well, if *that* is your problem:
bool deviceIsOpen = true;
device.write(largeChunkOfData);
while (deviceIsOpen || device.bytesAvailable()) {
while (device.canReadLine() ||
(!deviceIsOpen && device.bytesAvailable())
processLine(device.readLine());
if (deviceIsOpen)
deviceIsOpen = device.waitForReadyRead(-1);
}
This makes the loop run one more time, after the device disconnected. And
after it has disconnected, processLine is called one more time after
canReadLine() returns false (as long as there are still some bytes available).
Note the writing of a large chunk of data above. Like I explained before,
waitForReadyRead() *also* writes to the device as the opportunity presents
itself. If we had made a blocking readLine(), we'd have had to make it use
waitForReadyRead() just like it was done above. Or like this:
QByteArray blockingReadLine(QIODevice *device)
{
while (!device->canReadLine() && device->waitForReadyRead(-1))
;
return device->readLine();
}
I'll leave the addition of a timeout parameter as an exercise to the reader
(use QElapsedTimer). Note that waitForReadyRead() can return true but a line
is still not readable.
With this function, your loop with a socket would be as simple as:
do {
processLine(blockingReadLine(socket));
} while (socket->state() == QTcpSocket::ConnectedState);
Unfortunately, QIODevice::isOpen is not suitable for this condition. A socket
can be open but disconnected, a reply can be open and finished, etc.
> > Please stop reading Qt 3.3 documentation. It's waitForReadyRead in Qt 4.
>
> Ok, I see that the problems with 3.3 documentation have been mostly
> fixed in Qt 4 API docs. Great.
Good to know. :-)
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Senior Product Manager - Nokia, Qt Development Frameworks
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 190 bytes
Desc: This is a digitally signed message part.
Url : http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20100503/95198366/attachment.bin
More information about the Qt-interest-old
mailing list