diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp index 7ba862c602..914264e69e 100644 --- a/src/corelib/io/qwinoverlappedionotifier.cpp +++ b/src/corelib/io/qwinoverlappedionotifier.cpp @@ -83,7 +83,9 @@ class QWinIoCompletionPort : protected QThread { public: QWinIoCompletionPort() - : hPort(INVALID_HANDLE_VALUE) + : finishThreadKey(reinterpret_cast(this)), + drainQueueKey(reinterpret_cast(this + 1)), + hPort(INVALID_HANDLE_VALUE) { setObjectName(QLatin1String("I/O completion port thread")); HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); @@ -92,13 +94,19 @@ public: return; } hPort = hIOCP; + hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!hQueueDrainedEvent) { + qErrnoWarning("CreateEvent failed."); + return; + } } ~QWinIoCompletionPort() { - PostQueuedCompletionStatus(hPort, 0, 0, NULL); + PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL); QThread::wait(); CloseHandle(hPort); + CloseHandle(hQueueDrainedEvent); } void registerNotifier(QWinOverlappedIoNotifier *notifier) @@ -122,6 +130,14 @@ public: mutex.unlock(); } + void drainQueue() + { + QMutexLocker locker(&drainQueueMutex); + ResetEvent(hQueueDrainedEvent); + PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL); + WaitForSingleObject(hQueueDrainedEvent, INFINITE); + } + using QThread::isRunning; protected: @@ -130,23 +146,34 @@ protected: DWORD dwBytesRead; ULONG_PTR pulCompletionKey; OVERLAPPED *overlapped; + DWORD msecs = INFINITE; forever { BOOL success = GetQueuedCompletionStatus(hPort, &dwBytesRead, &pulCompletionKey, &overlapped, - INFINITE); + 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 (success && !(dwBytesRead || pulCompletionKey || overlapped)) { - // We've posted null values via PostQueuedCompletionStatus to end this thread. + if (pulCompletionKey == finishThreadKey) return; + if (pulCompletionKey == drainQueueKey) { + // Enter drain mode. + Q_ASSERT(msecs == INFINITE); + msecs = 0; + continue; } QWinOverlappedIoNotifier *notifier = reinterpret_cast(pulCompletionKey); @@ -158,9 +185,13 @@ protected: } private: + const ULONG_PTR finishThreadKey; + const ULONG_PTR drainQueueKey; HANDLE hPort; QSet notifiers; QMutex mutex; + QMutex drainQueueMutex; + HANDLE hQueueDrainedEvent; }; QWinIoCompletionPort *QWinOverlappedIoNotifier::iocp = 0; @@ -224,6 +255,8 @@ bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped } forever { + if (msecs == 0) + iocp->drainQueue(); DWORD result = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs)); if (result == WAIT_OBJECT_0) { ReleaseSemaphore(hSemaphore, 1, NULL);