diff options
author | Joerg Bornemann <joerg.bornemann@qt.io> | 2017-05-04 14:30:00 +0200 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2017-06-02 20:01:43 +0000 |
commit | ea57a23d7a584a50bdfd9d848696831c0a978306 (patch) | |
tree | 3a25c92ff40997e7be9640c8599002d2eb2f963a | |
parent | 1e503d76340a753d7a024ca9adc1c095fa3338cb (diff) |
Remove QWinOverlappedIoNotifier
This class in unused in qtbase since Qt 5.6.1.
The only outside usage was in qtserialport, which got its own copy of
QWinOverlappedIoNotifier in commit qtserialport/65dba188.
Change-Id: I7668e67a1cc49c4418c66141784b180cd5f9d479
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
-rw-r--r-- | src/corelib/io/io.pri | 6 | ||||
-rw-r--r-- | src/corelib/io/qwinoverlappedionotifier.cpp | 428 | ||||
-rw-r--r-- | src/corelib/io/qwinoverlappedionotifier_p.h | 90 | ||||
-rw-r--r-- | tests/auto/corelib/io/io.pro | 9 | ||||
-rw-r--r-- | tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro | 4 | ||||
-rw-r--r-- | tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp | 331 |
6 files changed, 3 insertions, 865 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index b0cac59f42..38e5c23c05 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -118,16 +118,14 @@ win32 { !winrt { HEADERS += \ io/qwindowspipereader_p.h \ - io/qwindowspipewriter_p.h \ - io/qwinoverlappedionotifier_p.h + io/qwindowspipewriter_p.h SOURCES += \ io/qsettings_win.cpp \ io/qstandardpaths_win.cpp \ io/qstorageinfo_win.cpp \ io/qwindowspipereader.cpp \ - io/qwindowspipewriter.cpp \ - io/qwinoverlappedionotifier.cpp + io/qwindowspipewriter.cpp LIBS += -lmpr } else { diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp deleted file mode 100644 index d7745ae1b6..0000000000 --- a/src/corelib/io/qwinoverlappedionotifier.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/**************************************************************************** -** -** 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/corelib/io/qwinoverlappedionotifier_p.h b/src/corelib/io/qwinoverlappedionotifier_p.h deleted file mode 100644 index 276a1d861e..0000000000 --- a/src/corelib/io/qwinoverlappedionotifier_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** 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 Q_CORE_EXPORT 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/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index 01ed84fda9..bd2a9f4c8e 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -34,12 +34,6 @@ SUBDIRS=\ qurl \ qurlinternal \ qurlquery \ - qwinoverlappedionotifier \ - -!win32 { - SUBDIRS -=\ - qwinoverlappedionotifier -} !qtHaveModule(gui): SUBDIRS -= \ qdatastream \ @@ -73,5 +67,4 @@ win32:!qtConfig(private_tests): SUBDIRS -= \ qprocess-noapplication winrt: SUBDIRS -= \ - qstorageinfo \ - qwinoverlappedionotifier + qstorageinfo diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro b/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro deleted file mode 100644 index 4f0e9da3c2..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qwinoverlappedionotifier -QT = core-private testlib -SOURCES = tst_qwinoverlappedionotifier.cpp diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp deleted file mode 100644 index 7034c2c9fd..0000000000 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include <private/qwinoverlappedionotifier_p.h> -#include <qbytearray.h> -#include <qt_windows.h> - -#ifndef PIPE_REJECT_REMOTE_CLIENTS -#define PIPE_REJECT_REMOTE_CLIENTS 0x08 -#endif - -class tst_QWinOverlappedIoNotifier : public QObject -{ - Q_OBJECT - -private slots: - void initTestCase(); - void readFile_data(); - void readFile(); - void waitForNotified_data(); - void waitForNotified(); - void waitForAnyNotified(); - void brokenPipe(); - void multipleOperations(); - -private: - QFileInfo sourceFileInfo; - DWORD notifiedBytesRead; - DWORD notifiedErrorCode; -}; - -class NotifierSink : public QObject -{ - Q_OBJECT -public: - NotifierSink(QWinOverlappedIoNotifier *notifier) - : QObject(notifier), - threshold(1) - { - connect(notifier, &QWinOverlappedIoNotifier::notified, this, &NotifierSink::notified); - } - -protected slots: - void notified(DWORD bytesRead, DWORD errorCode, OVERLAPPED *overlapped) - { - IOResult ioResult; - ioResult.bytes = bytesRead; - ioResult.errorCode = errorCode; - ioResult.overlapped = overlapped; - notifications.append(ioResult); - if (notifications.count() >= threshold) - emit notificationReceived(); - } - -signals: - void notificationReceived(); - -public: - int threshold; - - struct IOResult - { - IOResult() - : bytes(0), errorCode(ERROR_SUCCESS), overlapped(0) - {} - DWORD bytes; - DWORD errorCode; - OVERLAPPED *overlapped; - }; - - QList<IOResult> notifications; -}; - -void tst_QWinOverlappedIoNotifier::initTestCase() -{ - sourceFileInfo.setFile(QFINDTESTDATA("tst_qwinoverlappedionotifier.cpp")); - QVERIFY2(sourceFileInfo.exists(), "File tst_qwinoverlappedionotifier.cpp not found."); -} - -void tst_QWinOverlappedIoNotifier::readFile_data() -{ - QTest::addColumn<QString>("fileName"); - QTest::addColumn<int>("readBufferSize"); - QTest::addColumn<DWORD>("expectedBytesRead"); - - QString sourceFileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - int sourceFileSize = sourceFileInfo.size(); - - QTest::newRow("read file, less than available") - << sourceFileName << sourceFileSize / 2 << DWORD(sourceFileSize / 2); - QTest::newRow("read file, more than available") - << sourceFileName << sourceFileSize * 2 << DWORD(sourceFileSize); -} - -void tst_QWinOverlappedIoNotifier::readFile() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified_data() -{ - readFile_data(); -} - -void tst_QWinOverlappedIoNotifier::waitForNotified() -{ - QFETCH(QString, fileName); - QFETCH(int, readBufferSize); - QFETCH(DWORD, expectedBytesRead); - - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QCOMPARE(notifier.waitForNotified(100, 0), false); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QCOMPARE(notifier.waitForNotified(3000, &overlapped), true); - CloseHandle(hFile); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, expectedBytesRead); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); - QCOMPARE(notifier.waitForNotified(100, &overlapped), false); -} - -void tst_QWinOverlappedIoNotifier::waitForAnyNotified() -{ - const QString fileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath()); - const int readBufferSize = sourceFileInfo.size(); - - QWinOverlappedIoNotifier notifier; - HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()), - GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - notifier.setHandle(hFile); - notifier.setEnabled(true); - QVERIFY(!notifier.waitForAnyNotified(100)); - - OVERLAPPED overlapped1; - ZeroMemory(&overlapped1, sizeof(OVERLAPPED)); - QByteArray buffer1(readBufferSize, 0); - BOOL readSuccess = ReadFile(hFile, buffer1.data(), buffer1.size(), NULL, &overlapped1); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - OVERLAPPED overlapped2; - ZeroMemory(&overlapped2, sizeof(OVERLAPPED)); - QByteArray buffer2(readBufferSize, 0); - readSuccess = ReadFile(hFile, buffer2.data(), buffer2.size(), NULL, &overlapped2); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - QSet<OVERLAPPED *> overlappedObjects; - overlappedObjects << &overlapped1 << &overlapped2; - - for (int i = 1; i <= 2; ++i) { - OVERLAPPED *notifiedOverlapped = notifier.waitForAnyNotified(3000); - QVERIFY(overlappedObjects.contains(notifiedOverlapped)); - overlappedObjects.remove(notifiedOverlapped); - } - - CloseHandle(hFile); - QVERIFY(overlappedObjects.isEmpty()); - QVERIFY(!notifier.waitForAnyNotified(100)); -} - -void tst_QWinOverlappedIoNotifier::brokenPipe() -{ - QWinOverlappedIoNotifier notifier; - NotifierSink sink(¬ifier); - connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_brokenPipe"; - HANDLE hPipe = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hPipe != INVALID_HANDLE_VALUE); - HANDLE hReadEnd = CreateFile(pipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hReadEnd != INVALID_HANDLE_VALUE); - notifier.setHandle(hReadEnd); - notifier.setEnabled(true); - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); - QByteArray buffer(1024, 0); - BOOL readSuccess = ReadFile(hReadEnd, buffer.data(), buffer.size(), NULL, &overlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // close the write end of the pipe - CloseHandle(hPipe); - - QTestEventLoop::instance().enterLoop(3); - CloseHandle(hReadEnd); - QCOMPARE(sink.notifications.count(), 1); - QCOMPARE(sink.notifications.last().bytes, DWORD(0)); - QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_BROKEN_PIPE)); - QCOMPARE(sink.notifications.last().overlapped, &overlapped); -} - -void tst_QWinOverlappedIoNotifier::multipleOperations() -{ - QWinOverlappedIoNotifier clientNotifier; - NotifierSink sink(&clientNotifier); - sink.threshold = 2; - connect(&sink, &NotifierSink::notificationReceived, - &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); - - wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_multipleOperations"; - HANDLE hServer = CreateNamedPipe(pipeName, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 0, 0, NULL); - QVERIFY(hServer != INVALID_HANDLE_VALUE); - HANDLE hClient = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - QVERIFY(hClient != INVALID_HANDLE_VALUE); - clientNotifier.setHandle(hClient); - clientNotifier.setEnabled(true); - - // start async read on client - QByteArray clientReadBuffer(377, Qt::Uninitialized); - OVERLAPPED clientReadOverlapped; - ZeroMemory(&clientReadOverlapped, sizeof(clientReadOverlapped)); - BOOL readSuccess = ReadFile(hClient, clientReadBuffer.data(), clientReadBuffer.size(), - NULL, &clientReadOverlapped); - QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write client -> server - QByteArray clientDataToWrite(233, 'B'); - OVERLAPPED clientWriteOverlapped; - ZeroMemory(&clientWriteOverlapped, sizeof(clientWriteOverlapped)); - BOOL writeSuccess = WriteFile(hClient, clientDataToWrite.data(), clientDataToWrite.size(), - NULL, &clientWriteOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // start async write server -> client - QByteArray serverDataToWrite(144, 'A'); - OVERLAPPED serverOverlapped; - ZeroMemory(&serverOverlapped, sizeof(serverOverlapped)); - writeSuccess = WriteFile(hServer, serverDataToWrite.data(), serverDataToWrite.size(), - NULL, &serverOverlapped); - QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING); - - // read synchronously on server to complete the client -> server write - QByteArray serverReadBuffer(610, Qt::Uninitialized); - DWORD dwBytesRead = 0; - readSuccess = ReadFile(hServer, serverReadBuffer.data(), serverReadBuffer.size(), - &dwBytesRead, NULL); - QVERIFY(readSuccess); - QCOMPARE(int(dwBytesRead), clientDataToWrite.size()); - serverReadBuffer.resize(dwBytesRead); - QCOMPARE(serverReadBuffer, clientDataToWrite); - - QTestEventLoop::instance().enterLoop(3); - QTRY_COMPARE(sink.notifications.count(), 2); - foreach (const NotifierSink::IOResult &r, sink.notifications) { - QCOMPARE(r.errorCode, DWORD(ERROR_SUCCESS)); - if (r.bytes == DWORD(serverDataToWrite.count())) - QCOMPARE(r.overlapped, &clientReadOverlapped); - else if (r.bytes == DWORD(clientDataToWrite.count())) - QCOMPARE(r.overlapped, &clientWriteOverlapped); - else - QVERIFY2(false, "Unexpected number of bytes received."); - } - - CloseHandle(hClient); - CloseHandle(hServer); -} - -QTEST_MAIN(tst_QWinOverlappedIoNotifier) - -#include "tst_qwinoverlappedionotifier.moc" |