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>
This commit is contained in:
Joerg Bornemann 2017-05-04 14:30:00 +02:00
parent 1e503d7634
commit ea57a23d7a
6 changed files with 3 additions and 865 deletions

View File

@ -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 {

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -1,4 +0,0 @@
CONFIG += testcase
TARGET = tst_qwinoverlappedionotifier
QT = core-private testlib
SOURCES = tst_qwinoverlappedionotifier.cpp

View File

@ -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(&notifier);
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(&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);
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(&notifier);
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"