From 37a75436aa79f01c51ea458a6fd9b82e0340d0a4 Mon Sep 17 00:00:00 2001 From: David Pinheiro Date: Fri, 6 Jun 2014 10:07:32 +0200 Subject: [PATCH] Use the QWinOverlappedIoNotifier instead of the QWinEventNotifier https://bugreports.qt-project.org/browse/QTBUG-34946 Change-Id: I344b5ce023f0ee5996eb74a328b67108ab10657f --- src/serialport/qserialport.h | 4 +- src/serialport/qserialport_win.cpp | 272 +++++++++++++++---------------------- src/serialport/qserialport_win_p.h | 22 +-- 3 files changed, 122 insertions(+), 176 deletions(-) diff --git a/src/serialport/qserialport.h b/src/serialport/qserialport.h index ae5eaf3..48eed43 100644 --- a/src/serialport/qserialport.h +++ b/src/serialport/qserialport.h @@ -282,9 +282,7 @@ private: Q_DISABLE_COPY(QSerialPort) #if defined (Q_OS_WIN32) || defined(Q_OS_WIN64) - Q_PRIVATE_SLOT(d_func(), void _q_completeAsyncCommunication()) - Q_PRIVATE_SLOT(d_func(), void _q_completeAsyncRead()) - Q_PRIVATE_SLOT(d_func(), void _q_completeAsyncWrite()) + Q_PRIVATE_SLOT(d_func(), void _q_notified(quint32, quint32, OVERLAPPED*)) #endif }; diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp index 71521af..f5e3a19 100644 --- a/src/serialport/qserialport_win.cpp +++ b/src/serialport/qserialport_win.cpp @@ -47,7 +47,8 @@ #include #include #include -#include +#include + #include #ifndef CTL_CODE @@ -83,13 +84,6 @@ QT_BEGIN_NAMESPACE -static void initializeOverlappedStructure(OVERLAPPED &overlapped) -{ - overlapped.Internal = 0; - overlapped.InternalHigh = 0; - overlapped.Offset = 0; - overlapped.OffsetHigh = 0; -} QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) : QSerialPortPrivateData(q) @@ -98,39 +92,17 @@ QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) , readChunkBuffer(ReadChunkSize, 0) , readyReadEmitted(0) , writeStarted(false) - , communicationNotifier(new QWinEventNotifier(q)) - , readCompletionNotifier(new QWinEventNotifier(q)) - , writeCompletionNotifier(new QWinEventNotifier(q)) + , notifier(new QWinOverlappedIoNotifier(q)) , startAsyncWriteTimer(0) , originalEventMask(0) , triggeredEventMask(0) { - ::ZeroMemory(&communicationOverlapped, sizeof(communicationOverlapped)); - communicationOverlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); - if (!communicationOverlapped.hEvent) - q->setError(decodeSystemError()); - else { - communicationNotifier->setHandle(communicationOverlapped.hEvent); - q->connect(communicationNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_completeAsyncCommunication())); - } - + ::ZeroMemory(&communicationOverlapped, sizeof(communicationOverlapped)); ::ZeroMemory(&readCompletionOverlapped, sizeof(readCompletionOverlapped)); - readCompletionOverlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); - if (!readCompletionOverlapped.hEvent) - q->setError(decodeSystemError()); - else { - readCompletionNotifier->setHandle(readCompletionOverlapped.hEvent); - q->connect(readCompletionNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_completeAsyncRead())); - } - ::ZeroMemory(&writeCompletionOverlapped, sizeof(writeCompletionOverlapped)); - writeCompletionOverlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); - if (!writeCompletionOverlapped.hEvent) - q->setError(decodeSystemError()); - else { - writeCompletionNotifier->setHandle(writeCompletionOverlapped.hEvent); - q->connect(writeCompletionNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_completeAsyncWrite())); - } + + q->connect(notifier, SIGNAL(notified(quint32, quint32, OVERLAPPED*)), + q, SLOT(_q_notified(quint32, quint32, OVERLAPPED*))); } bool QSerialPortPrivate::open(QIODevice::OpenMode mode) @@ -187,13 +159,7 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode) if (!updateCommTimeouts()) return false; - - if (mode & QIODevice::ReadOnly) - readCompletionNotifier->setEnabled(true); - - if (mode & QIODevice::WriteOnly) - writeCompletionNotifier->setEnabled(true); - + if (!::SetCommMask(handle, originalEventMask)) { q->setError(decodeSystemError()); return false; @@ -202,7 +168,8 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode) if (!startAsyncCommunication()) return false; - communicationNotifier->setEnabled(true); + notifier->setHandle(handle); + notifier->setEnabled(true); return true; } @@ -214,10 +181,9 @@ void QSerialPortPrivate::close() if (!::CancelIo(handle)) q->setError(decodeSystemError()); - readCompletionNotifier->setEnabled(false); - writeCompletionNotifier->setEnabled(false); - communicationNotifier->setEnabled(false); - + + notifier->setEnabled(false); + readBuffer.clear(); writeStarted = false; @@ -321,9 +287,9 @@ bool QSerialPortPrivate::flush() } bool QSerialPortPrivate::clear(QSerialPort::Directions directions) -{ - Q_Q(QSerialPort); - +{ + Q_Q(QSerialPort); + DWORD flags = 0; if (directions & QSerialPort::Input) flags |= PURGE_RXABORT | PURGE_RXCLEAR; @@ -366,22 +332,12 @@ bool QSerialPortPrivate::setBreakEnabled(bool set) void QSerialPortPrivate::startWriting() { - Q_Q(QSerialPort); - - if (!writeStarted) { - if (!startAsyncWriteTimer) { - startAsyncWriteTimer = new QTimer(q); - q->connect(startAsyncWriteTimer, SIGNAL(timeout()), q, SLOT(_q_completeAsyncWrite())); - startAsyncWriteTimer->setSingleShot(true); - } - startAsyncWriteTimer->start(0); - } + if (!writeStarted) + startAsyncWrite(); } bool QSerialPortPrivate::waitForReadyRead(int msecs) -{ - Q_Q(QSerialPort); - +{ QElapsedTimer stopWatch; stopWatch.start(); @@ -392,25 +348,20 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs) bool timedOut = false; HANDLE triggeredEvent = 0; - if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &triggeredEvent) || !triggeredEvent) { - // This is occur timeout or another error - if (!timedOut) - q->setError(decodeSystemError()); + if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()))) { return false; } - if (triggeredEvent == communicationOverlapped.hEvent) { - _q_completeAsyncCommunication(); - } else if (triggeredEvent == readCompletionOverlapped.hEvent) { - _q_completeAsyncRead(); + if (communicationOverlapped.InternalHigh > 0) { + completeAsyncCommunication(); + } else if (writeCompletionOverlapped.InternalHigh > 0) { + completeAsyncWrite(writeCompletionOverlapped.InternalHigh); + } else { + completeAsyncRead(readCompletionOverlapped.InternalHigh); if (qint64(readBuffer.size()) != currentReadBufferSize) currentReadBufferSize = readBuffer.size(); else if (initialReadBufferSize != currentReadBufferSize) return true; - } else if (triggeredEvent == writeCompletionOverlapped.hEvent) { - _q_completeAsyncWrite(); - } else { - return false; } } while (msecs == -1 || timeoutValue(msecs, stopWatch.elapsed()) > 0); @@ -420,39 +371,25 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs) bool QSerialPortPrivate::waitForBytesWritten(int msecs) { - Q_Q(QSerialPort); - - if (writeBuffer.isEmpty()) - return false; + //Q_Q(QSerialPort); + QElapsedTimer stopWatch; stopWatch.start(); - if (!writeStarted) - startAsyncWrite(); - - forever { - bool timedOut = false; - HANDLE triggeredEvent = 0; - - if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &triggeredEvent) || !triggeredEvent) { - if (!timedOut) - q->setError(decodeSystemError()); + do { + if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()))) return false; - } - if (triggeredEvent == communicationOverlapped.hEvent) { - _q_completeAsyncRead(); - } else if (triggeredEvent == readCompletionOverlapped.hEvent) { - _q_completeAsyncRead(); - } else if (triggeredEvent == writeCompletionOverlapped.hEvent) { - _q_completeAsyncWrite(); - return writeBuffer.isEmpty(); + if (writeCompletionOverlapped.InternalHigh > 0) { + completeAsyncWrite(writeCompletionOverlapped.InternalHigh); + return true; + } else if (communicationOverlapped.InternalHigh > 0) { + completeAsyncCommunication(); } else { - return false; + completeAsyncRead(readCompletionOverlapped.InternalHigh); } - - } + } while (msecs == -1 || timeoutValue(msecs, stopWatch.elapsed()) > 0); return false; } @@ -556,14 +493,11 @@ bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy) return true; } -void QSerialPortPrivate::_q_completeAsyncCommunication() +void QSerialPortPrivate::completeAsyncCommunication() { Q_Q(QSerialPort); - DWORD numberOfBytesTransferred = 0; - - if (!::GetOverlappedResult(handle, &communicationOverlapped, &numberOfBytesTransferred, FALSE)) - q->setError(decodeSystemError()); + ::ZeroMemory(&communicationOverlapped, sizeof(communicationOverlapped)); bool error = false; @@ -590,56 +524,47 @@ void QSerialPortPrivate::_q_completeAsyncCommunication() startAsyncRead(); } -void QSerialPortPrivate::_q_completeAsyncRead() -{ - Q_Q(QSerialPort); +void QSerialPortPrivate::completeAsyncRead(DWORD numberOfBytes) +{ - DWORD numberOfBytesTransferred = 0; - if (!::GetOverlappedResult(handle, &readCompletionOverlapped, &numberOfBytesTransferred, FALSE)) - q->setError(decodeSystemError()); + ::ZeroMemory(&readCompletionOverlapped, sizeof(readCompletionOverlapped)); - if (numberOfBytesTransferred > 0) { + if (numberOfBytes > 0) { - readBuffer.append(readChunkBuffer.left(numberOfBytesTransferred)); + readBuffer.append(readChunkBuffer.left(numberOfBytes)); if (!emulateErrorPolicy()) emitReadyRead(); } // start async read for possible remainder into driver queue - if ((numberOfBytesTransferred == ReadChunkSize) && (policy == QSerialPort::IgnorePolicy)) + if ((numberOfBytes > 0) && (policy == QSerialPort::IgnorePolicy)) startAsyncRead(); else // driver queue is emplty, so startup wait comm event startAsyncCommunication(); } -void QSerialPortPrivate::_q_completeAsyncWrite() +void QSerialPortPrivate::completeAsyncWrite(DWORD numberOfBytes) { Q_Q(QSerialPort); - if (writeStarted) { - writeStarted = false; - DWORD numberOfBytesTransferred = 0; - if (!::GetOverlappedResult(handle, &writeCompletionOverlapped, &numberOfBytesTransferred, FALSE)) { - numberOfBytesTransferred = 0; - q->setError(decodeSystemError()); - return; - } + ::ZeroMemory(&writeCompletionOverlapped, sizeof(writeCompletionOverlapped)); - if (numberOfBytesTransferred > 0) { - writeBuffer.free(numberOfBytesTransferred); - emit q->bytesWritten(numberOfBytesTransferred); - } - } + writeBuffer.free(numberOfBytes); - startAsyncWrite(); + if (numberOfBytes > 0) + emit q->bytesWritten(numberOfBytes); + + if (writeBuffer.isEmpty()) + writeStarted = false; + else + startAsyncWrite(); } bool QSerialPortPrivate::startAsyncCommunication() { Q_Q(QSerialPort); - initializeOverlappedStructure(communicationOverlapped); if (!::WaitCommEvent(handle, &triggeredEventMask, &communicationOverlapped)) { const QSerialPort::SerialPortError error = decodeSystemError(); if (error != QSerialPort::NoError) { @@ -664,8 +589,7 @@ bool QSerialPortPrivate::startAsyncRead() return false; } } - - initializeOverlappedStructure(readCompletionOverlapped); + if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, NULL, &readCompletionOverlapped)) return true; @@ -685,24 +609,29 @@ bool QSerialPortPrivate::startAsyncWrite() { Q_Q(QSerialPort); - if (writeBuffer.isEmpty() || writeStarted) + qint64 nextSize = writeBuffer.nextDataBlockSize(); + const char *ptr = writeBuffer.readPointer(); + + // no more data to write + if (!ptr || nextSize == 0) return true; - initializeOverlappedStructure(writeCompletionOverlapped); - if (!::WriteFile(handle, writeBuffer.readPointer(), - writeBuffer.nextDataBlockSize(), - NULL, &writeCompletionOverlapped)) { + writeStarted = true; - QSerialPort::SerialPortError error = decodeSystemError(); - if (error != QSerialPort::NoError) { - if (error != QSerialPort::ResourceError) - error = QSerialPort::WriteError; - q->setError(error); - return false; - } + if (::WriteFile(handle, ptr, nextSize, NULL, &writeCompletionOverlapped)) + return true; + + QSerialPort::SerialPortError error = decodeSystemError(); + if (error != QSerialPort::NoError) { + writeStarted = false; + + if (error != QSerialPort::ResourceError) + error = QSerialPort::WriteError; + + q->setError(error); + return false; } - writeStarted = true; return true; } @@ -742,6 +671,26 @@ void QSerialPortPrivate::emitReadyRead() emit q->readyRead(); } +void QSerialPortPrivate::_q_notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *notifiedOverlapped) +{ + Q_Q(QSerialPort); + + const QSerialPort::SerialPortError error = decodeSystemErrorCode(errorCode); + if (error != QSerialPort::NoError) { + q->setError(error); + return; + } + + if (notifiedOverlapped == &communicationOverlapped) + completeAsyncCommunication(); + else if (notifiedOverlapped == &readCompletionOverlapped) + completeAsyncRead(numberOfBytes); + else if (notifiedOverlapped == &writeCompletionOverlapped) + completeAsyncWrite(numberOfBytes); + else + q->setError(QSerialPort::ResourceError); +} + void QSerialPortPrivate::handleLineStatusErrors() { Q_Q(QSerialPort); @@ -786,10 +735,13 @@ bool QSerialPortPrivate::updateCommTimeouts() return true; } -QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemErrorCode( DWORD errorCode) const { QSerialPort::SerialPortError error; - switch (::GetLastError()) { + switch (errorCode) { + case ERROR_SUCCESS: + error = QSerialPort::NoError; + break; case ERROR_IO_PENDING: error = QSerialPort::NoError; break; @@ -824,31 +776,25 @@ QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const return error; } -bool QSerialPortPrivate::waitAnyEvent(int msecs, bool *timedOut, HANDLE *triggeredEvent) +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const { - Q_Q(QSerialPort); - - Q_ASSERT(timedOut); + return decodeSystemErrorCode(::GetLastError()); +} - QVector handles = QVector() - << communicationOverlapped.hEvent - << readCompletionOverlapped.hEvent - << writeCompletionOverlapped.hEvent; +bool QSerialPortPrivate::waitAnyEvent(int msecs) +{ + Q_Q(QSerialPort); - DWORD waitResult = ::WaitForMultipleObjects(handles.count(), - handles.constData(), - FALSE, // wait any event - msecs == -1 ? INFINITE : msecs); + DWORD waitResult = ::WaitForSingleObject(handle, msecs == -1 ? INFINITE : msecs); if (waitResult == WAIT_TIMEOUT) { - *timedOut = true; q->setError(QSerialPort::TimeoutError); return false; } - if (waitResult >= DWORD(WAIT_OBJECT_0 + handles.count())) + if (waitResult != DWORD(WAIT_OBJECT_0)) { + q->setError(decodeSystemError()); return false; - - *triggeredEvent = handles.at(waitResult - WAIT_OBJECT_0); + } return true; } diff --git a/src/serialport/qserialport_win_p.h b/src/serialport/qserialport_win_p.h index 4e66685..0986440 100644 --- a/src/serialport/qserialport_win_p.h +++ b/src/serialport/qserialport_win_p.h @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE -class QWinEventNotifier; +class QWinOverlappedIoNotifier; class QTimer; class QSerialPortPrivate : public QSerialPortPrivateData @@ -88,12 +88,14 @@ public: bool setFlowControl(QSerialPort::FlowControl flowControl); bool setDataErrorPolicy(QSerialPort::DataErrorPolicy policy); - void handleLineStatusErrors(); + void handleLineStatusErrors(); + void processIoErrors(bool error); + QSerialPort::SerialPortError decodeSystemErrorCode(DWORD errorCode) const; QSerialPort::SerialPortError decodeSystemError() const; - void _q_completeAsyncCommunication(); - void _q_completeAsyncRead(); - void _q_completeAsyncWrite(); + void completeAsyncCommunication(); + void completeAsyncRead(DWORD numberOfBytes); + void completeAsyncWrite(DWORD numberOfBytes); bool startAsyncCommunication(); bool startAsyncRead(); @@ -101,7 +103,9 @@ public: bool emulateErrorPolicy(); void emitReadyRead(); - + + void _q_notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *notifiedOverlapped); + static QString portNameToSystemLocation(const QString &port); static QString portNameFromSystemLocation(const QString &location); @@ -119,9 +123,7 @@ public: QByteArray readChunkBuffer; bool readyReadEmitted; bool writeStarted; - QWinEventNotifier *communicationNotifier; - QWinEventNotifier *readCompletionNotifier; - QWinEventNotifier *writeCompletionNotifier; + QWinOverlappedIoNotifier *notifier; QTimer *startAsyncWriteTimer; OVERLAPPED communicationOverlapped; OVERLAPPED readCompletionOverlapped; @@ -134,7 +136,7 @@ private: bool updateCommTimeouts(); - bool waitAnyEvent(int msecs, bool *timedOut, HANDLE *triggeredEvent); + bool waitAnyEvent(int msecs); }; -- 1.7.11.msysgit.1