summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2017-05-16 13:41:29 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2017-05-17 14:50:33 +0000
commit65dba188d51d6dc67a15483f26c6b94a21abf5bc (patch)
tree9595a056d43e9eb8513c0bfa9c7b98fbb3603693
parent84221c27840529bccb3d50c7e2da26b40a310e18 (diff)
downloadqtserialport-65dba188d51d6dc67a15483f26c6b94a21abf5bc.tar.gz
Use local copy of QWinOverlappedIoNotifier
... as there are plans to remove it from the QtCore. Change-Id: I79a2250b7b392896fa1c7307488a74dd44d62736 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
-rw-r--r--src/serialport/qserialport_win.cpp2
-rw-r--r--src/serialport/qwinoverlappedionotifier.cpp428
-rw-r--r--src/serialport/qwinoverlappedionotifier_p.h90
-rw-r--r--src/serialport/serialport-lib.pri6
4 files changed, 524 insertions, 2 deletions
diff --git a/src/serialport/qserialport_win.cpp b/src/serialport/qserialport_win.cpp
index 8ca2be9..d6a1d13 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
diff --git a/src/serialport/qwinoverlappedionotifier.cpp b/src/serialport/qwinoverlappedionotifier.cpp
new file mode 100644
index 0000000..d7745ae
--- /dev/null
+++ b/src/serialport/qwinoverlappedionotifier.cpp
@@ -0,0 +1,428 @@
+/****************************************************************************
+**
+** 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(int msecs);
+ 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(int msecs)
+{
+ if (!iocp->isRunning()) {
+ qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier.");
+ return 0;
+ }
+
+ if (msecs == 0)
+ iocp->drainQueue();
+
+ const DWORD wfso = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(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(int msecs)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ QScopedAtomicIntIncrementor saii(d->waiting);
+ OVERLAPPED *result = d->waitForAnyNotified(msecs);
+ 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(int msecs, OVERLAPPED *overlapped)
+{
+ Q_D(QWinOverlappedIoNotifier);
+ QScopedAtomicIntIncrementor saii(d->waiting);
+ int t = msecs;
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+ forever {
+ OVERLAPPED *triggeredOverlapped = waitForAnyNotified(t);
+ if (!triggeredOverlapped)
+ return false;
+ if (triggeredOverlapped == overlapped)
+ return true;
+ t = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
+ if (t == 0)
+ 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..0cd0e4c
--- /dev/null
+++ b/src/serialport/qwinoverlappedionotifier_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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>
+
+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(int msecs);
+ bool waitForNotified(int msecs, 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
}