diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | src/serialport/qserialport.cpp | 29 | ||||
-rw-r--r-- | src/serialport/qserialport_p.h | 27 | ||||
-rw-r--r-- | src/serialport/qserialport_unix.cpp | 12 | ||||
-rw-r--r-- | src/serialport/qserialport_win.cpp | 129 | ||||
-rw-r--r-- | src/serialport/qwinoverlappedionotifier.cpp | 426 | ||||
-rw-r--r-- | src/serialport/qwinoverlappedionotifier_p.h | 91 | ||||
-rw-r--r-- | src/serialport/serialport-lib.pri | 6 |
8 files changed, 580 insertions, 142 deletions
diff --git a/.qmake.conf b/.qmake.conf index 78a277a..138038d 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.9.2 +MODULE_VERSION = 5.10.0 diff --git a/src/serialport/qserialport.cpp b/src/serialport/qserialport.cpp index 5534bb8..7853f1a 100644 --- a/src/serialport/qserialport.cpp +++ b/src/serialport/qserialport.cpp @@ -101,13 +101,12 @@ QSerialPortPrivate::QSerialPortPrivate() , isBreakEnabled(false) #if defined(Q_OS_WIN32) , handle(INVALID_HANDLE_VALUE) - , readChunkBuffer(ReadChunkSize, 0) + , readChunkBuffer(QSERIALPORT_BUFFERSIZE, 0) , communicationStarted(false) , writeStarted(false) , readStarted(false) , notifier(0) , startAsyncWriteTimer(0) - , originalEventMask(0) , triggeredEventMask(0) #elif defined(Q_OS_UNIX) , descriptor(-1) @@ -122,7 +121,8 @@ QSerialPortPrivate::QSerialPortPrivate() , writeSequenceStarted(false) #endif { - writeBufferChunkSize = InitialBufferSize; + writeBufferChunkSize = QSERIALPORT_BUFFERSIZE; + readBufferChunkSize = QSERIALPORT_BUFFERSIZE; } void QSerialPortPrivate::setError(const QSerialPortErrorInfo &errorInfo) @@ -1244,10 +1244,9 @@ qint64 QSerialPort::readBufferSize() const void QSerialPort::setReadBufferSize(qint64 size) { Q_D(QSerialPort); - - if (d->readBufferMaxSize == size) - return; d->readBufferMaxSize = size; + if (isReadable()) + d->startAsyncRead(); } /*! @@ -1436,21 +1435,9 @@ qint64 QSerialPort::readData(char *data, qint64 maxSize) Q_UNUSED(data); Q_UNUSED(maxSize); -#if defined(Q_OS_WIN32) - // 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. - Q_D(QSerialPort); - if (d->readBufferMaxSize || d->flowControl == QSerialPort::HardwareControl) - d->startAsyncRead(); -#elif defined(Q_OS_UNIX) - // We need try to re-trigger the read notification to read a remainder from a - // driver's queue in case we have a limited read buffer size. - Q_D(QSerialPort); - if (d->readBufferMaxSize && !d->isReadNotificationEnabled()) - d->setReadNotificationEnabled(true); -#endif + // In any case we need to start the notifications if they were + // disabled by the read handler. If enabled, next call does nothing. + d_func()->startAsyncRead(); // return 0 indicating there may be more data in the future return qint64(0); diff --git a/src/serialport/qserialport_p.h b/src/serialport/qserialport_p.h index 10fda34..0e4f8bd 100644 --- a/src/serialport/qserialport_p.h +++ b/src/serialport/qserialport_p.h @@ -56,6 +56,7 @@ #include "qserialport.h" #include <private/qiodevice_p.h> +#include <qdeadlinetimer.h> #if defined(Q_OS_WIN32) # include <qt_windows.h> @@ -97,9 +98,12 @@ struct serial_struct { # error Unsupported OS #endif +#ifndef QSERIALPORT_BUFFERSIZE +#define QSERIALPORT_BUFFERSIZE 32768 +#endif + QT_BEGIN_NAMESPACE -class QThread; class QWinOverlappedIoNotifier; class QTimer; class QSocketNotifier; @@ -121,11 +125,6 @@ class QSerialPortPrivate : public QIODevicePrivate { Q_DECLARE_PUBLIC(QSerialPort) public: - enum IoConstants { - ReadChunkSize = 512, - InitialBufferSize = 16384 - }; - QSerialPortPrivate(); bool open(QIODevice::OpenMode mode); @@ -158,13 +157,11 @@ public: qint64 writeData(const char *data, qint64 maxSize); + bool initialize(QIODevice::OpenMode mode); + static QString portNameToSystemLocation(const QString &port); static QString portNameFromSystemLocation(const QString &location); -#if defined(Q_OS_UNIX) - static qint32 settingFromBaudRate(qint32 baudRate); -#endif - static QList<qint32> standardBaudRates(); qint64 readBufferMaxSize; @@ -179,12 +176,13 @@ public: bool settingsRestoredOnClose; bool isBreakEnabled; + bool startAsyncRead(); + #if defined(Q_OS_WIN32) - bool initialize(); bool setDcb(DCB *dcb); bool getDcb(DCB *dcb); - OVERLAPPED *waitForNotified(int msecs); + OVERLAPPED *waitForNotified(QDeadlineTimer deadline); qint64 queuedBytesCount(QSerialPort::Direction direction) const; @@ -193,7 +191,6 @@ public: bool completeAsyncWrite(qint64 bytesTransferred); bool startAsyncCommunication(); - bool startAsyncRead(); bool _q_startAsyncWrite(); void _q_notified(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped); @@ -213,12 +210,12 @@ public: OVERLAPPED communicationOverlapped; OVERLAPPED readCompletionOverlapped; OVERLAPPED writeCompletionOverlapped; - DWORD originalEventMask; DWORD triggeredEventMask; #elif defined(Q_OS_UNIX) - bool initialize(QIODevice::OpenMode mode); + static qint32 settingFromBaudRate(qint32 baudRate); + bool setTermios(const termios *tio); bool getTermios(termios *tio); diff --git a/src/serialport/qserialport_unix.cpp b/src/serialport/qserialport_unix.cpp index 4df1fcb..4941a86 100644 --- a/src/serialport/qserialport_unix.cpp +++ b/src/serialport/qserialport_unix.cpp @@ -763,13 +763,19 @@ bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flowControl) return setTermios(&tio); } +bool QSerialPortPrivate::startAsyncRead() +{ + setReadNotificationEnabled(true); + return true; +} + bool QSerialPortPrivate::readNotification() { Q_Q(QSerialPort); // Always buffered, read data from the port into the read buffer qint64 newBytes = buffer.size(); - qint64 bytesToRead = ReadChunkSize; + qint64 bytesToRead = QSERIALPORT_BUFFERSIZE; if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) { bytesToRead = readBufferMaxSize - buffer.size(); @@ -798,10 +804,6 @@ bool QSerialPortPrivate::readNotification() newBytes = buffer.size() - newBytes; - // If read buffer is full, disable the read port notifier. - if (readBufferMaxSize && buffer.size() == readBufferMaxSize) - setReadNotificationEnabled(false); - // only emit readyRead() when not recursing, and only if there is data available const bool hasData = newBytes > 0; diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp index 87f9e7b..f90a7ef 100644 --- a/src/serialport/qserialport_win.cpp +++ b/src/serialport/qserialport_win.cpp @@ -40,12 +40,12 @@ ****************************************************************************/ #include "qserialport_p.h" +#include "qwinoverlappedionotifier_p.h" #include <QtCore/qcoreevent.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qvector.h> #include <QtCore/qtimer.h> -#include <private/qwinoverlappedionotifier_p.h> #include <algorithm> #ifndef CTL_CODE @@ -176,12 +176,9 @@ static inline void qt_set_flowcontrol(DCB *dcb, QSerialPort::FlowControl flowcon bool QSerialPortPrivate::open(QIODevice::OpenMode mode) { DWORD desiredAccess = 0; - originalEventMask = 0; - if (mode & QIODevice::ReadOnly) { + if (mode & QIODevice::ReadOnly) desiredAccess |= GENERIC_READ; - originalEventMask |= EV_RXCHAR; - } if (mode & QIODevice::WriteOnly) desiredAccess |= GENERIC_WRITE; @@ -193,7 +190,7 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode) return false; } - if (initialize()) + if (initialize(mode)) return true; ::CloseHandle(handle); @@ -347,18 +344,16 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs) const qint64 initialReadBufferSize = buffer.size(); qint64 currentReadBufferSize = initialReadBufferSize; - QElapsedTimer stopWatch; - stopWatch.start(); + QDeadlineTimer deadline(msecs); do { - const OVERLAPPED *overlapped = waitForNotified( - qt_subtract_from_timeout(msecs, stopWatch.elapsed())); + const OVERLAPPED *overlapped = waitForNotified(deadline); if (!overlapped) return false; if (overlapped == &readCompletionOverlapped) { const qint64 readBytesForOneReadOperation = qint64(buffer.size()) - currentReadBufferSize; - if (readBytesForOneReadOperation == ReadChunkSize) { + if (readBytesForOneReadOperation == QSERIALPORT_BUFFERSIZE) { currentReadBufferSize = buffer.size(); } else if (readBytesForOneReadOperation == 0) { if (initialReadBufferSize != currentReadBufferSize) @@ -368,7 +363,7 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs) } } - } while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0); + } while (!deadline.hasExpired()); return false; } @@ -381,12 +376,10 @@ bool QSerialPortPrivate::waitForBytesWritten(int msecs) if (!writeStarted && !_q_startAsyncWrite()) return false; - QElapsedTimer stopWatch; - stopWatch.start(); + QDeadlineTimer deadline(msecs); for (;;) { - const OVERLAPPED *overlapped = waitForNotified( - qt_subtract_from_timeout(msecs, stopWatch.elapsed())); + const OVERLAPPED *overlapped = waitForNotified(deadline); if (!overlapped) return false; @@ -484,11 +477,10 @@ bool QSerialPortPrivate::completeAsyncRead(qint64 bytesTransferred) readStarted = false; bool result = true; - if (bytesTransferred == ReadChunkSize + if (bytesTransferred == QSERIALPORT_BUFFERSIZE || queuedBytesCount(QSerialPort::Input) > 0) { result = startAsyncRead(); - } else if (readBufferMaxSize == 0 - || readBufferMaxSize > buffer.size()) { + } else { result = startAsyncCommunication(); } @@ -541,7 +533,7 @@ bool QSerialPortPrivate::startAsyncRead() if (readStarted) return true; - qint64 bytesToRead = ReadChunkSize; + qint64 bytesToRead = QSERIALPORT_BUFFERSIZE; if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size())) { bytesToRead = readBufferMaxSize - buffer.size(); @@ -552,6 +544,8 @@ bool QSerialPortPrivate::startAsyncRead() } } + Q_ASSERT(int(bytesToRead) <= readChunkBuffer.size()); + ::ZeroMemory(&readCompletionOverlapped, sizeof(readCompletionOverlapped)); if (::ReadFile(handle, readChunkBuffer.data(), bytesToRead, nullptr, &readCompletionOverlapped)) { readStarted = true; @@ -638,9 +632,9 @@ qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize) return maxSize; } -OVERLAPPED *QSerialPortPrivate::waitForNotified(int msecs) +OVERLAPPED *QSerialPortPrivate::waitForNotified(QDeadlineTimer deadline) { - OVERLAPPED *overlapped = notifier->waitForAnyNotified(msecs); + OVERLAPPED *overlapped = notifier->waitForAnyNotified(deadline); if (!overlapped) { setError(getSystemError(WAIT_TIMEOUT)); return nullptr; @@ -658,7 +652,7 @@ qint64 QSerialPortPrivate::queuedBytesCount(QSerialPort::Direction direction) co : ((direction == QSerialPort::Output) ? comstat.cbOutQue : -1); } -inline bool QSerialPortPrivate::initialize() +inline bool QSerialPortPrivate::initialize(QIODevice::OpenMode mode) { Q_Q(QSerialPort); @@ -691,20 +685,21 @@ inline bool QSerialPortPrivate::initialize() return false; } - if (!::SetCommMask(handle, originalEventMask)) { + const DWORD eventMask = (mode & QIODevice::ReadOnly) ? EV_RXCHAR : 0; + if (!::SetCommMask(handle, eventMask)) { setError(getSystemError()); return false; } + if ((eventMask & EV_RXCHAR) && !startAsyncCommunication()) + return false; + notifier = new QWinOverlappedIoNotifier(q); QObjectPrivate::connect(notifier, &QWinOverlappedIoNotifier::notified, this, &QSerialPortPrivate::_q_notified); notifier->setHandle(handle); notifier->setEnabled(true); - if ((originalEventMask & EV_RXCHAR) && !startAsyncCommunication()) - return false; - return true; } @@ -785,80 +780,16 @@ QSerialPortErrorInfo QSerialPortPrivate::getSystemError(int systemErrorCode) con } // This table contains standard values of baud rates that -// are defined in MSDN and/or in Win SDK file winbase.h - -static const QList<qint32> standardBaudRatePairList() -{ - - static const QList<qint32> standardBaudRatesTable = QList<qint32>() - - #ifdef CBR_110 - << CBR_110 - #endif - - #ifdef CBR_300 - << CBR_300 - #endif - - #ifdef CBR_600 - << CBR_600 - #endif - - #ifdef CBR_1200 - << CBR_1200 - #endif - - #ifdef CBR_2400 - << CBR_2400 - #endif - - #ifdef CBR_4800 - << CBR_4800 - #endif - - #ifdef CBR_9600 - << CBR_9600 - #endif - - #ifdef CBR_14400 - << CBR_14400 - #endif - - #ifdef CBR_19200 - << CBR_19200 - #endif - - #ifdef CBR_38400 - << CBR_38400 - #endif - - #ifdef CBR_56000 - << CBR_56000 - #endif - - #ifdef CBR_57600 - << CBR_57600 - #endif - - #ifdef CBR_115200 - << CBR_115200 - #endif - - #ifdef CBR_128000 - << CBR_128000 - #endif - - #ifdef CBR_256000 - << CBR_256000 - #endif - ; - - return standardBaudRatesTable; -}; - +// are defined in file winbase.h QList<qint32> QSerialPortPrivate::standardBaudRates() { - return standardBaudRatePairList(); + static const QList<qint32> baudRates = { + CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, + CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400, + CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 + }; + + return baudRates; } QSerialPort::Handle QSerialPort::handle() const diff --git a/src/serialport/qwinoverlappedionotifier.cpp b/src/serialport/qwinoverlappedionotifier.cpp new file mode 100644 index 0000000..6ec7463 --- /dev/null +++ b/src/serialport/qwinoverlappedionotifier.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinoverlappedionotifier_p.h" +#include <qdebug.h> +#include <qatomic.h> +#include <qelapsedtimer.h> +#include <qmutex.h> +#include <qpointer.h> +#include <qqueue.h> +#include <qset.h> +#include <qthread.h> +#include <qt_windows.h> +#include <private/qobject_p.h> +#include <private/qiodevice_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QWinOverlappedIoNotifier + \inmodule QtCore + \brief The QWinOverlappedIoNotifier class provides support for overlapped I/O notifications on Windows. + \since 5.0 + \internal + + The QWinOverlappedIoNotifier class makes it possible to use efficient + overlapped (asynchronous) I/O notifications on Windows by using an + I/O completion port. + + Once you have obtained a file handle, you can use setHandle() to get + notifications for I/O operations. Whenever an I/O operation completes, + the notified() signal is emitted which will pass the number of transferred + bytes, the operation's error code and a pointer to the operation's + OVERLAPPED object to the receiver. + + Every handle that supports overlapped I/O can be used by + QWinOverlappedIoNotifier. That includes file handles, TCP sockets + and named pipes. + + Note that you must not use ReadFileEx() and WriteFileEx() together + with QWinOverlappedIoNotifier. They are not supported as they use a + different I/O notification mechanism. + + The hEvent member in the OVERLAPPED structure passed to ReadFile() + or WriteFile() is ignored and can be used for other purposes. + + \warning This class is only available on Windows. + + Due to peculiarities of the Windows I/O completion port API, users of + QWinOverlappedIoNotifier must pay attention to the following restrictions: + \list + \li File handles with a QWinOverlappedIoNotifer are assigned to an I/O + completion port until the handle is closed. It is impossible to + disassociate the file handle from the I/O completion port. + \li There can be only one QWinOverlappedIoNotifer per file handle. Creating + another QWinOverlappedIoNotifier for that file, even with a duplicated + handle, will fail. + \li Certain Windows API functions are unavailable for file handles that are + assigned to an I/O completion port. This includes the functions + \c{ReadFileEx} and \c{WriteFileEx}. + \endlist + See also the remarks in the MSDN documentation for the + \c{CreateIoCompletionPort} function. +*/ + +struct IOResult +{ + IOResult(DWORD n = 0, DWORD e = 0, OVERLAPPED *p = 0) + : numberOfBytes(n), errorCode(e), overlapped(p) + {} + + DWORD numberOfBytes; + DWORD errorCode; + OVERLAPPED *overlapped; +}; + + +class QWinIoCompletionPort; + +class QWinOverlappedIoNotifierPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QWinOverlappedIoNotifier) +public: + QWinOverlappedIoNotifierPrivate() + : hHandle(INVALID_HANDLE_VALUE) + { + } + + OVERLAPPED *waitForAnyNotified(QDeadlineTimer deadline); + void notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped); + void _q_notified(); + OVERLAPPED *dispatchNextIoResult(); + + static QWinIoCompletionPort *iocp; + static HANDLE iocpInstanceLock; + static unsigned int iocpInstanceRefCount; + HANDLE hHandle; + HANDLE hSemaphore; + HANDLE hResultsMutex; + QAtomicInt waiting; + QQueue<IOResult> results; +}; + +QWinIoCompletionPort *QWinOverlappedIoNotifierPrivate::iocp = 0; +HANDLE QWinOverlappedIoNotifierPrivate::iocpInstanceLock = CreateMutex(NULL, FALSE, NULL); +unsigned int QWinOverlappedIoNotifierPrivate::iocpInstanceRefCount = 0; + + +class QWinIoCompletionPort : protected QThread +{ +public: + QWinIoCompletionPort() + : finishThreadKey(reinterpret_cast<ULONG_PTR>(this)), + drainQueueKey(reinterpret_cast<ULONG_PTR>(this + 1)), + hPort(INVALID_HANDLE_VALUE) + { + setObjectName(QLatin1String("I/O completion port thread")); + HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (!hIOCP) { + qErrnoWarning("CreateIoCompletionPort failed."); + return; + } + hPort = hIOCP; + hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!hQueueDrainedEvent) { + qErrnoWarning("CreateEvent failed."); + return; + } + } + + ~QWinIoCompletionPort() + { + PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL); + QThread::wait(); + CloseHandle(hPort); + CloseHandle(hQueueDrainedEvent); + } + + void registerNotifier(QWinOverlappedIoNotifierPrivate *notifier) + { + const HANDLE hHandle = notifier->hHandle; + HANDLE hIOCP = CreateIoCompletionPort(hHandle, hPort, + reinterpret_cast<ULONG_PTR>(notifier), 0); + if (!hIOCP) { + qErrnoWarning("Can't associate file handle %x with I/O completion port.", hHandle); + return; + } + mutex.lock(); + notifiers += notifier; + mutex.unlock(); + if (!QThread::isRunning()) + QThread::start(); + } + + void unregisterNotifier(QWinOverlappedIoNotifierPrivate *notifier) + { + mutex.lock(); + notifiers.remove(notifier); + mutex.unlock(); + } + + void drainQueue() + { + QMutexLocker locker(&drainQueueMutex); + ResetEvent(hQueueDrainedEvent); + PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL); + WaitForSingleObject(hQueueDrainedEvent, INFINITE); + } + + using QThread::isRunning; + +protected: + void run() + { + DWORD dwBytesRead; + ULONG_PTR pulCompletionKey; + OVERLAPPED *overlapped; + DWORD msecs = INFINITE; + + forever { + BOOL success = GetQueuedCompletionStatus(hPort, + &dwBytesRead, + &pulCompletionKey, + &overlapped, + msecs); + + DWORD errorCode = success ? ERROR_SUCCESS : GetLastError(); + if (!success && !overlapped) { + if (!msecs) { + // Time out in drain mode. The completion status queue is empty. + msecs = INFINITE; + SetEvent(hQueueDrainedEvent); + continue; + } + qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed."); + return; + } + + if (pulCompletionKey == finishThreadKey) + return; + if (pulCompletionKey == drainQueueKey) { + // Enter drain mode. + Q_ASSERT(msecs == INFINITE); + msecs = 0; + continue; + } + + QWinOverlappedIoNotifierPrivate *notifier + = reinterpret_cast<QWinOverlappedIoNotifierPrivate *>(pulCompletionKey); + mutex.lock(); + if (notifiers.contains(notifier)) + notifier->notify(dwBytesRead, errorCode, overlapped); + mutex.unlock(); + } + } + +private: + const ULONG_PTR finishThreadKey; + const ULONG_PTR drainQueueKey; + HANDLE hPort; + QSet<QWinOverlappedIoNotifierPrivate *> notifiers; + QMutex mutex; + QMutex drainQueueMutex; + HANDLE hQueueDrainedEvent; +}; + + +QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent) + : QObject(*new QWinOverlappedIoNotifierPrivate, parent) +{ + Q_D(QWinOverlappedIoNotifier); + WaitForSingleObject(d->iocpInstanceLock, INFINITE); + if (!d->iocp) + d->iocp = new QWinIoCompletionPort; + d->iocpInstanceRefCount++; + ReleaseMutex(d->iocpInstanceLock); + + d->hSemaphore = CreateSemaphore(NULL, 0, 255, NULL); + d->hResultsMutex = CreateMutex(NULL, FALSE, NULL); + connect(this, SIGNAL(_q_notify()), this, SLOT(_q_notified()), Qt::QueuedConnection); +} + +QWinOverlappedIoNotifier::~QWinOverlappedIoNotifier() +{ + Q_D(QWinOverlappedIoNotifier); + setEnabled(false); + CloseHandle(d->hResultsMutex); + CloseHandle(d->hSemaphore); + + WaitForSingleObject(d->iocpInstanceLock, INFINITE); + if (!--d->iocpInstanceRefCount) { + delete d->iocp; + d->iocp = 0; + } + ReleaseMutex(d->iocpInstanceLock); +} + +void QWinOverlappedIoNotifier::setHandle(Qt::HANDLE h) +{ + Q_D(QWinOverlappedIoNotifier); + d->hHandle = h; +} + +Qt::HANDLE QWinOverlappedIoNotifier::handle() const +{ + Q_D(const QWinOverlappedIoNotifier); + return d->hHandle; +} + +void QWinOverlappedIoNotifier::setEnabled(bool enabled) +{ + Q_D(QWinOverlappedIoNotifier); + if (enabled) + d->iocp->registerNotifier(d); + else + d->iocp->unregisterNotifier(d); +} + +OVERLAPPED *QWinOverlappedIoNotifierPrivate::waitForAnyNotified(QDeadlineTimer deadline) +{ + if (!iocp->isRunning()) { + qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier."); + return 0; + } + + DWORD msecs = deadline.remainingTime(); + if (msecs == 0) + iocp->drainQueue(); + if (msecs == -1) + msecs = INFINITE; + + const DWORD wfso = WaitForSingleObject(hSemaphore, msecs); + switch (wfso) { + case WAIT_OBJECT_0: + return dispatchNextIoResult(); + case WAIT_TIMEOUT: + return 0; + default: + qErrnoWarning("QWinOverlappedIoNotifier::waitForAnyNotified: WaitForSingleObject failed."); + return 0; + } +} + +class QScopedAtomicIntIncrementor +{ +public: + QScopedAtomicIntIncrementor(QAtomicInt &i) + : m_int(i) + { + ++m_int; + } + + ~QScopedAtomicIntIncrementor() + { + --m_int; + } + +private: + QAtomicInt &m_int; +}; + +/*! + * Wait synchronously for any notified signal. + * + * The function returns a pointer to the OVERLAPPED object corresponding to the completed I/O + * operation. In case no I/O operation was completed during the \a msec timeout, this function + * returns a null pointer. + */ +OVERLAPPED *QWinOverlappedIoNotifier::waitForAnyNotified(QDeadlineTimer deadline) +{ + Q_D(QWinOverlappedIoNotifier); + QScopedAtomicIntIncrementor saii(d->waiting); + OVERLAPPED *result = d->waitForAnyNotified(deadline); + return result; +} + +/*! + * Wait synchronously for the notified signal. + * + * The function returns true if the notified signal was emitted for + * the I/O operation that corresponds to the OVERLAPPED object. + */ +bool QWinOverlappedIoNotifier::waitForNotified(QDeadlineTimer deadline, OVERLAPPED *overlapped) +{ + Q_D(QWinOverlappedIoNotifier); + QScopedAtomicIntIncrementor saii(d->waiting); + while (!deadline.hasExpired()) { + OVERLAPPED *triggeredOverlapped = waitForAnyNotified(deadline); + if (!triggeredOverlapped) + return false; + if (triggeredOverlapped == overlapped) + return true; + } + return false; +} + +/*! + * Note: This function runs in the I/O completion port thread. + */ +void QWinOverlappedIoNotifierPrivate::notify(DWORD numberOfBytes, DWORD errorCode, + OVERLAPPED *overlapped) +{ + Q_Q(QWinOverlappedIoNotifier); + WaitForSingleObject(hResultsMutex, INFINITE); + results.enqueue(IOResult(numberOfBytes, errorCode, overlapped)); + ReleaseMutex(hResultsMutex); + ReleaseSemaphore(hSemaphore, 1, NULL); + if (!waiting) + emit q->_q_notify(); +} + +void QWinOverlappedIoNotifierPrivate::_q_notified() +{ + if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0) + dispatchNextIoResult(); +} + +OVERLAPPED *QWinOverlappedIoNotifierPrivate::dispatchNextIoResult() +{ + Q_Q(QWinOverlappedIoNotifier); + WaitForSingleObject(hResultsMutex, INFINITE); + IOResult ioresult = results.dequeue(); + ReleaseMutex(hResultsMutex); + emit q->notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped); + return ioresult.overlapped; +} + +QT_END_NAMESPACE + +#include "moc_qwinoverlappedionotifier_p.cpp" diff --git a/src/serialport/qwinoverlappedionotifier_p.h b/src/serialport/qwinoverlappedionotifier_p.h new file mode 100644 index 0000000..9ee998b --- /dev/null +++ b/src/serialport/qwinoverlappedionotifier_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINOVERLAPPEDIONOTIFIER_P_H +#define QWINOVERLAPPEDIONOTIFIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <qobject.h> +#include <qdeadlinetimer.h> + +typedef struct _OVERLAPPED OVERLAPPED; + +QT_BEGIN_NAMESPACE + +class QWinOverlappedIoNotifierPrivate; + +class QWinOverlappedIoNotifier : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QWinOverlappedIoNotifier) + Q_DECLARE_PRIVATE(QWinOverlappedIoNotifier) + Q_PRIVATE_SLOT(d_func(), void _q_notified()) + friend class QWinIoCompletionPort; +public: + QWinOverlappedIoNotifier(QObject *parent = 0); + ~QWinOverlappedIoNotifier(); + + void setHandle(Qt::HANDLE h); + Qt::HANDLE handle() const; + + void setEnabled(bool enabled); + OVERLAPPED *waitForAnyNotified(QDeadlineTimer deadline); + bool waitForNotified(QDeadlineTimer deadline, OVERLAPPED *overlapped); + +Q_SIGNALS: + void notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *overlapped); +#if !defined(Q_QDOC) + void _q_notify(); +#endif +}; + +QT_END_NAMESPACE + +#endif // QWINOVERLAPPEDIONOTIFIER_P_H diff --git a/src/serialport/serialport-lib.pri b/src/serialport/serialport-lib.pri index 9f83989..bb12df2 100644 --- a/src/serialport/serialport-lib.pri +++ b/src/serialport/serialport-lib.pri @@ -22,7 +22,11 @@ SOURCES += \ win32:!wince* { SOURCES += \ $$PWD/qserialport_win.cpp \ - $$PWD/qserialportinfo_win.cpp + $$PWD/qserialportinfo_win.cpp \ + $$PWD/qwinoverlappedionotifier.cpp + + PRIVATE_HEADERS += \ + $$PWD/qwinoverlappedionotifier_p.h LIBS_PRIVATE += -lsetupapi -ladvapi32 } |