summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2014-09-29 18:44:36 +0400
committerDenis Shienkov <denis.shienkov@gmail.com>2014-10-02 09:21:53 +0200
commitac0422e8c9e74f2275129e3c7c69ef64299f07a9 (patch)
tree5196d564d75318db03e399308c9893e6d480d4c1
parent26d61928f5a50c74f843cd88276c5b18e1bed375 (diff)
downloadqtserialport-ac0422e8c9e74f2275129e3c7c69ef64299f07a9.tar.gz
Fix reading on Windows at limited read buffer size
In case the read buffer has a limited size then are impossible to read remainder which is still can be in driver's queue, since no readyRead signal emmitted and reading are stalled. Problem is that Windows does not fire the EV_RXCHAR event in case a driver's queue has ready to read remainder; this event will be triggered only when a new data are received. The solution is to start of asynchronous read operation for reading of possible remainder from the queue after doing QSP::read() from the user. Besides is necessary to meet conditions: - do not start reading in case a reading already is started - do not start reading in case is not in limited buffer size - do not start reading in case is a previous reading returns a less data than read buffer size or are not in the hardware flow control mode Tested on Windows 8 with virtual com0com serial ports using Qt5 and then Qt4. Task-number: QTBUG-41295 Change-Id: I01797e6f8d6006751244144fead3616b1de1b811 Reviewed-by: Robert Kurjata <rkurjata@gmail.com> Reviewed-by: Sergey Belyashov <Sergey.Belyashov@gmail.com>
-rw-r--r--src/serialport/qserialport.cpp2
-rw-r--r--src/serialport/qserialport_unix.cpp5
-rw-r--r--src/serialport/qserialport_unix_p.h1
-rw-r--r--src/serialport/qserialport_win.cpp35
-rw-r--r--src/serialport/qserialport_win_p.h2
-rw-r--r--src/serialport/qserialport_wince.cpp5
-rw-r--r--src/serialport/qserialport_wince_p.h1
-rw-r--r--tests/auto/qserialport/tst_qserialport.cpp35
8 files changed, 82 insertions, 4 deletions
diff --git a/src/serialport/qserialport.cpp b/src/serialport/qserialport.cpp
index ffc763e..c815dd0 100644
--- a/src/serialport/qserialport.cpp
+++ b/src/serialport/qserialport.cpp
@@ -1343,7 +1343,7 @@ bool QSerialPort::setBreakEnabled(bool set)
qint64 QSerialPort::readData(char *data, qint64 maxSize)
{
Q_D(QSerialPort);
- return d->readBuffer.read(data, maxSize);
+ return d->readData(data, maxSize);
}
/*!
diff --git a/src/serialport/qserialport_unix.cpp b/src/serialport/qserialport_unix.cpp
index e80c0e8..b082275 100644
--- a/src/serialport/qserialport_unix.cpp
+++ b/src/serialport/qserialport_unix.cpp
@@ -383,6 +383,11 @@ void QSerialPortPrivate::startWriting()
setWriteNotificationEnabled(true);
}
+qint64 QSerialPortPrivate::readData(char *data, qint64 maxSize)
+{
+ return readBuffer.read(data, maxSize);
+}
+
bool QSerialPortPrivate::waitForReadyRead(int msecs)
{
Q_Q(QSerialPort);
diff --git a/src/serialport/qserialport_unix_p.h b/src/serialport/qserialport_unix_p.h
index 1213c30..408fdc5 100644
--- a/src/serialport/qserialport_unix_p.h
+++ b/src/serialport/qserialport_unix_p.h
@@ -120,6 +120,7 @@ public:
bool setBreakEnabled(bool set);
void startWriting();
+ qint64 readData(char *data, qint64 maxSize);
bool waitForReadyRead(int msecs);
bool waitForBytesWritten(int msecs);
diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp
index 18cef1e..3174db5 100644
--- a/src/serialport/qserialport_win.cpp
+++ b/src/serialport/qserialport_win.cpp
@@ -98,6 +98,7 @@ QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q)
, readChunkBuffer(ReadChunkSize, 0)
, readyReadEmitted(0)
, writeStarted(false)
+ , readStarted(false)
, communicationNotifier(new QWinEventNotifier(q))
, readCompletionNotifier(new QWinEventNotifier(q))
, writeCompletionNotifier(new QWinEventNotifier(q))
@@ -173,6 +174,7 @@ void QSerialPortPrivate::close()
writeCompletionNotifier->setEnabled(false);
communicationNotifier->setEnabled(false);
+ readStarted = false;
readBuffer.clear();
writeStarted = false;
@@ -267,8 +269,10 @@ bool QSerialPortPrivate::clear(QSerialPort::Directions directions)
Q_Q(QSerialPort);
DWORD flags = 0;
- if (directions & QSerialPort::Input)
+ if (directions & QSerialPort::Input) {
flags |= PURGE_RXABORT | PURGE_RXCLEAR;
+ readStarted = false;
+ }
if (directions & QSerialPort::Output) {
flags |= PURGE_TXABORT | PURGE_TXCLEAR;
writeStarted = false;
@@ -320,6 +324,21 @@ void QSerialPortPrivate::startWriting()
}
}
+qint64 QSerialPortPrivate::readData(char *data, qint64 maxSize)
+{
+ const qint64 result = readBuffer.read(data, maxSize);
+ // We need try to start async reading to read a remainder from a driver's queue
+ // in case we have a limited read buffer size. Because the read notification can
+ // be stalled since Windows do not re-triggered an EV_RXCHAR event if a driver's
+ // buffer has a remainder of data ready to read until a new data will be received.
+ if (readBufferMaxSize
+ && result > 0
+ && (result == readBufferMaxSize || flowControl == QSerialPort::HardwareControl)) {
+ startAsyncRead();
+ }
+ return result;
+}
+
bool QSerialPortPrivate::waitForReadyRead(int msecs)
{
Q_Q(QSerialPort);
@@ -523,14 +542,18 @@ bool QSerialPortPrivate::_q_completeAsyncCommunication()
bool QSerialPortPrivate::_q_completeAsyncRead()
{
const qint64 bytesTransferred = handleOverlappedResult(QSerialPort::Input, readCompletionOverlapped);
- if (bytesTransferred == qint64(-1))
+ if (bytesTransferred == qint64(-1)) {
+ readStarted = false;
return false;
+ }
if (bytesTransferred > 0) {
readBuffer.append(readChunkBuffer.left(bytesTransferred));
if (!emulateErrorPolicy())
emitReadyRead();
}
+ readStarted = false;
+
// start async read for possible remainder into driver queue
if ((bytesTransferred == ReadChunkSize) && (policy == QSerialPort::IgnorePolicy))
return startAsyncRead();
@@ -578,6 +601,9 @@ bool QSerialPortPrivate::startAsyncRead()
{
Q_Q(QSerialPort);
+ if (readStarted)
+ return true;
+
DWORD bytesToRead = policy == QSerialPort::IgnorePolicy ? ReadChunkSize : 1;
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
@@ -590,8 +616,10 @@ bool QSerialPortPrivate::startAsyncRead()
}
initializeOverlappedStructure(readCompletionOverlapped);
- if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, NULL, &readCompletionOverlapped))
+ if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, NULL, &readCompletionOverlapped)) {
+ readStarted = true;
return true;
+ }
QSerialPort::SerialPortError error = decodeSystemError();
if (error != QSerialPort::NoError) {
@@ -603,6 +631,7 @@ bool QSerialPortPrivate::startAsyncRead()
return false;
}
+ readStarted = true;
return true;
}
diff --git a/src/serialport/qserialport_win_p.h b/src/serialport/qserialport_win_p.h
index cf30c18..8db3a74 100644
--- a/src/serialport/qserialport_win_p.h
+++ b/src/serialport/qserialport_win_p.h
@@ -87,6 +87,7 @@ public:
bool setBreakEnabled(bool set);
void startWriting();
+ qint64 readData(char *data, qint64 maxSize);
bool waitForReadyRead(int msec);
bool waitForBytesWritten(int msec);
@@ -130,6 +131,7 @@ public:
QByteArray readChunkBuffer;
bool readyReadEmitted;
bool writeStarted;
+ bool readStarted;
QWinEventNotifier *communicationNotifier;
QWinEventNotifier *readCompletionNotifier;
QWinEventNotifier *writeCompletionNotifier;
diff --git a/src/serialport/qserialport_wince.cpp b/src/serialport/qserialport_wince.cpp
index c06e748..d6c58aa 100644
--- a/src/serialport/qserialport_wince.cpp
+++ b/src/serialport/qserialport_wince.cpp
@@ -351,6 +351,11 @@ void QSerialPortPrivate::startWriting()
notifyWrite();
}
+qint64 QSerialPortPrivate::readData(char *data, qint64 maxSize)
+{
+ return readBuffer.read(data, maxSize);
+}
+
bool QSerialPortPrivate::waitForReadyRead(int msec)
{
if (!readBuffer.isEmpty())
diff --git a/src/serialport/qserialport_wince_p.h b/src/serialport/qserialport_wince_p.h
index 91bba3c..be08bc1 100644
--- a/src/serialport/qserialport_wince_p.h
+++ b/src/serialport/qserialport_wince_p.h
@@ -86,6 +86,7 @@ public:
bool setBreakEnabled(bool set);
void startWriting();
+ qint64 readData(char *data, qint64 maxSize);
bool waitForReadyRead(int msec);
bool waitForBytesWritten(int msec);
diff --git a/tests/auto/qserialport/tst_qserialport.cpp b/tests/auto/qserialport/tst_qserialport.cpp
index 2b8de92..0fdde48 100644
--- a/tests/auto/qserialport/tst_qserialport.cpp
+++ b/tests/auto/qserialport/tst_qserialport.cpp
@@ -115,6 +115,10 @@ private slots:
void asynchronousWriteByTimer_data();
void asynchronousWriteByTimer();
+#ifdef Q_OS_WIN
+ void readBufferOverflow();
+#endif
+
protected slots:
void handleBytesWrittenAndExitLoopSlot(qint64 bytesWritten);
void handleBytesWrittenAndExitLoopSlot2(qint64 bytesWritten);
@@ -667,5 +671,36 @@ void tst_QSerialPort::asynchronousWriteByTimer()
QCOMPARE(receiverPort.readAll(), alphabetArray);
}
+#ifdef Q_OS_WIN
+void tst_QSerialPort::readBufferOverflow()
+{
+ clearReceiver();
+
+ QSerialPort senderPort(m_senderPortName);
+ QVERIFY(senderPort.open(QSerialPort::WriteOnly));
+
+ QSerialPort receiverPort(m_receiverPortName);
+ QVERIFY(receiverPort.open(QSerialPort::ReadOnly));
+
+ const int readBufferSize = alphabetArray.size() / 2;
+ receiverPort.setReadBufferSize(readBufferSize);
+ QCOMPARE(receiverPort.readBufferSize(), qint64(readBufferSize));
+
+ QCOMPARE(senderPort.write(alphabetArray), qint64(alphabetArray.size()));
+ QVERIFY2(senderPort.waitForBytesWritten(100), "Waiting for bytes written failed");
+
+ QByteArray readData;
+ while (receiverPort.waitForReadyRead(100)) {
+ QVERIFY(receiverPort.bytesAvailable() > 0);
+ readData += receiverPort.readAll();
+ }
+
+ QCOMPARE(readData, alphabetArray);
+
+ // No more bytes available
+ QVERIFY(receiverPort.bytesAvailable() == 0);
+}
+#endif
+
QTEST_MAIN(tst_QSerialPort)
#include "tst_qserialport.moc"