diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp index ea29e483bb..8a06a7ba21 100644 --- a/src/corelib/io/qwindowspipereader.cpp +++ b/src/corelib/io/qwindowspipereader.cpp @@ -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; } diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h index 2e990cc8c3..9b140b24d0 100644 --- a/src/corelib/io/qwindowspipereader_p.h +++ b/src/corelib/io/qwindowspipereader_p.h @@ -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); diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp index 0c698c6e53..c084912897 100644 --- a/src/corelib/io/qwinoverlappedionotifier.cpp +++ b/src/corelib/io/qwinoverlappedionotifier.cpp @@ -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(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 diff --git a/src/corelib/io/qwinoverlappedionotifier_p.h b/src/corelib/io/qwinoverlappedionotifier_p.h index 0659e1b20f..326df584d7 100644 --- a/src/corelib/io/qwinoverlappedionotifier_p.h +++ b/src/corelib/io/qwinoverlappedionotifier_p.h @@ -55,6 +55,7 @@ #include #include +#include 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 results; friend class QWinIoCompletionPort; }; diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp index 1de79b18bf..47be60b478 100644 --- a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp +++ b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp @@ -157,22 +157,22 @@ void tst_QWinOverlappedIoNotifier::waitForNotified() HANDLE hFile = CreateFile(reinterpret_cast(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()