diff options
author | Joerg Bornemann <joerg.bornemann@digia.com> | 2013-07-03 16:09:16 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-07-05 13:37:22 +0200 |
commit | ee73f7b7db91f6ba32300cb6c16978371012cfb9 (patch) | |
tree | edfda19e2abcb0a1fabbb318fe6cd6cea7271460 /src/corelib/io/qwinoverlappedionotifier.cpp | |
parent | 3f605c8b45634c3fae06610b1102cbeae2382bec (diff) |
QWinOverlappedIoNotifier: fix race condition
Fix race condition in waitForNotified for zero timeout.
A waitForNotified(0) call is typically used for triggering
events near the end of life of the I/O resource (e.g.
drainOutputPipes in QProcess). In that case we must
synchronize the IOCP thread and waitForNotified.
Task-number: QTBUG-29391
Change-Id: Iadbd8564ec461cbc48a6ef8c5627e30a5c6eecfd
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Diffstat (limited to 'src/corelib/io/qwinoverlappedionotifier.cpp')
-rw-r--r-- | src/corelib/io/qwinoverlappedionotifier.cpp | 43 |
1 files changed, 38 insertions, 5 deletions
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<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); @@ -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<QWinOverlappedIoNotifier *>(pulCompletionKey); @@ -158,9 +185,13 @@ protected: } private: + const ULONG_PTR finishThreadKey; + const ULONG_PTR drainQueueKey; HANDLE hPort; QSet<QWinOverlappedIoNotifier *> 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); |