QWinOverlappedIoNotifier: Add an extended waitForAnyNotified() method

The existing waitForNotified method has the design limitation that it
doesn't allow the tracking of multiple I/O operations on a single
file handle.

Therefore we introduce an additional method waitForAnyNotified that
returns a pointer to the triggered OVERLAPPED object.

Change-Id: I536ed7f6828daa2b0ce03f2d662eeb10aa89ca99
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
This commit is contained in:
Denis Shienkov 2013-11-22 15:29:34 +04:00 committed by The Qt Project
parent 0ab9188ad7
commit aefa611f0c
3 changed files with 83 additions and 18 deletions

View File

@ -41,6 +41,7 @@
#include "qwinoverlappedionotifier_p.h"
#include <qdebug.h>
#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qpointer.h>
#include <qqueue.h>
@ -292,6 +293,36 @@ void QWinOverlappedIoNotifier::setEnabled(bool enabled)
d->iocp->unregisterNotifier(d);
}
/*!
* 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);
if (!d->iocp->isRunning()) {
qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier.");
return 0;
}
if (msecs == 0)
d->iocp->drainQueue();
switch (WaitForSingleObject(d->hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs))) {
case WAIT_OBJECT_0:
ReleaseSemaphore(d->hSemaphore, 1, NULL);
return d->_q_notified();
case WAIT_TIMEOUT:
return 0;
default:
qErrnoWarning("QWinOverlappedIoNotifier::waitForAnyNotified: WaitForSingleObject failed.");
return 0;
}
}
/*!
* Wait synchronously for the notified signal.
*
@ -300,28 +331,21 @@ void QWinOverlappedIoNotifier::setEnabled(bool enabled)
*/
bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped)
{
Q_D(QWinOverlappedIoNotifier);
if (!d->iocp->isRunning()) {
qWarning("Called QWinOverlappedIoNotifier::waitForNotified on inactive notifier.");
return false;
}
int t = msecs;
QElapsedTimer stopWatch;
stopWatch.start();
forever {
if (msecs == 0)
d->iocp->drainQueue();
DWORD result = WaitForSingleObject(d->hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
if (result == WAIT_OBJECT_0) {
ReleaseSemaphore(d->hSemaphore, 1, NULL);
if (d->_q_notified() == overlapped)
return true;
continue;
} else if (result == WAIT_TIMEOUT) {
OVERLAPPED *triggeredOverlapped = waitForAnyNotified(t);
if (!triggeredOverlapped)
return false;
if (triggeredOverlapped == overlapped)
return true;
if (msecs != -1) {
t = msecs - stopWatch.elapsed();
if (t < 0)
return false;
}
}
qErrnoWarning("QWinOverlappedIoNotifier::waitForNotified: WaitForSingleObject failed.");
return false;
}
/*!

View File

@ -76,6 +76,7 @@ public:
Qt::HANDLE handle() const;
void setEnabled(bool enabled);
OVERLAPPED *waitForAnyNotified(int msecs);
bool waitForNotified(int msecs, OVERLAPPED *overlapped);
Q_SIGNALS:

View File

@ -58,6 +58,7 @@ private slots:
void readFile();
void waitForNotified_data();
void waitForNotified();
void waitForAnyNotified();
void brokenPipe();
void multipleOperations();
@ -195,6 +196,45 @@ void tst_QWinOverlappedIoNotifier::waitForNotified()
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) == 0);
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) == 0);
}
void tst_QWinOverlappedIoNotifier::brokenPipe()
{
QWinOverlappedIoNotifier notifier;