Don't indefinitely wait for data if it was able to read some data

Passing -1 to waitForReadyRead() may cause it to wait for some time
but the data retrieved may be enough for processing. So if 0 is passed
from read, indicating that there is potentially more to come, then
it will do a waitForReadyRead() then for more data to come.

Change-Id: I75f270d1f124ecc12b18512cc20fb11f7a88f02e
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Andy Shaw 2017-07-11 09:07:51 +02:00
parent a69a0e8254
commit 1d9547c9a4
2 changed files with 86 additions and 12 deletions

View File

@ -1265,18 +1265,8 @@ void QXmlInputSource::fetchData()
} else if (device->isOpen() || device->open(QIODevice::ReadOnly)) {
rawData.resize(BufferSize);
qint64 size = device->read(rawData.data(), BufferSize);
if (size != -1) {
// We don't want to give fromRawData() less than four bytes if we can avoid it.
while (size < 4) {
if (!device->waitForReadyRead(-1))
break;
int ret = device->read(rawData.data() + size, BufferSize - size);
if (ret <= 0)
break;
size += ret;
}
}
if (size == 0 && device->waitForReadyRead(-1))
size = device->read(rawData.data(), BufferSize);
rawData.resize(qMax(qint64(0), size));
}

View File

@ -48,6 +48,7 @@ private slots:
void reset() const;
void resetSimplified() const;
void waitForReadyIODevice() const;
void inputFromSlowDevice() const;
};
/*!
@ -207,5 +208,88 @@ void tst_QXmlInputSource::waitForReadyIODevice() const
QVERIFY(sv.success);
}
// This class is used to emulate a case where less than 4 bytes are sent in
// a single packet to ensure it is still parsed correctly
class SlowIODevice : public QIODevice
{
public:
SlowIODevice(const QString &expectedData, QObject *parent = 0)
: QIODevice(parent), currentPos(0), readyToSend(true)
{
stringData = expectedData.toUtf8();
dataTimer = new QTimer(this);
connect(dataTimer, &QTimer::timeout, [=]() {
readyToSend = true;
emit readyRead();
dataTimer->stop();
});
dataTimer->start(1000);
}
bool open(SlowIODevice::OpenMode) override
{
setOpenMode(ReadOnly);
return true;
}
bool isSequential() const override
{
return true;
}
qint64 bytesAvailable() const override
{
if (readyToSend && stringData.size() != currentPos)
return qMax(3, stringData.size() - currentPos);
return 0;
}
qint64 readData(char *data, qint64 maxSize) override
{
if (!readyToSend)
return 0;
const qint64 readSize = qMin(qMin((qint64)3, maxSize), (qint64)(stringData.size() - currentPos));
if (readSize > 0)
memcpy(data, &stringData.constData()[currentPos], readSize);
currentPos += readSize;
readyToSend = false;
if (currentPos != stringData.size())
dataTimer->start(1000);
return readSize;
}
qint64 writeData(const char *, qint64) override { return 0; }
bool waitForReadyRead(int msecs) override
{
// Delibrately wait a maximum of 10 seconds for the sake
// of the test, so it doesn't unduly hang
const int waitTime = qMax(10000, msecs);
QTime t;
t.start();
while (t.elapsed() < waitTime) {
QCoreApplication::processEvents();
if (readyToSend)
return true;
}
return false;
}
private:
QByteArray stringData;
int currentPos;
bool readyToSend;
QTimer *dataTimer;
};
void tst_QXmlInputSource::inputFromSlowDevice() const
{
QString expectedData = QStringLiteral("<foo><bar>kake</bar><bar>ja</bar></foo>");
SlowIODevice slowDevice(expectedData);
QXmlInputSource source(&slowDevice);
QString data;
while (true) {
const QChar nextChar = source.next();
if (nextChar == QXmlInputSource::EndOfDocument)
break;
else if (nextChar != QXmlInputSource::EndOfData)
data += nextChar;
}
QCOMPARE(data, expectedData);
}
QTEST_MAIN(tst_QXmlInputSource)
#include "tst_qxmlinputsource.moc"