QWinOverlappedIoNotifier: multiple I/O operations on the same handle

When doing multiple I/O operations on the same handle, we get notified
for every operations. These must be distinguished by comparing the
pointer to the OVERLAPPED struct.

We now pass the OVERLAPPED pointer via the notified signal and let the
receiver decide if it wants to handle this notification.

Change-Id: I4efe70f39c6ae5282b949f2f4b21f6e7dd3df785
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
bb10
Joerg Bornemann 2012-09-29 17:55:32 +02:00 committed by The Qt Project
parent d3c24d241e
commit 94dc0c6594
5 changed files with 68 additions and 38 deletions

View File

@ -68,7 +68,7 @@ QWindowsPipeReader::~QWindowsPipeReader()
{
if (readSequenceStarted) {
CancelIo(handle);
dataReadNotifier->waitForNotified(-1);
dataReadNotifier->waitForNotified(-1, &overlapped);
}
}
@ -156,8 +156,10 @@ bool QWindowsPipeReader::canReadLine() const
\internal
Will be called whenever the read operation completes.
*/
void QWindowsPipeReader::notified(DWORD numberOfBytesRead, DWORD errorCode)
void QWindowsPipeReader::notified(DWORD numberOfBytesRead, DWORD errorCode, OVERLAPPED *notifiedOverlapped)
{
if (&overlapped != notifiedOverlapped)
return;
if (!completeAsyncRead(numberOfBytesRead, errorCode)) {
pipeBroken = true;
emit pipeClosed();
@ -281,7 +283,7 @@ bool QWindowsPipeReader::waitForReadyRead(int msecs)
if (!readSequenceStarted)
return false;
readyReadEmitted = false;
dataReadNotifier->waitForNotified(msecs);
dataReadNotifier->waitForNotified(msecs, &overlapped);
return readyReadEmitted;
}

View File

@ -96,7 +96,7 @@ Q_SIGNALS:
void pipeClosed();
private Q_SLOTS:
void notified(DWORD numberOfBytesRead, DWORD errorCode);
void notified(DWORD numberOfBytesRead, DWORD errorCode, OVERLAPPED *notifiedOverlapped);
private:
bool completeAsyncRead(DWORD bytesRead, DWORD errorCode);

View File

@ -62,7 +62,8 @@ QT_BEGIN_NAMESPACE
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 and the operation's error code to the receiver.
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
@ -149,7 +150,7 @@ protected:
QWinOverlappedIoNotifier *notifier = reinterpret_cast<QWinOverlappedIoNotifier *>(pulCompletionKey);
mutex.lock();
if (notifiers.contains(notifier))
notifier->notify(dwBytesRead, errorCode);
notifier->notify(dwBytesRead, errorCode, overlapped);
mutex.unlock();
}
}
@ -164,11 +165,10 @@ Q_GLOBAL_STATIC(QWinIoCompletionPort, iocp)
QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent)
: QObject(parent),
hHandle(INVALID_HANDLE_VALUE),
lastNumberOfBytes(0),
lastErrorCode(ERROR_SUCCESS)
hHandle(INVALID_HANDLE_VALUE)
{
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
hSemaphore = CreateSemaphore(NULL, 0, 255, NULL);
hResultsMutex = CreateMutex(NULL, FALSE, NULL);
connect(this, &QWinOverlappedIoNotifier::_q_notify,
this, &QWinOverlappedIoNotifier::_q_notified, Qt::QueuedConnection);
}
@ -176,7 +176,8 @@ QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent)
QWinOverlappedIoNotifier::~QWinOverlappedIoNotifier()
{
setEnabled(false);
CloseHandle(hEvent);
CloseHandle(hResultsMutex);
CloseHandle(hSemaphore);
}
void QWinOverlappedIoNotifier::setHandle(HANDLE h)
@ -192,15 +193,24 @@ void QWinOverlappedIoNotifier::setEnabled(bool enabled)
iocp()->unregisterNotifier(this);
}
bool QWinOverlappedIoNotifier::waitForNotified(int msecs)
/*!
* Wait synchronously for the notified signal.
*
* \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)
{
DWORD result = WaitForSingleObject(hEvent, msecs == -1 ? INFINITE : DWORD(msecs));
switch (result) {
case WAIT_OBJECT_0:
_q_notified();
return true;
case WAIT_TIMEOUT:
return false;
forever {
DWORD result = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
if (result == WAIT_OBJECT_0) {
ReleaseSemaphore(hSemaphore, 1, NULL);
if (_q_notified() == overlapped)
return true;
continue;
} else if (result == WAIT_TIMEOUT) {
return false;
}
}
qErrnoWarning("QWinOverlappedIoNotifier::waitForNotified: WaitForSingleObject failed.");
@ -210,20 +220,25 @@ bool QWinOverlappedIoNotifier::waitForNotified(int msecs)
/*!
* Note: This function runs in the I/O completion port thread.
*/
void QWinOverlappedIoNotifier::notify(DWORD numberOfBytes, DWORD errorCode)
void QWinOverlappedIoNotifier::notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped)
{
lastNumberOfBytes = numberOfBytes;
lastErrorCode = errorCode;
SetEvent(hEvent);
WaitForSingleObject(hResultsMutex, INFINITE);
results.enqueue(IOResult(numberOfBytes, errorCode, overlapped));
ReleaseMutex(hResultsMutex);
ReleaseSemaphore(hSemaphore, 1, NULL);
emit _q_notify();
}
void QWinOverlappedIoNotifier::_q_notified()
OVERLAPPED *QWinOverlappedIoNotifier::_q_notified()
{
if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) {
ResetEvent(hEvent);
emit notified(lastNumberOfBytes, lastErrorCode);
if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0) {
WaitForSingleObject(hResultsMutex, INFINITE);
IOResult ioresult = results.dequeue();
ReleaseMutex(hResultsMutex);
emit notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped);
return ioresult.overlapped;
}
return 0;
}
QT_END_NAMESPACE

View File

@ -55,6 +55,7 @@
#include <qobject.h>
#include <qt_windows.h>
#include <qqueue.h>
QT_BEGIN_HEADER
@ -73,23 +74,35 @@ public:
HANDLE handle() const { return hHandle; }
void setEnabled(bool enabled);
bool waitForNotified(int msecs);
bool waitForNotified(int msecs, OVERLAPPED *overlapped);
Q_SIGNALS:
void notified(DWORD numberOfBytes, DWORD errorCode);
void notified(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
void _q_notify();
private Q_SLOTS:
void _q_notified();
OVERLAPPED *_q_notified();
private:
void notify(DWORD numberOfBytes, DWORD errorCode);
void notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
private:
HANDLE hHandle;
HANDLE hEvent;
DWORD lastNumberOfBytes;
DWORD lastErrorCode;
HANDLE hSemaphore;
HANDLE hResultsMutex;
struct IOResult
{
IOResult(DWORD n = 0, DWORD e = 0, OVERLAPPED *p = 0)
: numberOfBytes(n), errorCode(e), overlapped(p)
{}
DWORD numberOfBytes;
DWORD errorCode;
OVERLAPPED *overlapped;
};
QQueue<IOResult> results;
friend class QWinIoCompletionPort;
};

View File

@ -157,22 +157,22 @@ void tst_QWinOverlappedIoNotifier::waitForNotified()
HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()),
GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
QCOMPARE(notifier.waitForNotified(0), false);
QCOMPARE(notifier.waitForNotified(0, 0), false);
notifier.setHandle(hFile);
notifier.setEnabled(true);
QCOMPARE(notifier.waitForNotified(100), false);
QCOMPARE(notifier.waitForNotified(100, 0), false);
OVERLAPPED overlapped = {0};
QByteArray buffer(readBufferSize, 0);
BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped);
QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
QCOMPARE(notifier.waitForNotified(3000), true);
QCOMPARE(notifier.waitForNotified(3000, &overlapped), true);
CloseHandle(hFile);
QCOMPARE(sink.notifications, 1);
QCOMPARE(sink.notifiedBytesRead, expectedBytesRead);
QCOMPARE(sink.notifiedErrorCode, DWORD(ERROR_SUCCESS));
QCOMPARE(notifier.waitForNotified(100), false);
QCOMPARE(notifier.waitForNotified(100, &overlapped), false);
}
void tst_QWinOverlappedIoNotifier::brokenPipe()