From ac0422e8c9e74f2275129e3c7c69ef64299f07a9 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 29 Sep 2014 18:44:36 +0400 Subject: 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 Reviewed-by: Sergey Belyashov --- src/serialport/qserialport.cpp | 2 +- src/serialport/qserialport_unix.cpp | 5 +++++ src/serialport/qserialport_unix_p.h | 1 + src/serialport/qserialport_win.cpp | 35 ++++++++++++++++++++++++++++++++--- src/serialport/qserialport_win_p.h | 2 ++ src/serialport/qserialport_wince.cpp | 5 +++++ src/serialport/qserialport_wince_p.h | 1 + 7 files changed, 47 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/serialport/qserialport.cpp b/src/serialport/qserialport.cpp index ffc763e3..c815dd02 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 e80c0e89..b082275b 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 1213c30a..408fdc50 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 18cef1ea..3174db56 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 cf30c189..8db3a74d 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 c06e748c..d6c58aab 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 91bba3c7..be08bc15 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); -- cgit v1.2.3